學習目標 · Learning Objectives
- Explain, in a client meeting, why a competent Hong Kong agent must quote at least three carriers, and name the commercial and regulatory reasons (product shelf breadth, commission sustainability, non-solicited (NS) status, IA referral rules, and career-development agency consent) rather than just "price shopping".
- Normalise (標準化) two or more carrier quotes that define the same benefit differently into one comparable basis: convert guarantee periods, benefit definitions, deductible design, occupation classes, waiting periods, reinstatement terms and premium-paying periods into a canonical unit (HK$ per HK$1m of normalised benefit, and total cost of ownership over the client's stated horizon), and show the arithmetic.
- Build and present a bid sheet (報價對比表 / 比價單) as a compliant proposal presentation: every figure traceable to a carrier quotation with an issue date and expiry, non-guaranteed benefits shown as illustrations and never added to the sum-insured, and a written record of which carrier declined, loaded or referred.
Multi-Carrier Comparison (多間公司比價)
Lesson 07 · Insurance POS 101 · Hong Kong market · agent-facing
Learning Objectives
- Explain, in a client meeting, why a competent Hong Kong agent must quote at least three carriers, and name the commercial and regulatory reasons (product shelf breadth, commission sustainability, non-solicited (NS) status, IA referral rules, and career-development agency consent) rather than just "price shopping".
- Normalise (標準化) two or more carrier quotes that define the same benefit differently into one comparable basis: convert guarantee periods, benefit definitions, deductible design, occupation classes, waiting periods, reinstatement terms and premium-paying periods into a canonical unit (HK$ per HK$1m of normalised benefit, and total cost of ownership over the client's stated horizon), and show the arithmetic.
- Build and present a bid sheet (報價對比表 / 比價單) as a compliant proposal presentation: every figure traceable to a carrier quotation with an issue date and expiry, non-guaranteed benefits shown as illustrations and never added to the sum-insured, and a written record of which carrier declined, loaded or referred.
Why One Carrier Is Not Enough
In practice: the agent opens a need-analysis interview in Mong Kok on a Tuesday evening and, by Wednesday afternoon, needs to be able to say "Carrier B declines on a smoker under 40, Carrier C will offer but with a 50% loading, Carrier A offers standard plus a 3-year premium waiver" — with all three answers traceable to a system screen, not to memory.
The single-carrier trap
An agent who represents one carrier can end up in one of two bad states. In the first state, the client needs a product the carrier's shelf does not contain — a high-deductible medical plan for a self-employed client with a $1.6m deductible and an annual out-of-pocket cap, a defined-benefit retirement product, an ILAS fund, an accident plan that covers amateur football on a Sunday league — and the only honest thing to say is "I cannot offer that here." In the second, and more insidious state, the agent is confident, fluent, and only knows one company's underwriting appetite, one company's benefit wording, and one company's complaint-handling culture. They have never been asked by a client "why not the other bank?" and cannot answer without bluffing. Both states end the agency relationship.
The structural reason is that Hong Kong life and general insurance is not a two-player market. The Insurance Authority (保險業監管局, IA) supervises more than 130 authorised insurers, of which roughly 60–70 write life or health business, and the agency force is tens of thousands of people. A general agent's office that carries six to twelve active agency agreements can see two to five genuinely comparable quotes for most cases. An agent with one agreement is, in effect, an in-house salesperson for that carrier and should be paid and evaluated like one.
The five reasons that actually move the number
1. Product shelf breadth. Carriers do not compete on a single product grid. As at 2025–2026 the HK market contains at least four distinct medical architectures — full reimbursement with an annual deductible, an annual limit with per-day room-and-board caps, a high-deductible plus "top-up rider" structure, and an out-of-pocket cap ("自付額封頂") design — plus separate accident-and-medical (BMX/A&H) legacy books, defined-benefit critical illness products, term life with decreasing or level sums assured, and savings/participating plans with non-guaranteed benefits. No single carrier's shelf is competitive across all of them for every client profile. A 34-year-old male consultant with a disclosed hypertension history is standard at one carrier, referred at another, and declined at a third. If you only hold one agency agreement, you inherit that carrier's appetite as your own.
2. Commission sustainability and carrier concentration risk. First-year commission for a Hong Kong regular-premium (期繳) protection plan commonly sits in the range of 40%–60% of the first-year premium for a strong shelf (CLIC-style high-commission shelves exist and are real, not hype), with renewal commission of 5%–15% thereafter and a possible 10%–30% bonus tier on persistency and/or case rate over the first 6–24 months. An agent whose entire income comes from one carrier is exposed to that carrier's product withdrawal, commission cuts, and channel re-organisation. Carrying three or more carriers that are genuinely non-correlated — ideally one with a strong protection shelf, one with a strong savings shelf, one with a strong accident/ILAS shelf — is basic income-risk management. Note the asymmetry: this is a diversification argument, not an arbitrage argument. Rebating off another agent's quoted plan and pocketing the difference is not a business model; it is a complaint waiting to happen.
3. Non-solicited (NS) and referral status. The IA's Best Practice Guide on Referral Schemes and Disclosure of Existing Relationships is the single most misunderstood document in agency practice. If the client already holds a policy of the same class with the carrier you represent, and you induce them to take out a new one and cancel or replace the old, you are dealing in a replacement (取代舊單). The old agent is entitled to handle the case as NS or declining. The permitted path for a new agent is to act on a referral basis: the existing agent must agree in writing to release the case, and typically shares the commission. Attempting to take a replacement case without the old agent's written consent exposes you to an IA complaint, a compliance investigation, and a commission clawback. This alone is enough to justify holding more than one agency agreement, because you cannot refer a case you do not represent.
4. Agency agreement and cross-selling consent. Every agency agreement grants you the right to sell that carrier's products to clients you approached. Selling Carrier D's product to a client whose details came from a Carrier A proposal requires A's written agency-consent or a genuine independent introduction. A multi-carrier agent can do this; a single-agreement agent who fabricates one is committing a fraud-adjacent act. The POS makes this visible: a consent flag is per-carrier, per-product-class, and time-bound.
5. Career and mobility protection. Statistics published by the IA on agency force turnover have consistently shown a high exit rate, and a common (and expensive) reason is dependency. An agent whose only product knowledge is one shelf re-uses it verbatim in their next role and misprices work. A multi-carrier agent's skill — normalisation, needs mapping, comparison presentation — is portable and interviewable.
What "N carriers" actually means in practice
The honest answer is depth on a shelf, breadth across shelves, not a data dump. A workable configuration:
| Shelf role | Typical strength | Why it is in the portfolio |
|---|---|---|
| Protection specialist (1–2 carriers) | Term life, critical illness, medical with stable definitions | The bread-and-butter cases; must have reliable underwriting and a known claims reputation |
| Savings / participating specialist (1–2) | Whole life, limited-pay, endowment, non-guaranteed bonus plans | Only a minority of clients should be steered here; you need a non-guaranteed-benefits conversation you can actually have |
| Accident & hospital cash (1–2) | BMX, hospital cash, low-cost supplementary cover | Feeds the medical stack; very different margin profile |
| ILAS / linked general (1) | Investment-linked assurance | Needed to answer "what if the client wants market exposure inside a wrapper"; also a referral magnet |
| Narrow, opportunistic (0–2) | A carrier with a genuinely better niche product or a temporary underwriting appetite | Reviewed quarterly, not defaulted to |
Practically: three to six active agreements, each one you have sold at least five cases from, each one you can quote without asking. A thirteenth agreement you have never submitted is a liability, not an asset.
The agency is the system of comparison
Here is the point that matters for the POS: the comparison is not a spreadsheet the agent maintains at home. It is a system function. The POS holds the normalised catalogue, runs the normalisation engine, caches carrier rates with issue and expiry dates, and emits a bid sheet with an audit trail. If the agent cannot reproduce the bid sheet six months later for a complaint or a mis-sale investigation, the comparison never happened in any meaningful sense.
flowchart LR
A[Need analysis<br/>client profile + budget] --> B[Fan-out quote request<br/>carrier adapters]
B --> C1[Carrier A<br/>rates + wording]
B --> C2[Carrier B<br/>rates + wording]
B --> C3[Carrier C<br/>rates + wording]
C1 --> D[Normalisation engine]
C2 --> D
C3 --> D
D --> E[Canonical quote objects<br/>guaranteed + non-guaranteed kept separate]
E --> F[Bid sheet generator<br/>PDF + audit row]
F --> G{Client decision}
G -->|place| H[Application<br/>Lesson 08]
G -->|decline/withdraw| I[Archive + no-recontact timer]
D --> J[Rejection / referral learning set]
J --> K[Profile appetite map]
The Normalisation Problem
In practice: the agent discovers that "HK$1,000,000 surgical" from Carrier A and "HK$1,000,000 surgical" from Carrier D are not the same promise, and that showing them side by side without adjustment is a misrepresentation risk, not just a bad comparison.
Why raw quotes cannot be placed side by side
Carriers write benefits in their own house language. Three things are routinely true at once:
- The names differ but the promises overlap only partly. "Major Illness Benefit" in one wording pays on diagnosis of any one of 50+ specified conditions. In another, "Critical Illness Benefit" lists 30 conditions but pays a lump sum only if the insured survives 30 days after diagnosis, or pays in tranches (e.g. 20% on diagnosis of the specified condition, 80% on confirmed heart attack). Same HK$1,000,000 headline, materially different expected claim.
- Deductible architecture differs. Carrier A's medical plan has a HK$0 deductible with an HK$8,000 annual limit and HK$850/day room-and-board capped at HK$6,800. Carrier D's has a HK$20,000 annual deductible with an HK$8m annual limit, unlimited room-and-board up to HK$12,000/day in a private room, and an out-of-pocket cap of HK$200,000/yr. Compare HK$7,900 vs HK$5,200 of annual premium and you have compared two different products for different people. A healthy 32-year-old with no admissions in four years should usually prefer the deductible design; a 52-year-old with two admissions and a chronic condition should usually prefer the low-limit design.
- Occupation and smoking treatment differ invisibly. Client is an office manager in an architecture firm. Carrier A classes him occupation class 2. Carrier D classes office-based architects class 4 and office managers class 2, and has no occupation class for self-employed consultants at all. Carrier B's medical is flat-rated by age and smoking with a 1.75 multiplier for smokers. If you normalise only premium you will present a plan the client cannot actually buy.
The canonical benefit unit
The normalisation target used in the POS is deliberately boring and deliberately absolute:
Normalised first-year premium (HK$) per HK$1,000,000 of normalised guaranteed sum insured, plus a stated total-cost-of-ownership figure over the client's chosen horizon.
Two rules make this defensible:
- Guaranteed and non-guaranteed never mix. Non-guaranteed benefits (非保證利益 — terminal bonuses 終期紅利, reversionary bonuses 歸屬紅利, illustrated dividend options) are shown in a separate column, under an explicitly labelled illustration with the assumed investment return stated (illustrations are not projections; see the Insurance Authority Best Practice Guide on illustrations of non-guaranteed benefits). A normalised premium per unit of cover figure must use guaranteed sums assured only. If you fold illustrations into the denominator you are quoting a number that can evaporate.
- Non-equivalent benefit sets are normalised by mapping, not by averaging. Where a benefit does not map 1:1, you either build an explicit equivalence multiplier from the carrier's published benefit wording, or you mark the cell
NOT_EQUIVALENTand exclude that dimension from the scoring. Silently averaging incommensurable benefits is how mis-selling happens.
The normalisation engine
The engine takes a CarrierQuote (raw, carrier-shaped) and produces a NormalisedQuote (canonical) plus a NormalisationTrace that records every adjustment made, by whom, and why. The trace is what you hand to compliance when a client complains that the comparison was misleading.
// content is illustrative: shapes mirror the carrier-adapter boundary described above.
export type Money = { amount: number; currency: "HKD" }; // integer cents avoided; HKD only
export type GuaranteeTier = "GUARANTEED" | "NON_GUARANTEED";
export type Deduction = {
scheme: "ANNUAL_DEDUCTIBLE" | "ANNUAL_LIMIT" | "FRANCHISE" | "CO_PAY_PERCENT" | "NONE";
amount: Money;
appliesPer: "PER_YEAR" | "PER_CLAIM" | "PER_ADMISSION";
};
export type RoomBenefit = {
dailyLimit: Money;
maxDaily: Money; // e.g. private-room cap
coveredRoomClass: "SHARED" | "SEMI_PRIVATE" | "PRIVATE" | "DELUXE";
daysPerAdmissionMax: number | null;
};
export type RenewalCommitment = "GUARANTEED_RENEWAL" | "GUARANTEED_PERMANENT" | "ANNUAL_REVIEWABLE";
/** Raw, carrier-shaped benefit as it arrives from the adapter. */
export interface CarrierBenefit {
carrierId: string;
productCode: string;
productName: string;
productVersion: string; // MUST be pinned; never quote an unpinned version
rateTableEffectiveFrom: string; // ISO date
quotationNumber: string;
quotedAt: string; // ISO date-time
expiresAt: string; // ISO date-time — HK quotes commonly expire in 14–30 days
coverage: {
basicSumInsured: Money;
tier: GuaranteeTier;
termYears: number | null; // null = to age 100/105/120
premiumPayingYears: 1 | 5 | 10 | 15 | 20 | 25 | "LIFETIME";
benefits: BenefitLine[];
};
medical?: {
deduction: Deduction;
room: RoomBenefit;
outOfPocketCap: Money | null;
preExistingExclusionApplied: boolean;
renewalCommitment: RenewalCommitment;
};
risk: {
ageNextBirthday: number;
sex: "M" | "F";
smoker: boolean;
declaredConditions: string[];
occupationClass: number | null; // carrier-specific, NOT portable
loadingPercent?: number; // e.g. 50 = 50% loading
referralRequired: boolean;
};
premium: {
firstYear: Money;
subsequentYearAnnual?: Money; // level or stepped — steps must be itemised, never a single number
premiumSchedule: Array<{
policyYear: number;
premium: Money;
}>;
};
nonGuaranteedIllustration?: {
assumedReturnPercent: number;
projectedSumInsuredAtAge: number;
ageProjected: number;
};
}
export type BenefitLine =
| { kind: "LUMP_SUM_DEATH"; trigger: string; paysWithinDaysOfTrigger: number }
| { kind: "LUMP_SUM_DIAGNOSIS"; conditions: string[]; paysWithinDaysOfTrigger: number; staged?: number[] }
| { kind: "HOSPITAL_CASH"; perDay: Money; maxDays: number; triggers: string[] }
| { kind: "REIMBURSEMENT"; subLimit: Money; territory: "HK" | "HK_PLUS_OVERSEAS" };
/* ------------------------------------------------------------------ */
/* Step 1: equivalence mapping — the heart of normalisation */
/* ------------------------------------------------------------------ */
export type EquivalenceMultiplier = {
benefitKind: string;
carrierA: { value: number; source: string };
carrierB: { value: number; source: string };
multiplier: number | null; // null ⇒ NOT_EQUIVALENT, exclude from scoring
rationale: string;
};
/**
* Benefit equivalence is *authored by an underwriter*, not computed.
* The engine refuses to run if an equivalence row is missing, because
* an unauthored mapping is a guess and a guess in a client document is a
* misrepresentation risk.
*/
export function requireEquivalence(
rows: EquivalenceMultiplier[],
benefitKind: string,
): EquivalenceMultiplier {
const row = rows.find((r) => r.benefitKind === benefitKind);
if (!row) throw new MissingEquivalenceError(benefitKind);
if (row.multiplier === null) return row; // explicitly NOT_EQUIVALENT
if (!Number.isFinite(row.multiplier) || row.multiplier <= 0) {
throw new InvalidEquivalenceError(benefitKind);
}
return row;
}
export interface NormalisationAdjustment {
field: string;
rule: string; // stable rule id, e.g. "R-MED-DED-01"
from: string | number | null;
to: string | number | null;
reason: string;
authorisedBy: string; // underwriter id — four-eyes, see Lesson 09
}
export interface NormalisationTrace {
quoteId: string;
engineVersion: string;
adjustedAt: string;
adjustments: NormalisationAdjustment[];
excludedDimensions: string[]; // dimensions marked NOT_EQUIVALENT
reviewedBySecondPairOfEyes: boolean;
}
export interface NormalisedQuote {
quoteId: string;
carrierId: string;
productCode: string;
productVersion: string;
quotationNumber: string;
validUntil: string;
guaranteedSumInsured: Money;
/** premium per HK$1m of normalised guaranteed cover — the headline comparable */
premiumPerMillion: number;
/** total of scheduled premiums across the client's horizon — for savings/participating */
totalPremiumOverHorizon: Money;
horizonYears: number;
/** never summed with guaranteed figures; client must be told these may fall to zero */
nonGuaranteedIllustrated: { assumedReturnPercent: number; projectedSumInsured: Money } | null;
medical?: {
annualDeductible: Money;
outOfPocketCap: Money | null;
effectiveRoomClass: string;
renewalCommitment: RenewalCommitment;
};
underwritingOutcome: "STANDARD" | "STANDARD_WITH_EXCLUSION" | "LOADED" | "REFERRED" | "DECLINED";
loadingPercent: number;
trace: NormalisationTrace;
}
/* ------------------------------------------------------------------ */
/* Step 2: the normalisation itself */
/* ------------------------------------------------------------------ */
const HK_PER_MILLION = 1_000_000;
export function normaliseQuote(
raw: CarrierQuote,
client: NormalisationContext,
): NormalisedQuote | { outcome: "DECLINED"; reason: string } {
// 0. Refuse to normalise anything the compliance layer will not honour.
assertQuotationIsLive(raw); // expiresAt must be in the future
assertVersionIsPinned(raw.productVersion);
assertRateTableEffective(raw.rateTableEffectiveFrom, client.effectiveDate);
const adjustments: NormalisationAdjustment[] = [];
const excluded: string[] = [];
// 1. Decide the horizon. The agent must state it; the engine must not infer it.
const horizonYears = client.horizonYears;
// 2. Drop non-guaranteed benefit lines from the comparable sum insured.
const guaranteedLines = raw.coverage.benefits.filter((b) => b.payTier === "GUARANTEED");
const droppedLines = raw.coverage.benefits.filter((b) => b.payTier === "NON_GUARANTEED");
if (droppedLines.length > 0) {
adjustments.push({
field: "coverage.benefits",
rule: "R-GT-01",
from: raw.coverage.benefits.length,
to: guaranteedLines.length,
reason: `${droppedLines.length} non-guaranteed benefit line(s) excluded from the comparable sum insured`,
authorisedBy: "SYSTEM",
});
}
// 3. Normalise sum insured into a comparable *cover unit*.
const coverUnit = normaliseCoverUnit(
raw.coverage.basicSumInsured,
raw.coverage.termYears,
client,
adjustments,
);
// 4. Normalise the premium to the horizon.
const schedule = buildHorizonSchedule(raw.premium.premiumSchedule, horizonYears);
const totalPremiumOverHorizon = sumMoney(schedule);
// 5. Medical architecture normalisation.
let medical: NormalisedQuote["medical"];
if (raw.medical) {
// Deductible designs are NOT equivalent; carry the flag rather than a number.
if (raw.medical.deduction.scheme === "ANNUAL_LIMIT") {
excluded.push("deductible_vs_annual_limit");
adjustments.push({
field: "medical.deduction",
rule: "R-MED-DED-02",
from: "ANNUAL_LIMIT",
to: "NOT_EQUIVALENT",
reason:
"annual-limit medical architecture cannot be compared per HK$1m on the same basis as an " +
"annual-deductible plan; compare out-of-pocket exposure instead",
authorisedBy: client.underwriterId,
});
}
medical = {
annualDeductible:
raw.medical.deduction.scheme === "ANNUAL_DEDUCTIBLE"
? raw.medical.deduction.amount
: { amount: 0, currency: "HKD" },
outOfPocketCap: raw.medical.outOfPocketCap,
effectiveRoomClass: raw.medical.room.coveredRoomClass,
renewalCommitment: raw.medical.renewalCommitment,
};
}
// 6. Loading: a loaded quote is a *different product at a different price*.
const loadingPercent = raw.risk.loadingPercent ?? 0;
if (loadingPercent > 0) {
adjustments.push({
field: "premium.firstYear",
rule: "R-UND-01",
from: raw.premium.firstYear.amount,
to: round2(raw.premium.firstYear.amount * (1 + loadingPercent / 100)),
reason: `substandard loading ${loadingPercent}% applied by underwriting`,
authorisedBy: raw.carrierId,
});
}
// 7. Occupation class is carrier-specific and must never be compared numerically.
// We keep it for display but flag it.
adjustments.push({
field: "risk.occupationClass",
rule: "R-OCC-01",
from: raw.risk.occupationClass ?? null,
to: raw.risk.occupationClass ?? null,
reason:
"occupation class is carrier-specific; class numbers are NOT comparable across carriers " +
"and are excluded from scoring",
authorisedBy: "SYSTEM",
});
excluded.push("occupation_class_number");
const adjustedFirstYear = round2(
raw.premium.firstYear.amount * (1 + loadingPercent / 100),
);
const premiumPerMillion =
coverUnit.hkdEquivalent >= HK_PER_MILLION
? round2(
(adjustedFirstYear * HK_PER_MILLION) / coverUnit.hkdEquivalent,
)
: null;
const trace: NormalisationTrace = {
quoteId: raw.quotationNumber,
engineVersion: "norm-engine/2.4.0",
adjustedAt: new Date().toISOString(),
adjustments,
excludedDimensions: excluded,
reviewedBySecondPairOfEyes: false,
};
return {
quoteId: raw.quotationNumber,
carrierId: raw.carrierId,
productCode: raw.productCode,
productVersion: raw.productVersion,
quotationNumber: raw.quotationNumber,
validUntil: raw.expiresAt,
guaranteedSumInsured: raw.coverage.basicSumInsured,
premiumPerMillion,
totalPremiumOverHorizon,
horizonYears,
nonGuaranteedIllustrated: raw.nonGuaranteedIllustration
? {
assumedReturnPercent: raw.nonGuaranteedIllustration.assumedReturnPercent,
projectedSumInsured: {
amount: raw.nonGuaranteedIllustration.projectedSumInsuredAtAge,
currency: "HKD",
},
}
: null,
medical,
underwritingOutcome: raw.risk.referralRequired
? "REFERRED"
: loadingPercent > 0
? "LOADED"
: raw.medical?.preExistingExclusionApplied
? "STANDARD_WITH_EXCLUSION"
: "STANDARD",
loadingPercent,
trace,
};
}
Note two design decisions in that engine that are worth more than the code itself. First, normaliseQuote refuses to run on an expired quotation or an unpinned product version — a stale quote in a bid sheet is worse than no quote, because it invites a client to make a decision on information the agent knew was out of date. Second, every adjustment carries a rule id. When a client or a regulator asks "why is Carrier D showing HK$7,412 per million when its quote says HK$6,980?", the answer is a rule id lookup, not a recollection.
Worked normalisation: three medical quotes, one client
Client: Chan Siu Ming (陳小明), age 34 next birthday, non-smoker, no declared conditions, history of two day-cases in 2019 (appendicectomy, D&C), employed as an office manager in a property management company, budget HK$8,000–9,000/month for a family of three. Horizon chosen by the agent and stated on the sheet: 20 years, because the youngest child is 6 and the agent is recommending the parent's income protection to age 65 with the medical running alongside.
The key input the agent had to normalise first: "hospital cash of HK$1,000/day" does not mean the same thing at the three carriers, so the agent asked each carrier what triggers payment.
| Dimension | Carrier A · "MediCare Choice" | Carrier D · "HealthGuard HD" | Carrier F · "CarePlus Prime" |
|---|---|---|---|
| Version | v3.2 (eff. 2025-04-01) | v5.0 (eff. 2025-07-15) | v2.1 (eff. 2025-01-20) |
| Architecture | Full reimbursement, HK$0 deductible, HK$8,000 annual limit | High deductible: HK$20,000/yr, HK$8m limit, HK$200,000 out-of-pocket cap | Deductible HK$5,000/yr, HK$3m limit, HK$100,000 out-of-pocket cap |
| Room & board | HK$850/day, capped at HK$6,800/admission | HK$850/day standard / HK$1,600 private, capped HK$12,000/day | HK$800/day, cap HK$5,500/admission |
| Hospital cash | HK$1,000/day, any admission ≥ 6 hours | HK$1,000/day, surgical admissions only | HK$800/day, admission ≥ 24 hours or HK$500 for 6–23 hours |
| Day-case benefit | HK$1,000 lump per day-case | nil | HK$600 lump per day-case |
| Guaranteed renewal | Guaranteed, annual review of rates only | Guaranteed renewal to age 120 | Guaranteed to 65, then annual reviewable |
| Declared conditions effect | none (below underwriting thresholds) | none | none |
| First-year premium | HK$7,880 | HK$5,240 | HK$6,410 |
| 20-year premium total | HK$157,600 (level) | HK$104,800 + 5% steps by year 15 | HK$128,200 (level) |
Now the normalisation the agent did, in the open, on the sheet:
- Hospital cash is not equivalent. Carrier A pays HK$1,000 for any admission ≥ 6h; Carrier F pays HK$500 for the same 6–23h admission. For Chan, whose 2019 appendicectomy was a 1-night stay, the realised difference is real money. Carrier D pays nothing for the 1,400+ non-surgical admissions a 34-year-old man generates over 20 years. So
hospital_cashis marked NOT_EQUIVALENT and moved to a narrative column, with the agent's stated position: "if you are in hospital for anything other than an operation, this plan pays you HK$1,000 a day and the other two do not." - Architecture is not equivalent. The agent refuses to score annual premium per million for the medical plans, because an HK$8,000 annual limit and an HK$200,000 out-of-pocket cap are not the same promise. Instead the sheet shows a modelled two-year family cost for each: Chan stays healthy for the first 18 months, then the father has a HK$260,000 emergency appendicectomy in a private room (HK$2 days private @1,600 + surgeon + anaesthesia + drugs + HK$5,000 deductible, capped at HK$200,000 out-of-pocket).
- Carrier A: pays HK$2,600 (2 days cash @1,000 + 1 day-case 0) → out-of-pocket HK$257,400 (annual limit exhausted immediately).
- Carrier D: pays HK$3,200 → out-of-pocket HK$60,000 (HK$5,000 deductible + HK$50,000 before the HK$200k annual out-of-pocket cap is reached; total household exposure capped at HK$205,000/yr).
- Carrier F: pays HK$1,600 + HK$600 day-case = HK$2,200 → out-of-pocket HK$70,600 (HK$5,000 deductible + HK$65,600).
- The agent's recommendation, written on the sheet: Carrier D on architecture, Carrier A on cash flow, and a 30-day decision window. The honest summary is that Carrier A's premium is 50% higher and buys Chan a guaranteed, unlimited-in-time annual-limit benefit that suits a young healthy family with a large savings buffer; Carrier D's deductible design suits a family that would be financially devastated by a HK$260,000 bill. The bid sheet says both, in those words.
Rules that make the engine defensible
| Rule id | Rule | Why it exists |
|---|---|---|
R-GT-01 | Non-guaranteed benefit lines never enter the comparable sum insured | Illustrated benefits may fall to zero; comparing them to guaranteed cover misleads |
R-GT-02 | Illustrations must state assumed return %, age, and be labelled not-a-projection | IA best practice on illustrations; also stops "you'll get $3m at 65" claims |
R-MED-DED-02 | Annual-limit vs annual-deductible architectures are NOT_EQUIVALENT | Forces a modelled-out-of-pocket comparison instead of a fake per-million number |
R-MED-DED-03 | Out-of-pocket cap is reported as a household figure, never a policy figure | Clients read "HK$200,000 cap" as "I only ever lose HK$200,000"; the real statement is per insured per policy year |
R-OCC-01 | Occupation class numbers excluded from scoring | Class 2 at one carrier is class 4 at another |
R-UND-01 | Loading applied to first-year premium and shown on the sheet | A loaded quote is a different price; hiding it creates a referral dispute at claim time |
R-QTE-01 | Quotation expiry hard-stops bid sheet generation | Stale pricing in a client document is a mis-sale vector |
R-VER-01 | Product version pinned to rate-table effective date | Product wording and rates change; an unpinned quote cannot be reproduced |
R-NS-01 | Existing-policy overlap triggers NS/referral determination before presentation | IA best practice on referral schemes; prevents a complaint that cannot be answered |
R-CMP-01 | Minimum 3 carriers attempted for a comparison case; fewer requires a recorded reason | One-carrier comparisons are the signature of agent-captured business |
-- The bid sheet must be reconstructable years later. Store the whole thing.
CREATE TABLE bid_sheet (
bid_sheet_id TEXT PRIMARY KEY,
client_id TEXT NOT NULL REFERENCES client(client_id),
agent_id TEXT NOT NULL REFERENCES agent(agent_id),
created_at TEXT NOT NULL,
horizon_years INTEGER NOT NULL,
assumption_notes TEXT NOT NULL,
engine_version TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN
('DRAFT','ISSUED','VIEWED','ACCEPTED','DECLINED','WITHDRAWN','EXPIRED')),
selected_quote_id TEXT REFERENCES carrier_quote(quotation_number)
);
CREATE TABLE carrier_quote (
quotation_number TEXT PRIMARY KEY,
carrier_id TEXT NOT NULL,
product_code TEXT NOT NULL,
product_version TEXT NOT NULL,
rate_table_effective_from TEXT NOT NULL,
quoted_at TEXT NOT NULL,
expires_at TEXT NOT NULL,
outcome TEXT NOT NULL CHECK (outcome IN
('STANDARD','STANDARD_WITH_EXCLUSION','LOADED','REFERRED','DECLINED','WITHDRAWN')),
outcome_reason TEXT,
raw_payload_json TEXT NOT NULL, -- verbatim carrier response, never mutated
adapter_version TEXT NOT NULL
);
CREATE TABLE normalisation_trace (
trace_id TEXT PRIMARY KEY,
bid_sheet_id TEXT NOT NULL REFERENCES bid_sheet(bid_sheet_id),
quote_id TEXT NOT NULL REFERENCES carrier_quote(quotation_number),
engine_version TEXT NOT NULL,
adjustments_json TEXT NOT NULL,
excluded_dimensions_json TEXT NOT NULL,
second_review_by TEXT REFERENCES agent(agent_id),
second_reviewed_at TEXT
);
CREATE TABLE bid_sheet_line (
bid_sheet_id TEXT NOT NULL REFERENCES bid_sheet(bid_sheet_id),
quote_id TEXT NOT NULL REFERENCES carrier_quote(quotation_number),
display_rank INTEGER NOT NULL,
normalised_json TEXT NOT NULL,
presentation_note TEXT, -- the agent's own words, shown verbatim to the client
PRIMARY KEY (bid_sheet_id, quote_id)
);
-- Every carrier you approached, including the one that said no.
CREATE TABLE comparison_attempt (
bid_sheet_id TEXT NOT NULL REFERENCES bid_sheet(bid_sheet_id),
carrier_id TEXT NOT NULL,
attempted_at TEXT NOT NULL,
declined_to_quote INTEGER NOT NULL DEFAULT 0,
decline_reason TEXT,
ns_check_result TEXT NOT NULL CHECK (ns_check_result IN
('NOT_APPLICABLE','NO_OVERLAP','NS_EXISTING_AGENT_HELD','REFERRED_WITH_CONSENT','BLOCKED')),
PRIMARY KEY (bid_sheet_id, carrier_id)
);
CREATE INDEX ix_bid_sheet_client ON bid_sheet(client_id, created_at DESC);
CREATE INDEX ix_trace_quote ON normalisation_trace(quote_id);
Building a Bid Sheet
In practice: the agent opens a new bid sheet in the POS, fans the quote out to the four carriers they actively represent, waits for three responses, and presses Generate — which produces a paginated PDF that can be emailed to the client before the meeting ends, with every figure hyperlinked to its source quotation.
What a bid sheet is, precisely
A bid sheet (比價單 / 建議書對比表) is a decision document, not a rate table. It has five jobs and each one has a hard requirement:
| Job | Content requirement | Failure mode if you skip it |
|---|---|---|
| Show the client there was a genuine choice | Minimum 3 carriers attempted, including declines and referrals | Client assumes you only have one product; no leverage, no trust, no referral from their friends |
| Make the comparison honest | Normalised basis stated in one line at the top of the page, non-equivalent dimensions flagged | Mis-sale complaint; IA investigation; commission clawback |
| State the price exactly | First-year premium, full subsequent-year schedule (never "about HK$700/month thereafter"), and the horizon total | Client's first-year claims experience differs from expectation → complaint; the commonest complaint in the market |
| State what is not guaranteed | Separate block, labelled, with assumed return % and the words "may be reduced or not paid at all" | Mis-selling on non-guaranteed benefits; the most heavily penalised area of agency conduct |
| Carry the audit trail | Quote numbers, product versions, issue/expiry dates, agent name and IA registration-relevant details | Six months later you cannot prove what you said |
The one-line basis statement is the piece most often missing and the piece most worth memorising:
"All figures below are for guaranteed benefits only, on a per HK$1,000,000 of sum insured basis, over a 20-year horizon assuming level premiums. Benefits marked † are not directly comparable between carriers and are explained in the note. Non-guaranteed benefits are shown separately in Appendix B and are not included in any comparison here."
The page-by-page layout the POS renders
BID SHEET — Prepared 2026-03-11 14:22 HKT
Client: CHAN SIU MING (陳小明) Agent: WONG KA HO (FSIR no. on file)
Client profile used for rating: age 34 (next birthday), male, non-smoker,
no declared conditions, occupation "office manager (property mgmt)"
Horizon chosen by agent: 20 years — reason: youngest child is 6;
income protection recommended to age 65
Carriers approached: 4 Live comparable quotes: 2 + 1 NOT_EQUIVALENT on 2 dimensions
PAGE 1 — Summary comparison
column: A | D | F
1-year premium 7,880 5,240 6,410
20-year premium total 157,600 104,800 128,200
premium per HK$1m 7,880 5,240 6,410
sum insured 1,000,000 (level) 1,000,000 (level) 1,000,000 (level)
underwriting standard standard standard
PES applied no no no
renewal guaranteed guaranteed guaranteed to 65
quote ref Q-77120 Q-90344 Q-51288
expires 2026-04-01 2026-04-02 2026-03-28
† modelled out-of-pocket, HK$260k private surgical episode:
257,400 60,000 70,600
PAGE 2 — Medical architecture in words (the "read this before you decide")
agent-authored, max 250 words, no superlatives, no competitor disparagement
PAGE 3 — Benefit-by-benefit detail with NOT_EQUIVALENT flags and rules applied
PAGE 4 — Underwriting outcomes including declines and referrals, with reasons
Carrier B — DECLINED: smoker/applicant age band + declared hypertension (see Lesson 06)
Carrier C — REFERRED to underwriter; response expected 2026-03-14, not in this sheet
Carrier E — DECLINED TO QUOTE: occupation class not defined for self-employed
consultants in this plan; the client is employed, so reclassified as office manager
on 2026-03-11 and re-quoted — see comparison_attempt row
PAGE 5 — APPENDIX B: non-guaranteed benefits, clearly boxed, grey background,
stating assumed return 4.0% p.a. for illustration only, not a projection,
may be reduced or not paid at all
PAGE 6 — Declarations the client signs: I have read the benefit definitions; I
understand the non-equivalent dimensions flagged †; I understand the
premiums shown assume level premiums and are subject to the schedule on page 1;
I have been told which carriers declined and why.
That last block matters. A signed acknowledgement that the client saw the flags is worth a great deal in a complaint investigation, and it costs one checkbox in the POS.
The generator: turning normalised quotes into the sheet
export interface BidSheetContext {
client: ClientProfile;
agent: AgentProfile;
horizonYears: number;
horizonReason: string;
attempts: ComparisonAttempt[]; // includes declines
quotes: CarrierQuote[];
equivalences: EquivalenceMultiplier[];
narrative: {
architectureNote: string; // agent-authored, length-checked
benefitNotes: Record<string, string>;
};
}
export interface BidSheet {
bidSheetId: string;
generatedAt: string;
basisStatement: string;
rows: ComparisonRow[];
attempts: ComparisonAttempt[];
appendixB: NonGuaranteedAppendix;
pdfPath: string;
sha256: string; // tamper-evidence for the audit record
clientAcknowledgementRequired: boolean;
}
export interface ComparisonRow {
label: string;
unit: string;
values: Array<{ carrierId: string; value: string | number | null; comparable: boolean }>;
notEquivalent?: boolean;
footnote?: string;
}
export function generateBidSheet(ctx: BidSheetContext, quotes: NormalisedQuote[]): BidSheet {
// 1. Gate: a comparison case needs at least three carriers *attempted*.
if (ctx.attempts.length < 3) {
throw new InsufficientComparisonError(
`only ${ctx.attempts.length} carrier(s) attempted; rule R-CMP-01 requires 3`,
);
}
// A recorded reason is not an escape hatch if the reason is empty.
for (const a of ctx.attempts) {
if (a.declinedToQuote && !a.declineReason?.trim()) {
throw new MissingDeclineReasonError(a.carrierId);
}
}
// 2. Gate: no live quote may be expired, and NS status must be resolved
// for every carrier before anything is shown to the client.
for (const a of ctx.attempts) {
if (a.nsCheckResult === "BLOCKED") {
throw new BlockedByNsCheckError(a.carrierId);
}
if (a.nsCheckResult === "NS_EXISTING_AGENT_HELD") {
throw new ReferralConsentRequiredError(a.carrierId);
}
}
const rows: ComparisonRow[] = [
row("First-year premium", "HKD", quotes.map((q) => ({
carrierId: q.carrierId,
value: q.trace.adjustments.find((x) => x.rule === "R-UND-01")?.to ?? 0,
comparable: true,
}))),
row("Premium per HK$1m of guaranteed cover", "HKD", quotes.map((q) => ({
carrierId: q.carrierId,
value: q.premiumPerMillion,
comparable: true,
}))),
row(`Total premium over ${ctx.horizonYears} years`, "HKD", quotes.map((q) => ({
carrierId: q.carrierId,
value: q.totalPremiumOverHorizon.amount,
comparable: true,
}))),
row("Underwriting outcome", "—", quotes.map((q) => ({
carrierId: q.carrierId,
value: q.underwritingOutcome,
comparable: true,
}))),
row("Renewal commitment", "—", quotes.map((q) => ({
carrierId: q.carrierId,
value: q.medical?.renewalCommitment ?? "n/a",
comparable: true,
}))),
// Non-comparable dimension: carried across, explicitly flagged, never scored.
{
label: "Modelled out-of-pocket for a HK$260,000 private surgical episode",
unit: "HKD",
notEquivalent: true,
footnote:
"† Not comparable on premium alone — annual-limit and annual-deductible architectures " +
"protect different financial profiles. Modelled by the agent from published benefit limits.",
values: ctx.quotes.map((q) => ({
carrierId: q.carrierId,
value: q.modelledOutOfPocket?.hkd ?? null,
comparable: false,
})),
},
// Second non-comparable dimension.
{
label: "Hospital cash trigger",
unit: "—",
notEquivalent: true,
footnote:
"† Carriers define the qualifying admission differently (≥6h vs surgical only vs ≥24h). " +
"For a client with two admissions under 24h in the last five years this is decisive.",
values: ctx.quotes.map((q) => ({
carrierId: q.carrierId,
value: q.hospitalCashTrigger ?? null,
comparable: false,
})),
},
];
const basis = buildBasisStatement(ctx.horizonYears, ctx.horizonReason);
const sheet: BidSheet = {
bidSheetId: newId("BDS"),
generatedAt: new Date().toISOString(),
basisStatement: basis,
rows,
attempts: ctx.attempts,
appendixB: buildNonGuaranteedAppendix(quotes),
pdfPath: `bid_sheets/${newId("BDS")}.pdf`,
sha256: "", // filled after render
clientAcknowledgementRequired: true,
};
return sheet;
}
function buildBasisStatement(horizon: number, reason: string): string {
return (
`All figures are for guaranteed benefits only, on a per HK$1,000,000 of sum insured basis, ` +
`over a ${horizon}-year horizon assuming level premiums. Dimensions marked † are not ` +
`directly comparable between carriers and are explained in the note. Non-guaranteed benefits ` +
`appear in Appendix B, are based on an assumed return stated there, and are not included in ` +
`any comparison. Horizon reason: ${reason}.`
);
}
/** Appendix B is the most-litigated page in the file. It never shares a table with guaranteed figures. */
function buildNonGuaranteedAppendix(quotes: NormalisedQuote[]): NonGuaranteedAppendix {
return {
disclaimer:
"The values shown are illustrations based on the assumed return stated. They are not " +
"projections, not guarantees, and the benefits may be reduced or not paid at all. " +
"Non-guaranteed benefits are not part of the sum insured and do not increase the guaranteed " +
"death benefit shown in the main comparison.",
entries: quotes
.filter((q) => q.nonGuaranteedIllustrated)
.map((q) => ({
carrierId: q.carrierId,
assumedReturnPercent: q.nonGuaranteedIllustrated!.assumedReturnPercent,
projectedAtAge: q.nonGuaranteedIllustrated!.ageProjected,
projectedSumInsured: q.nonGuaranteedIllustrated!.projectedSumInsured.amount,
})),
};
}
Two gates in that generator are worth calling out to an agent directly. First, R-CMP-01 blocks the PDF from generating if fewer than three carriers were attempted — including cases where you did try and were declined to quote. Second, the NS gate blocks the PDF if the client holds a same-class policy with a carrier whose existing agent has not consented in writing. In both cases the POS shows a red banner naming the exact missing item, not a generic error. That is deliberate: the agent should be prevented from producing a document, not from producing a document and hoping.
The signed acknowledgement, as data
{
"bidSheetId": "BDS-2026-00417",
"client": { "id": "C-88213", "nameZh": "陳小明", "nameEn": "CHAN SIU MING" },
"issuedAt": "2026-03-11T14:22:31+08:00",
"sha256": "9f2c7b1e4a8d0c3f5b6e2a19d4c7b8e0f3a6d9c2b5e8f1a4d7c0b3e6f9a2d5c8",
"acknowledgements": [
{ "id": "ACK-1", "text": "I have read the benefit definitions for each quoted plan.", "accepted": true },
{ "id": "ACK-2", "text": "I understand that the dimensions marked † are not directly comparable and have read the explanation.", "accepted": true },
{ "id": "ACK-3", "text": "I understand premiums assume a level schedule as stated on page 1 and are subject to the schedule shown there.", "accepted": true },
{ "id": "ACK-4", "text": "I have been told which carriers declined or referred my case and the reasons given.", "accepted": true },
{ "id": "ACK-5", "text": "I understand the non-guaranteed benefits in Appendix B may be reduced or not paid at all.", "accepted": true },
{ "id": "ACK-6", "text": "I have had the opportunity to ask questions and have received satisfactory answers.", "accepted": true }
],
"signedAt": "2026-03-11T16:04:09+08:00",
"signature": {
"method": "E_SIGNATURE",
"methodDetail": "adopted handwritten signature on tablet, drawn in the client's own hand",
"signedBy": "陳小明",
"ipOrDevice": "ipad-9f3c…",
"timestampedBy": "signsvc/1.8.2"
},
"agentAttestation": {
"agentId": "AG-2210",
"attestedAt": "2026-03-11T16:05:40+08:00",
"carriersPresented": ["CARR_A", "CARR_D", "CARR_F"],
"carriersDeclinedOrReferred": [
{ "carrierId": "CARR_B", "result": "DECLINED", "reason": "smoker/applicant band + declared hypertension" },
{ "carrierId": "CARR_C", "result": "REFERRED", "eta": "2026-03-14" },
{ "carrierId": "CARR_E", "result": "DECLINED_TO_QUOTE", "reason": "occupation class undefined; reclassified and re-quoted" }
]
}
}
Presenting the Comparison
In practice: the agent does not email the PDF and wait. The agent prints two copies, sits the client down, turns the sheet sideways, and spends twenty minutes on page 1 — starting with what the client cannot have, not with what they can.
The presentation order that works
- Start with the exclusions. "Before you look at any premium, here is what each plan does not cover." Clients who understand the exclusions first do not later feel misled. This is also the single most effective compliance behaviour available to an agent.
- Show the client that carriers disagreed. Read out the declines and the referral. Naming Carrier B's decline out loud does three things: it demonstrates you worked, it demonstrates the client's profile is not a universal one, and it makes the eventual "yes, Carrier D" feel like the client's decision rather than your only option.
- Present the non-comparable dimensions as the decision. If two architectures are not comparable, the decision is which financial profile the client has, not which premium is smaller. Say it in those words: "These two are not better and worse. They are built for a family with savings and a family without."
- Give a recommendation with the runner-up. "My recommendation is D, and if you want a plan that does not have a deductible at all, it is A, and it costs you HK$2,640 more a month." Naming the runner-up is the cheapest credibility available.
- State the horizon and the reason out loud. "I have costed this over twenty years because your youngest is six. If you think the need changes at ten, say so and I will re-run it."
- Close on the deadline, honestly. Quotation numbers expire. "These prices hold until the 1st of April. After that I have to re-quote and I cannot promise the same number." A manufactured deadline is both obvious and, if the client notices, fatal to trust.
Language discipline
| Do not say | Say instead | Why |
|---|---|---|
| "This plan will cover your cancer treatment." | "This plan pays your claim up to the stated limit for the covered events listed on page 3, subject to the exclusions on page 4." | "Will cover" implies a promise about claims outcome |
| "You'll get $3 million by 65." | "If the bonuses declared average the 4% assumed, the illustration shows $3m. If they average zero, you get the guaranteed $1m." | Non-guaranteed benefits; the most penalised area of agency conduct |
| "Carrier A is bad value." | "Carrier A's premium is 50% higher and it removes your deductible. Whether that is worth it depends on your savings." | Disparagement of a competitor; also just wrong about the client |
| "That's the market rate." | "That is the rate on Carrier D's current version for a 34-year-old non-smoker. I have two other quotes on this sheet." | "Market rate" implies you checked all 130 insurers |
| "It won't lapse." | "It lapses if premium is not paid for three months from the due date. Free-look is 21 days — see Lesson 10." | Lapse statements are a persistent source of complaint |
| "This covers pre-existing conditions." | "Carrier A declined to apply an exclusion to your two 2019 admissions because they were below their threshold. Carrier D did the same. Here is the wording." | PES is applied at underwriting, never assumed by the agent |
What goes in the file afterwards
The POS writes, automatically on issuance: the bid sheet PDF with its hash, the acknowledgement, the client view event (opened, page, timestamp), the presentation notes, and the no-recontact timer if declined. If the client asks for 45 days to think, that is a scheduling fact, not a suspension of the file.
export function onClientDecision(input: {
bidSheetId: string;
decision: "ACCEPT" | "DECLINE" | "DEFER";
selectedQuoteId?: string;
deferUntilDays?: number;
}): Outcome {
const sheet = loadBidSheet(input.bidSheetId);
if (sheet.status === "EXPIRED") throw new StaleDecisionError(sheet.bidSheetId);
switch (input.decision) {
case "ACCEPT": {
if (!input.selectedQuoteId) throw new MissingSelectionError();
// Acceptance is an instruction to start Lesson 08, not an end state.
const app = createApplicationFromQuote(input.selectedQuoteId, sheet);
markOtherQuotesSuperseded(sheet, input.selectedQuoteId);
return { status: "ACCEPTED", next: `application/${app.applicationId}`, freezeQuotes: true };
}
case "DEFER": {
const days = Math.min(Math.max(input.deferUntilDays ?? 45, 1), 90);
scheduleReQuotation(sheet, days); // before expiry, not after
armNoContactTimer(sheet.clientId, days * 2); // polite, auditable
return { status: "DEFERRED", reQuoteOn: addDays(new Date(), days) };
}
case "DECLINE": {
closeAllQuotes(sheet);
// Do not train "no" as "no forever". One re-contact is permitted in 24 months.
armNoContactTimer(sheet.clientId, 730, reason: "DECLINED_COMPARISON");
recordRefusalReason(sheet, await askAgentWhy()); // required, free text, min 20 chars
return { status: "DECLINED" };
}
}
}
armNoContactTimer(sheet.clientId, 730) is the quiet compliance feature agents use without noticing. If the POS did not stop you contacting a client who said no, the Compliance (合規) module would flag 20+ contacts to a declined client as a red line — and it would be right to.
Switching Cost
In practice: a client who asks "what if I move my policy to another company?" is asking a question about friction, not about price. The agent's job is to name the friction accurately — and that means being honest that the friction is real.
What switching actually costs
Switching is not a free option, and pretending otherwise is how agencies lose their reputation. The real costs:
| Cost component | Realistic value in HK | Notes |
|---|---|---|
| Surrender value shortfall | 5%–35% of the policy's cash value | The dominant cost. Surrender value is usually well below the sum assured and well below the accumulated premiums |
| New underwriting | Free for standard cases; can decline, load, or apply PES | Health disclosures must be repeated in full — the agent's ageing file does not substitute for a medical report if the carrier asks |
| New premium holiday | None by default | A new plan starts at premium due |
| Re-underwriting age band | Almost always at a worse band | You are older than when you bought; a 40 → 50 jump can add 30%–60% to a medical premium |
| Disruption of claims continuity | Typically resets nothing for medical, but resets 2-year waiting periods for new accident/CI riders | A new CI plan starts a fresh 30–90 day waiting period and a fresh survival period |
| Time and paperwork | 2–6 hours across 2–4 weeks | New medical records, financial evidence, signatures, delivery |
| Loss of no-claim or loyalty discounts | Varies | Some carriers give a loyalty discount at age 60/65 that resets at a new carrier |
| Agent relationship | Not a cost — an asset | The current agent's servicing record and knowledge of the case history matter more than most clients realise |
The calculation an agent should be able to do in the meeting
Switch if: surrenderValue_A + newPolicyFirstYearPremium_A < surrenderValue_B + newPolicyFirstYearPremium_B
Since surrender value today is identical for A and B (same policy), the test collapses to:
newPolicyPremium_A < newPolicyPremium_B by more than the transaction cost of doing it.
Example: existing policy, cash value at 2026-03-11 = HK$412,000.
Option 1 — keep: pay HK$0 surrender cost, premium HK$38,400/yr continuing.
Option 2 — surrender + replace: surrender at 88% of value → HK$412,000 - tax (HK$0, insurance
surrender is capital gains-exempt; the treaty is generous) = HK$362,560 received,
lose HK$49,440. New plan premium HK$44,000/yr → saves HK$5,600/yr, break-even in 8.8 years.
If the client's horizon is under 8.8 years: DO NOT SWITCH. Say so.
The discipline this demands: compute the break-even and show it. "It would take you just under nine years to get your money back, so if you're fairly sure you'll be gone or insured elsewhere in five years, keeping the policy is the better decision" is a sentence that wins more clients than any product pitch, because it is the first time anyone has talked to them like a rational adult.
The case where switching IS right
- The original agent stopped responding, or there is a service failure the carrier cannot resolve. Cost of the friction is worth paying.
- The plan has a decreasing sum assured and the client's need has risen.
- The plan's guaranteed benefits were reduced, or the carrier withdrew the product and the client was moved onto an uncompetitive successor plan.
- The original plan was participating with a persistently low declared bonus and the client has a strong reason to think the guarantee mechanism is not working.
- The original agent held the policy on a replacement basis from a competitor within the first two years, and the client now wants to deal with the competitor's agent.
Replacement and NS: the case that ends careers badly
An agent persuades a client to replace a policy held with another carrier. Eighteen months later the client complains to that carrier. The carrier's compliance department produces: your agent's call notes, the recorded pre-call statement that the client had "a policy I am not happy with," the comparison sheet you prepared, and the surrender form signed at the counter.
The path that avoids this is entirely procedural and entirely boring: the incumbent agent is contacted in writing, the case is registered as NS (non-solicited) or referral with consent, the commission sharing is agreed and recorded, and the replacement is disclosed to the incumbent. If the incumbent refuses consent, the honest answer to the client is "I can look at other options for you, but I cannot take over your existing policy from the agent who holds it, and neither can anyone else without that agent's agreement."
stateDiagram-v2
[*] --> ExistingPolicyHeld
ExistingPolicyHeld --> NSAssessment: client mentions existing policy
NSAssessment --> OverlapNone: no same-class policy found
NSAssessment --> IncumbentAgentHeld: same-class policy at another agency
NSAssessment --> SameAgencyHeld: same-class policy in your own book
IncumbentAgentHeld --> ConsentRequested: client wants to switch
IncumbentAgentHeld --> DeclinedToSwitch: incumbent refuses consent
ConsentRequested --> ReferralWithConsent: written consent + commission share agreed
ConsentRequested --> Expired: no response in 10 business days ⇒ treat as refusal
ReferralWithConsent --> BreakevenComputed: surrender + new premium calculated
DeclinedToSwitch --> AdviseNoSwitch: client keeps existing policy
Expired --> AdviseNoSwitch
OverlapNone --> BreakevenComputed
SameAgencyHeld --> BreakevenComputed
BreakevenComputed --> SwitchNotRecommended: horizon < break-even
BreakevenComputed --> SwitchRecommended: horizon > break-even
SwitchRecommended --> Reunderwrite: new medical disclosure + evidence
SwitchNotRecommended --> DocumentedNo: advice recorded in CRM
Reunderwrite --> Declined: new application declined or loaded
Reunderwrite --> Standard: new application issued
Declined --> KeepExisting: outcome documented for the client
Standard --> ReplacementDisclosed: incumbent notified in writing
ReplacementDisclosed --> Closed
AdviseNoSwitch --> Closed
DocumentedNo --> Closed
KeepExisting --> Closed
Handling Objections
In practice: the POS surfaces the top six objections as a one-tap coaching panel with the evidence attached, so the agent walks into the meeting knowing the published wording rather than improvising.
Objection 1 — "You are cheaper everywhere else"
The truthful response is that you are not, and that you could not be. Only a handful of general agents hold every shelf: ILAS, every medical architecture, every accident variant, offshore legacy books, and the specialist health products are distributed through different channels. Where the client is right — for a specific, genuinely cheaper, single-carrier product — the right move is to say so and refer them, which builds more business than a defensive answer. Where they are quoting an agent who is preparing to lapse the policy to commission, the answer is the break-even arithmetic above.
Objection 2 — "I want to think about it"
Do not discount it and do not attack it. The useful questions are: what specifically do you want to think about — the budget, the coverage, the carrier, or whether to insure at all? Then answer only that. If it is budget, re-run the horizon. If it is carrier, go back to the referral. If it is whether to insure at all, that is a need conversation you should have had earlier, and it is better to have it now.
Objection 3 — "Carrier A is cheaper by HK$200 a month"
Correct, and here is why, and here is what it buys. This objection is really a statement that the client has read the per-million figure and stopped. The whole point of normalisation is that the cheap number is cheap because of something. Name it. If the agent cannot name it, the agent should not have shown the number.
Objection 4 — "I already have medical cover through my employer"
Often true, often much thinner than the client believes. The check is three questions: what is the annual limit, what is the pre-existing exclusion situation for the employee, and what happens if the client leaves the job. Employer group medical is usually an indemnity plan with a per-admission cap, an annual limit, a defined-network or preferred-provider panel, and a pre-existing condition exclusion that does not apply to the employee but does to dependants. The POS should carry the employer-benefit capture in the client profile precisely so this objection becomes a two-minute question rather than a lost case.
Objection 5 — "Is my information safe?"
The answer is specific, not reassuring: what is collected, where it is stored (Hong Kong), who can see it at the carrier (the underwriter and the claims team, not the whole company), what leaves the building (name, DOB, HKID/contact, medical disclosure, banking — and only what the application requires), how long it is kept (per the record-keeping rule in Lesson 09, five years after the relationship ends or the policy ends, whichever is later), and how to ask for a copy or a correction. Vague assurances are worse than no answer.
Objection 6 — "What if I die and the family don't get it?"
This is a nomination conversation (指定受益人), which is Lesson 10, and it is worth having before the policy is written rather than at claim time. The agent should establish the nominated beneficiary, the trustee/guardian question for minor children, the "nominate a person to act for the beneficiaries" field, and whether the beneficiary is aware. A policy with no nomination and no guardian clause is a common source of family dispute that the insurance was supposed to prevent.
export const objectionPlaybook: Record<string, {
trigger: RegExp;
evidenceRef: string; // the POS attaches the published document
agentScript: string;
neverSay: string[];
}> = {
CHEAPER_ELSEWHERE: {
trigger: /平啲|便宜啲|貴咗|competitor|cheaper/i,
evidenceRef: "IA_BPG_referral_disclosure; carrier_shelf_matrix",
agentScript:
"I am not going to tell you I am the cheapest — I would be lying, because I do not carry " +
"every product. Here is what I do carry, here is the sheet, and if you want me to ask " +
"whether your existing plan is worth keeping, I will do that before we decide anything new.",
neverSay: ["nobody can beat me", "my price is guaranteed market-wide"],
},
NEED_TO_THINK: {
trigger: /諗下|考慮下|think about|not sure/i,
evidenceRef: "needs_analysis_recap",
agentScript:
"Of course — that is the right thing to do. So that my follow-up is useful rather than a " +
"nagging call, can I ask: is it the premium, the coverage, the company, or whether to do " +
"this at all? Those four need four different conversations.",
neverSay: ["the price will go up tomorrow", "this offer expires tonight"],
},
CHEAPER_CARRIER: {
trigger: /per million|per million|每月平|平啲/i,
evidenceRef: "bid_sheet_page1 + normalisation_trace R-MED-DED-02",
agentScript:
"Yes, on the headline number they are cheaper, and the reason is on page 2 — they use an " +
"annual limit instead of a deductible. That is a real trade, not a trick. Let me show you " +
"what happens to your family in the worst realistic year under each one.",
neverSay: ["they are not really cheaper", "that is misleading advertising"],
},
EMPLOYER_COVER: {
trigger: /公司|僱主|employer|group medical|醫療計劃/i,
evidenceRef: "employer_benefit_capture",
agentScript:
"Let's check three things on your company plan: the annual limit, whether dependants get " +
"the pre-existing exclusion waived, and what happens to you when you leave. Two of those " +
"three usually change the answer.",
neverSay: ["your company cover is no good"],
},
DATA_SAFETY: {
trigger: /私隱|個人資料|privacy|data|安全/i,
evidenceRef: "PDPO_notice; carrier_privacy_summary; retention_schedule",
agentScript:
"Here is exactly what leaves this room and where it goes, who at the carrier can open it, " +
"how long it is kept and how to ask for a copy or a correction. I will give you the sheet " +
"rather than tell you it is fine.",
neverSay: ["it is all encrypted so it is fine", "nobody sees it"],
},
BENEFICIARY_UNCERTAIN: {
trigger: /受益人|beneficiary|如果我死|named/i,
evidenceRef: "nomination_guidance (Lesson 10)",
agentScript:
"Let's settle that now rather than at claim time. Who should receive it, who looks after it " +
"if they are minors, and do they know? If we skip this the family can end up fighting while " +
"they are grieving.",
neverSay: ["it goes to the family automatically", "the company sorts it out"],
},
};
Key Takeaways
- Multi-carrier is a risk-control decision before it is a pricing decision. Depth on a protection shelf, depth on a savings shelf, one accident/ILAS shelf, and two or three narrow opportunistic agreements is the working configuration. An agreement you have never submitted is a liability.
- Raw quotes are not comparable, and showing them raw is a misrepresentation risk. The canonical comparable is premium per HK$1,000,000 of normalised guaranteed sum insured, plus a stated-horizon premium total, with non-equivalent dimensions carried separately and explicitly flagged.
- Guaranteed and non-guaranteed benefits never share a table. Illustrative bonus figures go in a separate appendix, with the assumed return stated, the words "not a projection" written out, and the guarantee that they may be reduced or not paid at all.
R-GT-01andR-GT-02exist for exactly this. - "Not equivalent" is a finding, not a failure. Annual-limit versus annual-deductible medical, different hospital-cash triggers, different room caps, different occupation class numbering, different survival periods — mark them
NOT_EQUIVALENT, exclude the dimension from scoring, and let the client's financial profile make the decision. - The normalisation trace is the deliverable. Every adjustment carries a rule id, a reason and an authorising underwriter id. When a client asks why two numbers differ, the answer is a rule lookup, never a recollection.
- A bid sheet must attempt at least three carriers and must show the declines.
R-CMP-01blocks generation below three; the refusal list is often the most persuasive page in the document because it proves the work was done. - State the price exactly. First-year premium, the full subsequent-year schedule (not "about HK$700 thereafter"), and the horizon total with the reason for the horizon written on the sheet.
- Switching cost is dominated by surrender value shortfall, and the agent should compute the break-even live. If the horizon is shorter than the break-even, the correct advice is "do not switch", and giving that advice is worth more than the sale it loses.
- Replacement cases are procedural, not persuasive. Check NS status before presenting, obtain the incumbent agent's written consent, register the referral, agree the commission share, and disclose the replacement. No consent, no switch — regardless of how good the reason sounds.