GUIDES
Implementation guide
Build the full integration in eight steps. You do the first four once per account or per customer. After that your system pushes documents and reacts to events. If the words consumer, engagement, or surface check are new, read How it works first.
-
Map your ids to ours
Decide which of your records is the insured and which is the consumer before you write code. Use ids your system already owns and does not reuse.
Yours Ours Rule Whoever's rules apply: your customer, or you consumer_refOne per customer whose rules differ. Use your own name when the rules are yours The vendor, contractor, or company you onboard insured_refStable for the life of the record. Do not use an email address A parent company the insured works under parent_insured_refLets a parent's line satisfy a child's rule Your own document id or filename document_refOptional, echoed on every event Store our ids next to your rows: the document
idfrom the push receipt, pluscoverage_line_id,request_id, andverdict_idas they appear. You need them to read state and to answer support questions. -
Create the consumer
PUT /consumers/{consumer_ref}with a name, contacts, and any metadata you want echoed back. It is an upsert. Call it from your customer provisioning flow.A consumer has no effect on results until a requirement set exists for it.
-
Publish the requirement set
Turn the customer's insurance SOP into rules with
POST /consumers/{consumer_ref}/requirement-sets. A consumer usually carries one requirement set (key: default). Add a differentkeyonly when some insureds follow different rules from the rest. Each rule names a coverage type, the requirement, the class of document that is acceptable, and who must confirm it. Publish a first version withapplies_to: new. For a later change, callpreview, then publishall_activewith agrace_period_dayswindow, 14 by default. Publishingall_activerebinds only the engagements following that key.Send structured rules when you can. A PDF or DOCX
source_documentis parsed into a draft for you to review. -
Point delivery at your endpoint
PUT /deliverywith a webhookurland theeventsyou want, or["*"]. The response returns the signingsecretonce. Store it. Add abatchblock as well if a scheduled file suits your data team better than a stream.Each key's mode has its own delivery configuration. Configure it once with the test key and once with the live key.
-
Push documents
Wherever a certificate enters your system, call
POST /documentswith the file,insured_ref, andconsumer_ref. The file field isfiles, repeated once per file. Push every version you receive. Insurance Verified deduplicates by hash and merges lines. You get201 Createdback with{transmission_id, idempotent_replay, engagement_id, engagement, documents[]}, and each document entry carriesid,sha256, andcontent_bytes_deduplicated. Store theidand theengagement_id.Send an
Idempotency-Keyheader on every POST and PUT, so a retry creates no second transmission. One push call is onetransmission_id, and a replay returns that transmission.Send the file as you received it. Insurance Verified extracts and classifies it, and
metahas no effect on what it reads. -
Handle events
Verify the
X-IV-Signatureover<timestamp>.<body>, reject a delivery whose timestamp is more than five minutes old, then dispatch onX-IV-Event. The default subscription isdocument.*andverdict.*. Subscribe to the other families you handle.Event What to do in your system document.extractedRecord the coverage lines against the insured. A line's request_idisnulluntil its clock opensdocument.failed,document.not_insurance,document.unreadable_fieldsAsk the insured for a readable insurance document. reasonorunreadable_fields[]says what went wrongverdict.issuedStore it. kind: surfaceis the surface check,kind: verifiedis a verdict.scope: lineis one coverage line resolving.scope: engagementis the roll-up, which sets the insured's status. On a failure, show the insuredwording.summaryandwording.next, andrules[]when they want detailverdict.supersededMark the old verdict replaced and read the new one remediation.openedAsk the insured for the complete declarations packet named in askinsured_contact.missingSend the insured's email or phone with PUT /insureds/{insured_ref}request.sla_at_risk,request.overdueSurface it to your operations team, or add hours with POST /requests/{request_id}/extendStore every
event_idand drop duplicates. Deliveries retry for 24 hours and can arrive out of order. -
Read state when a screen needs it
Events tell you when something changed. When a screen or a report needs the answer now, read it. When an event and a read disagree, use the read.
Read What it gives you GET /engagements/{engagement_id}The whole verification in one object: the requirement set it follows, the surface check, the verdict, per-line verdicts, coverage lines, documents, and the event trail GET /insureds/{insured_ref}/verdictsThe verdict history against one consumer, {data, next_cursor}, newest first.consumer_refis a required query parameter.engagement_idnarrows to one engagement when the insured holds several with the consumerGET /verdicts/{verdict_id}One verdict, as a bare object with no datawrapperGET /insureds/{insured_ref}/requestsThe clocks on one engagement, newest first. This read gives you a request_idwith no webhook.consumer_refis requiredGET /requests/{request_id}The clock on one line, with its attempts and extensions GET /consumers/{consumer_ref}/engagementsEvery engagement under a consumer, newest first, filterable by requirement set, status, and insured GET /documents/{document_id}One document's status, what was extracted from it, and the engagement it fed GET /insureds/{insured_ref}/coverageEvery line on the insured and its verification state, including lines available from a parent A test-key engagement resolves in about a minute, so you can poll
GET /engagements/{engagement_id}end to end. -
Test, then go live
Run the whole flow on a test key. Outreach is simulated, verdicts carry
test: true, and the lifecycle resolves in about a minute.POST /delivery/webhook/testsends a signedpingto your endpoint. Swap in the live key when your handlers pass.Test keys are prefixed
ive_test_and live keysive_live_. You can hold both at once. A live key reads no test data, and a test key reads no live data.
The consumer holds no key and makes no calls. To show your customer its verdicts, build a screen from your copy of the events, or send it the batch file.
What to keep on your side
| Keep | Why |
|---|---|
The webhook secret and the key |
Each is shown once |
Every event_id you have processed |
Deduplication |
The transmission_id and document id from every push receipt |
Tying your file to ours in a support question |
The engagement_id per insured, consumer, and requirement set |
The id to poll GET /engagements/{engagement_id} with |
The latest engagement-scope verdict_id per insured and consumer, with its status and kind |
Your status screen |
coverage_line_id per line Insurance Verified found |
Support and audit questions |
request_id per open line |
Answering "where is it" without a read |
Reading rules[]
The field that carries the outcome depends on the verdict's kind. Here is the same requirement at both stages.
On kind: surface, the paper reading is in surface. result is unverified because the broker has not answered yet.
{ "rule_key": "general_liability.limit_min.each_occurrence",
"surface": "pass", "result": "unverified", "severity": "hard",
"reasons": ["verification_pending"], "flags": [],
"required": {"amount": 1000000}, "observed": {"amount": 1000000},
"verification_required": "broker", "verification_observed": null }
On kind: verified, the rule carries no surface field, and result carries the outcome.
{ "rule_key": "general_liability.limit_min.each_occurrence",
"result": "pass", "severity": "hard",
"verification_state": "verified", "reasons": [], "flags": [],
"required": {"amount": 1000000}, "observed": {"amount": 1000000},
"verification_required": "broker", "verification_observed": "broker" }
Branch on rules[].surface at stage one and on rules[].result at stage two. status is the aggregate at both. A client that branches on result at stage one reads a passing surface check as a non-pass.
The full rule object:
| Field | What it is |
|---|---|
rule_key |
<coverage_type>.<requirement>.<label>, for example general_liability.limit_min.each_occurrence. An any_of rule carries the key you chose |
result |
pass, fail, gap, unverified, not_applicable. Branch on it when kind is verified |
surface |
pass, fail, not_yet, pending_review, not_applicable. Present when kind is surface. Branch on it there |
stage |
surface, open, verified. Present on the engagement roll-up only, saying how far this requirement got |
verification_state |
The line's outcome at the source. Present on a kind: verified verdict |
severity |
hard blocks the engagement, soft warns |
required, observed |
The value the rule asked for and the value found |
reasons[] |
Why it reads that way: verification_pending, limit_below_minimum, document_class_below_required, policy_form_unconfirmed, contradicted_at_source, verification_timed_out, and others. The list grows |
flags[] |
Something a person may need to act on: timed_out, refused, contradicted, unable_to_verify, state_review_required |
document_class_required, document_class_observed |
The paper the rule accepts and the paper received |
verification_required, verification_observed |
Who had to confirm it and who did |
coverage_line_id |
The line that decided the rule, or null when nothing was found |
satisfied_by |
The alt_key of the any_of alternative that decided it |
satisfied_via_parent, satisfied_via_program |
Set when a parent's line or an enrolled program decided the rule |
alternatives[] |
Per-alternative results on an any_of rule |
document_ids[], fact_ids[] |
What the reading was built from |
Ignore reason codes and flags you do not know. New ones ship without a version bump.
Common mistakes
| Mistake | What to do |
|---|---|
Branching on rules[].result on a surface check |
Read rules[].surface. On a surface check result is usually unverified with reason verification_pending |
| Showing the surface check as the verdict | Show it as pending confirmation until a kind: verified verdict arrives |
Branching on wording |
Branch on status and rules[]. wording changes without a version bump |
| Sending one document per insured | Push every certificate, renewal, and endorsement you receive. Insurance Verified merges them into lines |
Publishing a changed requirement set all_active without preview |
Call preview first. It lists who newly fails |
Expecting a request_id on the push receipt |
Read it from document.extracted or GET /insureds/{insured_ref}/requests once the clock opens |
Expecting 202 and an array from POST /documents |
The response is 201 with one object |
Expecting 400 on a bad field |
Validation failures are 422, in the standard error envelope, with the field named in error.message |
Reusing an insured_ref for a different party |
Mint a new ref. A ref stays bound to the party it first named |
Go-live checklist
| Check | Done when |
|---|---|
| Consumer exists | PUT /consumers/{consumer_ref} returns 200 |
| Requirement set published | GET /consumers/{consumer_ref}/requirement-sets shows a published version |
| Webhook verified | POST /delivery/webhook/test lands and your signature check passes |
| Handlers idempotent | Replaying a stored event changes nothing |
| Surface check and verdict both handled | Your status screen distinguishes kind |
| Own domain verified, if you send from one | Settings, then Sending, shows the domain as Verified |
| Live key swapped in | The first verdict.issued arrives with test: false |