Skip to content

Webhooks & integrations

EstimateIQ integrates with external systems — a carrier portal, a CRM, a Power Automate flow, and XactAnalysis — through a small webhook surface. External status strings normalize into the internal ball-in-court state machine, and status changes fan back out to a downstream system.

Inbound: push a status in

POST /api/webhooks/status

Authenticate with the shared token header x-webhook-token: <token> (or Authorization: Bearer <token> — both accepted; the token is the Worker’s WEBHOOK_TOKEN, falling back to REVSRC_SECRET_KEY). If no token is configured the endpoint is open (preview only).

Body — identify the estimate by one of estimateNo | claimNo | id, plus a status:

{
"estimateNo": "EST-24117-MIT",
"status": "approved",
"note": "Carrier approved as revised",
"actor": "State Farm"
}
FieldRequiredNotes
id / estimateNo / claimNoone ofHow EstimateIQ resolves the target estimate.
statusyesAny external status string — mapped to an internal status.
notenoFree text, recorded on the estimate timeline.
actornoWho made the change (e.g. the carrier).
approvedRcvnoApproved RCV amount on approved / settled.

On success EstimateIQ resolves the estimate, maps the status, recomputes the ball, appends a webhook event, fires the outbound webhook, and returns the new state. Failure modes: 401 (unauthorized), 404 (estimate not found), 422 (unmapped status).

External → internal status map

Inbound status strings match case-insensitively, then by longest-key substring, so phrases like "carrier - revision requested" still resolve. The default map (overridable per deployment via the statusMap setting):

External phrase(s)Internal status
draft, new, opendraft
submitted, sent, received, acknowledgedsubmitted
in review, desk audit, auditing, under reviewin_review
revision requested, returned, supplement, rejected, kickbackrevision_requested
responded, resubmittedresponded
approved, acceptedapproved
settled, paidsettled
closedclosed

XactAnalysis lifecycle sync

POST /api/webhooks/xactanalysis

The carrier estimating platform pushes lifecycle events; EstimateIQ creates or advances the tracker and archives the correspondence into the estimate thread + triage inbox. Same x-webhook-token auth. The event field selects the action:

eventEffect
assignmentCreates a new tracker (needs estimateNo, carrierId, insured) and logs an inbound assignment email.
revisionLogs an auditor revision (parse + rules + follow-up) and archives the note.
submittedRecords a resubmission to the carrier.
approved / settledMarks the estimate approved / settled and archives the notice.
statusGeneric status change, mapped through the status map.
{ "event": "revision", "estimateNo": "EST-24117-MIT", "note": "Need day-6 moisture reading.", "amount": 14820 }

Discovery & test

GET /api/webhooks/status # live schema + status map (no auth; safe to read)
GET /api/webhooks/xactanalysis # XactAnalysis event schema (no auth)
POST /api/webhooks/test # echoes your payload back (connection test)

The GET discovery endpoints are self-documenting — they return the auth requirement, the body schema, an example, and (for status) the current status map, so an integrator can wire up a flow without reading source.

Outbound: fire events on change

When a status change lands, EstimateIQ POSTs a payload to the URL in the outboundWebhookUrl setting (set in Settings or via the API). If no URL is configured, the send is skipped. Best-effort — it never throws into the inbound request.

{
"event": "status_changed",
"estimateNo": "EST-24117-MIT",
"claimNo": "...",
"id": "...",
"status": "Approved",
"internalStatus": "approved",
"ballInCourt": "done",
"at": "2026-06-30T00:00:00.000Z"
}

Inbound carrier-mail pipeline

Carrier mail can also arrive as a batch on the MAIL_INGEST queue. The Worker’s queue() handler:

  1. Archives each raw message to R2 before parsing.
  2. Matches the subject against the enabled subject rules to pull an estimate number.
  3. If it resolves to a known estimate, kicks the durable RevisionWorkflow (persist → parse → apply revision + rules → arm SLA alarm → notify).
  4. If not, leaves the message archived and unmatched for inbox triage.

Transient failures retry (up to max_retries, then the dead-letter queue).

Power Automate example

Add an HTTP action to your flow:

  • Method: POST · URI: https://esb.guru/api/webhooks/status
  • Headers: x-webhook-token: @{variables(‘EstimateIQToken’)} · Content-Type: application/json
  • Body:
{
"estimateNo": "@{triggerBody()?['estimateNumber']}",
"status": "@{triggerBody()?['carrierStatus']}",
"note": "@{triggerBody()?['statusNote']}",
"actor": "@{triggerBody()?['carrierName']}"
}

Store the token in a secure variable or the flow’s connection reference — never inline it.