GUIDES
Events
Subscribe to these events and you learn when a document, a line, a request, or a verdict changes. Each one reaches you as a signed webhook POST, as a row in a scheduled batch file, or both, configured with PUT /delivery. You can subscribe to every name below. Nineteen fire today. Seven are defined and not emitted yet: document.rejected, coverage_line.expiring, coverage_line.reverification_due, coverage_line.expired, remediation.resolved, program.changed, and outreach.identity_required.
Documents
Every document event carries document_id, insured_ref, consumer_ref, and engagement_id in data, null where the push did not set them.
| Event | When it fires | Key fields | What to do |
|---|---|---|---|
document.received |
A file is stored under your key, deduplicated or not. A source_document pushed to requirement-sets fires it too |
transmission_id, filename, sha256, size_bytes, status: received, deduplicated |
Mark the file received against your record |
document.extracted |
The document's coverage lines are built | doc_type, status, coverage_lines[] (coverage line objects, each with a request_id), unreadable_fields[] |
Store the lines and their request ids. request_id is null for a line with no open request yet |
document.not_insurance |
The file is read and is not an insurance document | doc_type |
Ask the insured for the right file |
document.unreadable_fields |
Extraction reads the file but cannot make out some fields after a second read | unreadable_fields[], extraction_id |
Ask for a legible copy. Rules that need those fields read unverified |
document.failed |
The stored file cannot be read as sent: a damaged photo or scan that does not convert. A type the API does not accept returns 415 on POST /documents and fires nothing |
reason, remediation |
Ask for a PDF or a JPEG. remediation says what to ask for |
document.rejected |
Not emitted yet | Nothing arrives today |
Results
| Event | When it fires | Key fields | What to do |
|---|---|---|---|
verdict.issued |
A surface check or a verdict is issued | The verdict object: verdict_id, kind, scope, status, rules[], wording, supersedes, engagement_id, requirement_set |
Read kind first. kind: surface is the check on paper. kind: verified with scope: engagement is the insured's verdict, and scope: line is one line resolving |
verdict.superseded |
A kind: verified verdict replaces an earlier one |
verdict_id, superseded_by, kind, scope, status |
Mark the old one replaced and read the new one. A new surface check sends only verdict.issued, with supersedes set |
Coverage lines
The coverage line object carries coverage_line_id, coverage_type, carrier, policy_number, effective_date, expiration_date, limits, additional_insured, subrogation_waived, extends_to_children, document_class, verification (state, confirmed_by, confirmed_at, method), flags, and request.
| Event | When it fires | Key fields | What to do |
|---|---|---|---|
coverage_line.changed |
A line is created, a document merges into it, its verification resolves, or another policy of its type supersedes it | Coverage line object, change (created, document_merged, resolved, or superseded), previous_state, and previous_document_class on a document change |
Update the line on your side |
coverage_line.expiring |
Not emitted yet | Nothing arrives today | |
coverage_line.reverification_due |
Not emitted yet | Nothing arrives today | |
coverage_line.expired |
Not emitted yet | Nothing arrives today |
Requests
request.sla_at_risk and request.overdue carry the request object: request_id, coverage_line_id, opened_at, sla_target_at, sla_hard_at, status, extensions[], total_extension_hours, final_verdict_id, and engagement_ids[].
| Event | When it fires | Key fields | What to do |
|---|---|---|---|
request.sla_at_risk |
An open request reaches sla_target_at with no final verdict. Its status becomes verdict_due |
Request object | Surface it to your operations team |
request.overdue |
A verdict_due request passes the hard stop it opened with. Its status becomes overdue. Extensions do not delay this event today |
Request object | Expect the line to resolve timed_out once the hard stop plus any extensions passes |
request.extended |
Hours are added with POST /requests/{request_id}/extend |
request_id, hours, reason, granted_at, by, total_extension_hours, hard_stop_at |
Push out any deadline you show internally |
Evidence and contradictions
| Event | When it fires | Key fields | What to do |
|---|---|---|---|
contradiction.detected |
A newly merged document states a different value than the line already holds, once per attribute | coverage_line_id, attribute, values[] (held, new), sources[] (document:<id> entries) |
Escalate. The line carries the contradiction_open flag |
evidence.attached |
A line's verification resolves with proof attached | coverage_line_id, evidence_id, kind, verification_state |
Store the id if you show proof to anyone |
Requirement sets
| Event | When it fires | Key fields | What to do |
|---|---|---|---|
requirement_set.published |
A version is published. It is queued in both live and test mode | consumer_ref, requirement_set, version, applies_to, grace_period_days, effective_on, impact |
Record the version in force. impact holds the same counts the preview returned |
Remediation
| Event | When it fires | Key fields | What to do |
|---|---|---|---|
remediation.opened |
A reviewer marks a page not usable: a page with no policy number and no carrier that matches no line, or a document Insurance Verified could not read | remediation_id, ask: complete_declarations_packet, reason (policy_number_not_on_pages or page_not_usable), document_id, coverage_type, insured_ref, consumer_ref, engagement_id |
Ask the insured for the complete declarations packet. GET /engagements/{engagement_id} lists the item in outstanding[] until a document with the policy number arrives |
remediation.resolved |
Not emitted yet | Nothing arrives today |
Programs
| Event | When it fires | Key fields | What to do |
|---|---|---|---|
enrollment.changed |
PUT /insureds/{insured_ref}/enrollments/{program_ref} changes an enrollment. A call that changes nothing fires nothing |
The enrollment object, plus previous (enrolled, effective_on, ends_on before the change, null on the first enrollment) |
Update the insured's program status on your side. Insurance Verified re-evaluates the insured's active engagements |
program.changed |
Not emitted yet | Nothing arrives today |
Outreach
| Event | When it fires | Key fields | What to do |
|---|---|---|---|
outreach.identity_required |
Not emitted yet | Nothing arrives today | |
insured_contact.missing |
Insurance Verified has no phone or email for the insured, and the engagement's outreach has a step to the insured. The insured's steps are skipped | insured_ref, consumer_ref, engagement_id, reason: no_insured_contact, sentence |
Send the contact with PUT /insureds/{insured_ref} (contact.email, contact.phone). GET /engagements/{engagement_id} lists kind: insured_contact_missing in outstanding[] until a contact is on file |
Delivery
| Event | When it fires | Key fields | What to do |
|---|---|---|---|
delivery.batch_produced |
A batch export is written | The batch: batch_id, period_start, period_end, status, trigger, produced_at, files[] |
Ingest the batch. GET /exports/{batch_id} returns signed URLs |
delivery.batch_failed |
A batch export still fails 24 hours after it was first attempted | The batch plus error |
Re-run the period with POST /delivery/batch/run |
POST /delivery/webhook/test sends one more event, ping, with data.message. It belongs to no object and exists to prove your endpoint and your signature check.
Handling events
Every delivery has the same envelope: event_id, event, occurred_at, data, and test, plus insured_ref, consumer_ref, and document_id when data carries them. The fields above live in data. test is true for test-mode events.
Check the signature before you parse anything. Recompute the HMAC-SHA256 over <timestamp>.<body> with your signing secret, compare it to X-IV-Signature, and reject a delivery whose X-IV-Timestamp is more than five minutes from your clock.
Then dedupe. Retries run for 24 hours, so store every event_id you have handled and drop repeats. A retried event can land after newer ones, so a verdict.superseded can arrive before the verdict.issued it names. Write handlers that work in any order.
When in doubt,
GETthe object.GET /verdicts/{verdict_id}andGET /insureds/{insured_ref}/coveragereturn the current state, and an event that disagrees with one of them is stale.
New event names can appear within v1. Ignore the ones you do not know.