Skip to main content

Server integration

Event reference

Every event, its payload and when it fires.

Envelope

Every Finn webhook delivery uses the same outer shape. Only data varies by event type.

{
  "id": "evt_2H4abc",
  "type": "call.completed",
  "created_at": "2026-05-22T14:32:14.892Z",
  "org_id": "org_8fac17c5",
  "data": { }
}
FieldTypeNotes
idstringUnique per event. Use it as your dedupe key. Retries reuse the same id.
typestringOne of the events below. Match on this before touching data.
created_atISO-8601When Finn generated the event, not when it was delivered.
org_idstringYour organization.
dataobjectEvent-specific body.

Delivery order is best-effort. A call.completed can land before its call.started if the first delivery attempt failed and was retried. Sort by created_at and write handlers that tolerate arriving out of order. See webhook-retries for the retry schedule and webhook-security for signature verification.

Event catalog

EventFires when
call.startedThe carrier reports the call picked up.
call.completedThe call ended, for any reason, including missed, voicemail, busy, and failed. Delivered within 5 seconds of the call ending.
call.analyzedPost-call analysis finished extracting your configured fields. Separate from call.completed because extraction runs after the call record is closed.
call.transferredA warm transfer to a human was executed.
deployment.completedA campaign finished its audience.
wallet.low_balanceYour wallet dropped below the threshold configured in the dashboard.

You subscribe to events per webhook endpoint. A subscription with "events": ["call.completed"] receives nothing else, including call.analyzed. Subscribe to both if you need the extracted fields.

call.completed

This is the event most integrations are built on. Full payload example and the surrounding handler code live in webhooks. The data fields:

FieldTypeNotes
call_idstringGlobally unique. Stable across call.started, call.completed, call.analyzed.
deployment_idstring / nullSource campaign. Null for one-off test calls.
finn_idstringThe agent that handled the call.
phone_number_idstringThe Finn number involved.
from / toE.164Pre-translation. WhatsApp calls carry wa_id here instead.
call_typeenuminbound | outbound
channelenumpstn | whatsapp | sip
started_atISO-8601When Finn began dialing or received the call.
answered_atISO-8601 / nullNull when the call never picked up.
ended_atISO-8601
duration_secondsintBillable duration, answered_at to ended_at.
ring_secondsint
outcome.labelstringYour outcome taxonomy, defined in the prompt.
outcome.confidencefloat 0–1Model confidence in the label.
outcome.extracted_fieldsobjectFree-form, shaped by your prompt design.
sentimentenumpositive | neutral | negative
csat_scorenumber / null
call_statusenumcompleted | no_answer | busy | failed | voicemail
hangup_partyenumcaller | agent | system
recording_urlstringExpires after 30 days by default. Mirror to your own storage for archive.
recording_duration_secondsint
transcript_urlstringJSON transcript.
credits_chargedfloatWallet debit for this call.
currencystring
monetary_valuefloat
audience_idstring / null
audience_rowobject / nullThe full CSV row dialed. Null for inbound calls.
metadataobjectWhatever your inbound webhook returned in metadata, echoed back.

What breaks

SymptomCause
outcome.label is missing or genericThe agent prompt does not define an outcome taxonomy. Nothing to classify against.
audience_row is null on calls you expected to have itThe call was inbound. Branch on call_type before reading it.
duration_seconds is 0 but you were billed a connectionThe call rang and was never answered. Check call_status before treating duration as a business signal.
Extracted fields absent from call.completedAnalysis had not finished when the event fired. They arrive on call.analyzed.
Fetching recording_url returns 403The 30 day window elapsed.
Handler runs twice on the same callYou are deduping on call_id rather than id, or not deduping at all.

Payloads not documented here

The data shape for call.started, call.transferred, deployment.completed, and wallet.low_balance is not yet settled in this reference. Do not infer them from call.completed. To see a real payload for any of them, subscribe an endpoint and read the delivery in Settings → Integrations → Webhooks → Logs in the dashboard, which shows the full request body Finn sent. There is no API endpoint that returns example payloads.

Related: webhooks, webhook-security, webhook-retries, post-call-analysis, call-outcomes, api-webhooks.