Skip to content

Webhooks

Webhooks push results to you as they happen, so you don’t have to poll. Midwater sends a signed POST with a JSON body to your HTTPS endpoint.

  1. In the app, open Routes & destinations and add a webhook destination with your endpoint’s URL. The URL must be publicly reachable; private, loopback and link-local addresses are refused.
  2. A webhook destination receives conversation.evaluated, agent.health_changed and group.health_changed, plus check.failed from any route that sends to it. It gets live conversations only, unless you tick Also send events from test calls.
  3. Copy the environment’s signing secret (whsec_…) from the Developers page and verify every delivery.
  4. Press Send test event to receive a test event.
Event Sent when
conversation.evaluated A conversation finished scoring. Carries the outcome and every result.
check.failed An active check failed on a conversation, and a route sends that check (or that severity) to this destination.
check.failed.digest Instead of check.failed, when the route delivers as an hourly digest.
agent.health_changed An agent’s health changed, with watch or at_risk as the old or new status.
group.health_changed The same, for a group.
test Someone pressed Send test event.

check.failed comes only from routes: a route says which checks or severities, for which agents or groups, go to which destination. Shadow checks never trigger it.

Every event has the same envelope:

{
"id": "evt_a1b2c3d4e5",
"type": "conversation.evaluated",
"environment": "live",
"created_at": "2026-10-08T21:12:00.000Z",
"data": {
"conversation_id": "cmv0187nm005po3016rag4dcg",
"external_id": "call_8f2a91",
"agent": { "id": "front-desk", "name": "Front desk", "health_status": "healthy" },
"group": { "id": "brightsmile-dental", "name": "BrightSmile Dental" },
"outcome": "resolved",
"results": [
{ "check_key": "need_unresolved", "verdict": "pass", "score": 0.04, "decided_by": "model", "shadow": false }
]
}
}

environment is test or live. data.agent is on every conversation and health event, with data.group when the agent has one. The full schema for each event is in the API reference.

A check.failed event:

{
"id": "evt_9f8e7d6c5b",
"type": "check.failed",
"environment": "live",
"created_at": "2026-10-08T21:12:01.000Z",
"data": {
"conversation": { "id": "cmv0187nm005po3016rag4dcg", "external_id": "call_8f2a91", "channel": "voice", "agent_version": "3.2.0", "ended_at": "2026-10-08T21:11:40.000Z" },
"agent": { "id": "front-desk", "name": "Front desk", "health_status": "watch" },
"check": { "key": "false_booking_confirmation", "name": "Agent confirmed a booking with no successful booking event", "severity": "high", "version": 1 },
"result": { "score": 1, "verdict": "fail", "decided_by": "rule", "reason": "Agent confirmed a booking, but no successful booking event exists." },
"url": "https://app.example/conversations/cmv0187nm005po3016rag4dcg"
}
}

Health events carry from, to, both windows (last_7_days, last_30_days, as on the health endpoints) and a url. group.health_changed also names the agent whose status set the group’s.

Header
Midwater-Signature t=<unix seconds>,v1=<hex>. Verify it.
Midwater-Event The event type, the same as type in the body.
Midwater-Delivery The delivery ID. The same on every retry of one delivery: use it to ignore duplicates.
User-Agent Midwater-Webhooks/1.0
Content-Type application/json
  • Answer with any 2xx within 5 seconds. Do slow work after you answer, for example on a queue.
  • Today, anything else, or no answer in time, is retried 3 times, about 2, 8 and 30 seconds apart. After that the delivery is marked failed.
  • Planned Retries will move to 8 attempts over about 24 hours, so an endpoint that’s down for a few hours still gets its events.
  • Every attempt, with its status and timing, is in the destination’s delivery log in the app.
  • Deliveries can arrive more than once and out of order. Deduplicate on Midwater-Delivery, which is the same on every retry of a delivery, and use created_at and the conversation’s own status rather than arrival order.