Every field of a product definition, every check it must pass, how a request runs through it, and the API that authors, executes, explains and replays products.
The BimaStack team · · 9 min read
Every field of a product definition, every check it must pass, how a request runs through it, and the API that authors, executes, explains and replays products. The guide builds one end to end.
Identity and versions
A definition is a PRODUCT_DEFINITION setting identified by organisation, optional binder, and product code, with the key definition. Versions, effective dates, two-person approval and audit are the settings machinery’s; a binder’s definition overrides the organisation’s for that binder. It is published dated: a future start leaves today’s version live until then.
The document is read strictly: an unknown property is a DEFINITION_UNPARSEABLE finding, never dropped. carrier is required on a broker’s or agency’s product and refused on an insurer’s; source only comes with a carrier.
inputs
Field
Meaning
name, type
type is STRING, NUMBER, BOOLEAN, DATE, ENUM, LIST or RECORD
allowedValues
For an ENUM
elementType, record
For a LIST, and for a RECORD or LIST of RECORD (records are declared under records, scalar fields only)
{amount} or {percent, of: SUM_INSURED | CLAIM, minimum}
waitingPeriod
{days}
requires, excludes
Codes that must, or must not, be on together
enabledWhen
A BOOLEAN input that switches it on
conditions
Text shown on the quote, not evaluated
Resolution is deterministic: mandatory, default and requested items are on; a child of an item that is off is off; a child explicitly requested under a parent that isn’t chosen is a COVERAGE_CONFLICT, never silently dropped. Stages receive the covers that are on as coverages, a list of records with code, type, parent, limitAmount, deductibleAmount, deductiblePercent, excessAmount, excessPercent and waitingDays (absent amounts 0, no parent "").
stages
A stage served by a workflow written with its own names, unedited
Any output of an earlier stage, e.g. underwriting.loadingPercent
A stage takes only what it declares. A workflow record may ask for a subset of a product record’s fields; a field the workflow wants that the product lacks is a stage failure, never a default. A product DATE fills a DATE or STRING; an ENUM fills an ENUM or STRING. Stages run in the order validation, eligibility, underwriting, pricing; any may be absent.
The pricing contract
Output
Type
pricing.total
NUMBER
Required: the amount payable, tax and fees included
pricing.netPremium
NUMBER
Optional: before tax and fees
pricing.taxTotal, pricing.feeTotal
NUMBER
Optional
pricing.linePremiums
LIST<NUMBER>
Optional: one per cover that is on, in order
The quote rounds these to two places, half to even. It doesn’t check that lines add up to the total, so round before combining in the workflow when they must.
rules
{code, stage, effect (ERROR | DECLINE | REFER), message, firedBy, coverage?}. firedBy names a BOOLEAN output of that stage’s workflow, after its outputMapping; a missing or non-BOOLEAN flag is a stage failure, never “not fired”. Pricing-stage rules are assessed after pricing and can move an ACCEPT to REFER or DECLINE; whether pricing runs is decided by the earlier stages.
quoteClass
Part
Meaning
code
The standard form answered, e.g. TRAVEL, MOTOR_PRIVATE, MEDICAL
inputs.<name>
Exactly one source: from (a question, or list.field), value (a constant), or addOn (true when chosen)
Translates answers; an answer with no entry leaves the product out of that comparison
fields
Fills a record, or a list of records, item by item
refineFrom
A question asked once a plan is chosen that gives a better value; the product is priced again with it at review
accepts
The only answers it prices, per question
addOns, coverageLimits, highlights
Form add-on → product cover; a limit from an amount question; the cover each price-card line reads
Checks
A finding is {rule, path, message, tier}. STRUCTURAL findings refuse the draft. REFERENCE findings are reported by validate and block publish, since a workflow may be written after the definition.
A stage that can’t do its job (its workflow not in effect, an input it can’t be given, a value of the wrong type) fails closed with its stage and workflow named. An eligibility check that couldn’t run must never read as “eligible”.
Executing: API version 2
Send the header API-Version: 2 with every product call. A version the API doesn’t serve is 400 API_VERSION_UNSUPPORTED. Within a version, changes are only additive: a field may be added, never renamed or removed; a breaking change is a new version served beside it.
Call
Does
POST /api/products/{code}/execute
Run the published product; return the quote; record it
POST /api/products/{code}/explain
As execute, with the per-stage trace; recorded
POST /api/products/{code}/simulate
As execute against an inline draft definition; not recorded
GET /api/products/{code}/schema
A JSON Schema of the inputs, the cover catalogue, every reason code and the stages
GET /api/product-computations
Recorded computations, newest first, by product, outcome and time
GET /api/product-computations/{requestId}
One: the request, the quote, the definition version
Numbers are exact decimals as sent; nothing is coerced, so "10" for a NUMBER is INPUT_TYPE_INVALID.
Every field is always present, null when it doesn’t apply; premium is null for a DECLINE.
rateVersionsUsed lists everything versioned the quote read: the definition, each stage workflow, every table, setting, formula and block link.
Errors are RFC 7807 problems with a stable code, the requestId, and for a bad request errors[] of {code, fieldPath, message}. A rejected request isn’t recorded.
Recorded and replayable
Every execute and explain writes one insert-only row: the request, the quote and the definition version. Its id is the requestId. Replay runs the request again at its own date against the same definition version, and says what moved:
Replay
{ "identical": false,
"differences": [ "premium",
"rateVersionsUsed: RATE_TABLE/travel-outbound-base-premium@v1 is no longer used",
"rateVersionsUsed: RATE_TABLE/travel-outbound-base-premium@v2 is now used" ] }
The usual cause is a table or workflow republished with a start date on or before the quote’s date. A record also says who sold the product and on what basis: their own, a distribution agreement, through a broker, on a storefront or as platform content, and who carries the risk.
Authoring API
Call
Does
GET /api/product-definitions?productCode=&organizationId=&binderId=&status=