> ## Documentation Index
> Fetch the complete documentation index at: https://docs.callers.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook payload

> Field-by-field reference for the JSON Callers sends with call webhooks: outcome, transcript, summary, success criteria, extracted variables, and more.

Simple webhooks send the complete call payload as JSON in the body of a `POST` request (`Content-Type: application/json`, signed in the `X-Signature` header). Advanced webhooks use the same data as the source for their templates. This page documents every field.

You can fetch the same payload for any past call with `GET /agents/{agentId}/calls/{callId}/webhook-payload`.

> [!NOTE]
>
> New fields may be added at any time. Ignore fields you don't recognize instead of rejecting the request. Fields with no value may be `null` or omitted.

## Example

```json webhook.json theme={"dark"}
{
  "id": "6650c4d0a1b2c3d4e5f60901",
  "sessionId": "c2f1e0b4-7a51-4d0e-9a8f-2b6f1d3e4a5c",
  "webhookEvent": "POST_PROCESSING_FINISHED:INTERESTED",
  "createdAt": "2026-10-20T14:56:41.642Z",
  "isIncoming": false,
  "isTestCall": false,
  "campaign": { "id": "6650c1f2a1b2c3d4e5f60718", "name": "Demo booking" },
  "account": { "id": "6650a0a0a1b2c3d4e5f60001", "name": "Acme Inc." },
  "organizationId": "6650a0a0a1b2c3d4e5f60001",
  "phoneFrom": "15555551234",
  "phoneTo": "14155552671",
  "agentPhoneNumber": "15555551234",
  "contactPhoneNumber": "14155552671",
  "contact": {
    "id": "6650c3a9a1b2c3d4e5f60801",
    "name": "Dana Levi",
    "phoneNumber": "14155552671",
    "status": "INTERESTED",
    "attempts": 1,
    "data": { "company": "Acme", "crm_id": "0035e00000AbCdE" }
  },
  "status": "INTERESTED",
  "callStatus": "COMPLETED",
  "postProcessStatus": "INTERESTED",
  "hangUpReason": "CALL_FINISHED",
  "duration": 128,
  "totalDuration": 128,
  "recordingUrl": "https://…",
  "contactStatus": { "doNotCall": false, "wrongNumber": false, "callMeLater": false },
  "successCriteria": {
    "goalReached": true,
    "name": "Demo Scheduled",
    "description": "The contact agreed to a demo date and time.",
    "reason": "The contact accepted Tuesday at 3 pm for a demo.",
    "type": "INTERESTED"
  },
  "interested": {
    "description": "The contact agreed to a demo date and time.",
    "reached": true
  },
  "summary": {
    "Current tool": {
      "description": "Which tool does the contact use today?",
      "options": [],
      "order": 1,
      "type": "OPEN_QUESTION",
      "value": "Spreadsheets"
    },
    "Budget approved": {
      "description": "Has budget been approved?",
      "options": [],
      "order": 2,
      "type": "YES_NO",
      "value": true
    }
  },
  "extractedVariables": {
    "demo_time": { "value": "2026-10-27T15:00:00", "type": "string", "description": "Agreed demo time" }
  },
  "unansweredQuestions": [
    { "question": "Do you integrate with SAP?", "context": "Asked while discussing integrations." }
  ],
  "promisedActions": [
    { "commitment": "Send a calendar invite", "commitmentType": "email", "context": "Agreed at the end of the call." }
  ],
  "transcript": [
    { "role": "assistant", "content": "Hi Dana, this is Alex from Acme. Do you have a minute?" },
    { "role": "user", "content": "Sure, what's this about?" },
    { "role": "assistant", "content": "I'd love to show you a quick demo. Would Tuesday at 3 pm work?" },
    { "role": "user", "content": "Tuesday at 3 works." }
  ],
  "conversationFlow": ["Greeting", "Pitch", "Book demo", "Goodbye"],
  "events": [
    {
      "type": "MEETING",
      "data": {
        "duration": 30,
        "eventTypeId": "1234567",
        "invitationSent": true,
        "email": "dana@example.com",
        "name": "Dana Levi",
        "slot": "2026-10-27T15:00:00.000Z",
        "meetingUrl": "https://cal.com/…"
      }
    },
    {
      "type": "SMS",
      "data": {
        "sent": true,
        "status": "queued",
        "sentAt": "2026-10-20T14:58:01.000Z",
        "sentTo": "14155552671",
        "sentFrom": "15555551234",
        "text": "Thanks Dana, see you Tuesday at 3 pm!"
      }
    }
  ],
  "booking": { "meetingUrl": "https://cal.com/…" },
  "twilio": { "accountSid": "AC…", "callSessionId": "c2f1e0b4-7a51-4d0e-9a8f-2b6f1d3e4a5c" }
}
```

## Examples by event

Every delivery uses the same structure. What changes is how much of it is filled in: events that fire before post-call analysis carry the identifiers, the contact, and the live status, but no outcome yet.

**DIALING / CONNECTED**

Sent before the conversation is analyzed. `transcript`, `events`, and `conversationFlow` are empty, and outcome fields are absent or `null`. On `DIALING` the `callStatus` is `NEW`; on `CONNECTED` it's `IN_PROGRESS`.

```json theme={"dark"}
{
  "id": "6650c4d0a1b2c3d4e5f60901",
  "sessionId": "c2f1e0b4-7a51-4d0e-9a8f-2b6f1d3e4a5c",
  "webhookEvent": "CONNECTED",
  "createdAt": "2026-10-20T14:56:41.642Z",
  "isIncoming": false,
  "isTestCall": false,
  "campaign": { "id": "6650c1f2a1b2c3d4e5f60718", "name": "Demo booking" },
  "account": { "id": "6650a0a0a1b2c3d4e5f60001", "name": "Acme Inc." },
  "organizationId": "6650a0a0a1b2c3d4e5f60001",
  "phoneFrom": "15555551234",
  "phoneTo": "14155552671",
  "agentPhoneNumber": "15555551234",
  "contactPhoneNumber": "14155552671",
  "contact": {
    "id": "6650c3a9a1b2c3d4e5f60801",
    "name": "Dana Levi",
    "phoneNumber": "14155552671",
    "data": { "company": "Acme", "crm_id": "0035e00000AbCdE" }
  },
  "status": "IN_PROGRESS",
  "callStatus": "IN_PROGRESS",
  "contactStatus": {},
  "successCriteria": {},
  "interested": {},
  "summary": null,
  "unansweredQuestions": [],
  "promisedActions": [],
  "transcript": [],
  "conversationFlow": [],
  "events": []
}
```

**COULD\_NOT\_CONNECT:NO\_ANSWER**

The contact didn't pick up. There is no conversation, so there is nothing to analyze: `callStatus` is `NO_ANSWER`, the contact moves to `PENDING_RETRY` with `attempts` counting this attempt, and the agent retries according to its retry settings. When the last attempt is used up, a separate `CONTACT_UNREACHED` delivery follows.

```json theme={"dark"}
{
  "id": "6650c4d0a1b2c3d4e5f60902",
  "sessionId": "0d9b7a21-3c4e-4f5a-8b6c-7d8e9f0a1b2c",
  "webhookEvent": "COULD_NOT_CONNECT:NO_ANSWER",
  "createdAt": "2026-10-20T15:10:02.118Z",
  "isIncoming": false,
  "isTestCall": false,
  "campaign": { "id": "6650c1f2a1b2c3d4e5f60718", "name": "Demo booking" },
  "account": { "id": "6650a0a0a1b2c3d4e5f60001", "name": "Acme Inc." },
  "organizationId": "6650a0a0a1b2c3d4e5f60001",
  "phoneFrom": "15555551234",
  "phoneTo": "14155552671",
  "agentPhoneNumber": "15555551234",
  "contactPhoneNumber": "14155552671",
  "contact": {
    "id": "6650c3a9a1b2c3d4e5f60801",
    "name": "Dana Levi",
    "phoneNumber": "14155552671",
    "status": "PENDING_RETRY",
    "attempts": 1,
    "data": { "company": "Acme", "crm_id": "0035e00000AbCdE" }
  },
  "status": "NO_ANSWER",
  "callStatus": "NO_ANSWER",
  "contactStatus": {},
  "successCriteria": {},
  "interested": {},
  "summary": null,
  "unansweredQuestions": [],
  "promisedActions": [],
  "transcript": [],
  "conversationFlow": [],
  "events": []
}
```

**Text agent (POST\_PROCESSING\_FINISHED)**

Text agents (SMS, WhatsApp) send a conversation payload: `messages` instead of `transcript`, `goal` instead of `successCriteria`, and a `contactStatus` with text-specific flags. There is no `webhookEvent` field; simple webhooks carry the triggering event names in `webhookEvents`.

```json theme={"dark"}
{
  "id": "6650d1e0a1b2c3d4e5f60a01",
  "sessionId": "7e3f2a10-9b8c-4d7e-a6f5-1c2b3d4e5f60",
  "webhookEvents": ["POST_PROCESSING_FINISHED:INTERESTED"],
  "createdAt": "2026-10-21T09:12:30.004Z",
  "isIncoming": false,
  "campaign": { "id": "6650c1f2a1b2c3d4e5f60719", "name": "Renewal reminders (SMS)" },
  "account": { "id": "6650a0a0a1b2c3d4e5f60001", "name": "Acme Inc." },
  "organizationId": "6650a0a0a1b2c3d4e5f60001",
  "phoneFrom": "15555551234",
  "phoneTo": "14155552671",
  "contact": {
    "id": "6650c3a9a1b2c3d4e5f60801",
    "name": "Dana Levi",
    "phoneNumber": "14155552671",
    "data": { "plan": "Pro", "renewal_date": "2026-11-30" }
  },
  "status": "COMPLETED",
  "postProcessStatus": "INTERESTED",
  "contactStatus": { "unsubscribed": false, "wrongNumber": false, "followUpPending": false },
  "goal": {
    "reached": true,
    "name": "Renewal Confirmed",
    "description": "The contact agreed to renew.",
    "reason": "The contact replied that they want to keep the Pro plan.",
    "type": "INTERESTED"
  },
  "successCriteriaResults": [
    {
      "name": "Renewal Confirmed",
      "description": "The contact agreed to renew.",
      "isReached": true,
      "reason": "The contact replied that they want to keep the Pro plan.",
      "confidence": 0.95,
      "type": "INTERESTED",
      "achievementLevel": 1,
      "order": 0
    }
  ],
  "summary": {
    "Reason for churn risk": {
      "description": "Why might the contact not renew?",
      "options": [],
      "order": 1,
      "type": "OPEN_QUESTION",
      "value": "Price was a concern, resolved by the annual discount."
    }
  },
  "extractedVariables": {
    "preferred_billing": { "value": "annual", "type": "string", "description": "Billing cadence the contact chose" }
  },
  "unansweredQuestions": [],
  "promisedActions": [
    { "commitment": "Send the annual pricing link", "commitmentType": "sms", "context": "Agreed at the end of the chat." }
  ],
  "messages": [
    { "role": "assistant", "content": "Hi Dana, your Pro plan renews on Nov 30. Want to keep it?" },
    { "role": "user", "content": "Yes, if I can switch to annual billing." },
    { "role": "assistant", "content": "Done. I'll text you the annual pricing link." }
  ]
}
```

### Which fields are filled on which events

| Field | `DIALING`, `CONNECTED`, `COULD_NOT_CONNECT:*`, `FAILED` | `HANG_UP` | `POST_PROCESSING_FINISHED:*` | `CALL_FINISHED` |
| - | - | - | - | - |
| `transcript`, `conversationFlow`, `events` | Empty | Filled | Filled | Filled |
| `duration`, `totalDuration` | Absent | Filled | Filled | Final (includes transferred part) |
| `postProcessStatus`, `summary`, `successCriteria`, `extractedVariables`, `promisedActions`, `unansweredQuestions` | Empty | Empty (analysis hasn't run) | Filled | Filled |
| `status` | The call status | The `hangUpReason` | The outcome | The outcome |
| `recordingUrl` | Absent | May be missing | May be missing | Reliable (if recording is on) |

Need the outcome? Subscribe to a `POST_PROCESSING_FINISHED:*` event or `CALL_FINISHED`, not `HANG_UP`.

## Field reference

### Identifiers and context

**id** `string`

Call ID. Use it with the call endpoints, and with `webhookEvent` as a deduplication key.

**sessionId** `string`

Call session ID. Used by the [warm-transfer summary](https://docs.callers.ai/api-reference/guides/subaccounts-and-sip.md#play-a-warm-transfer-summary) endpoint.

**webhookEvent** `string`

The event that triggered this delivery, for example `CALL_FINISHED`. See [Webhook events](https://docs.callers.ai/documentation/webhook/webhook-events.md).

**createdAt** `string`

When the call was created (ISO 8601, UTC).

**isIncoming** `boolean`

`true` if this is an inbound agent.

**isTestCall** `boolean`

`true` for test calls placed from the dashboard (no contact attached). Filter these out in production.

**campaign** `object`

The agent that handled the call. Named `campaign` for backward compatibility.

**properties**

**id** `string`

Agent ID.

**name** `string`

Agent name.

**account** `object`

The account (or subaccount) the agent belongs to.

**properties**

**id** `string`

Account ID.

**name** `string`

Account name.

**organizationId** `string`

Account ID (same as `account.id`).

### Phone numbers and contact

**phoneFrom** `string`

The calling number.

**phoneTo** `string`

The called number.

**agentPhoneNumber** `string`

Same value as `phoneFrom`.

**contactPhoneNumber** `string`

Same value as `phoneTo`.

**contact** `object`

The contact called.

**properties**

**id** `string`

Contact ID. Empty for test calls.

**name** `string`

Contact name.

**phoneNumber** `string`

Contact's phone number.

**status** `string`

The contact's current status, such as `INTERESTED` or `PENDING_RETRY`.

**attempts** `number`

Dial attempts made to this contact so far.

**data** `object`

The contact's custom data (the variables you uploaded). For test calls, the variable values used in the test.

### Status and outcome

**status** `string`

The call's overall result in one field. It's the first that applies of:

1. The call status while the call isn't completed: `NEW`, `IN_PROGRESS`, `BUSY`, `NO_ANSWER`, `VOICE_MAIL`, `FAILED`.
2. `COMPLETED` for completed calls that didn't count as a real conversation (for example, silence or an immediate hang-up).
3. The post-call outcome once analysis is done: `INTERESTED`, `NOT_INTERESTED`, `CALL_ME_LATER`, `WRONG_NUMBER`, `DO_NOT_CALL`.
4. The hang-up reason (see `hangUpReason`) for a completed call that hasn't been analyzed yet.

**callStatus** `string`

Telephony status: `NEW`, `IN_PROGRESS`, `COMPLETED` (answered and ended), `NO_ANSWER`, `BUSY`, `VOICE_MAIL`, `FAILED`, `DO_NOT_CALL_REGISTRY_MATCHED` (number found on a Do Not Call registry, not dialed), or `ANALYZING`.

**postProcessStatus** `string`

Outcome from post-call analysis: `INTERESTED`, `NOT_INTERESTED`, `CALL_ME_LATER`, `WRONG_NUMBER`, `DO_NOT_CALL`, or `null` if not analyzed (yet).

**contactStatus** `object`

What the contact asked for.

**properties**

**doNotCall** `boolean`

The contact asked not to be called again.

**wrongNumber** `boolean`

The person said it's the wrong number.

**callMeLater** `boolean`

The contact asked to be called back later.

**reason** `string`

Why a call was classified as a callback or unsuccessful attempt: `CALL_ME_LATER_REQUESTED`, `SILENT_CONTACT`, `SHORT_REPLY_OR_HANGUP`, `TECHNICAL_OR_UNSPECIFIED_HANGUP`, `AI_SCREENING_HANGUP`, or `AI_SCREENING_TIMEOUT`.

**successCriteria** `object`

The [success criterion](https://docs.callers.ai/documentation/results/success-criteria.md) that matched.

**properties**

**goalReached** `boolean`

Whether the agent's goal was reached.

**name** `string`

The matched criterion's name, for example `Demo Scheduled`.

**description** `string`

The criterion's description.

**reason** `string`

Why the AI chose this criterion, based on the conversation.

**type** `string`

`INTERESTED` (goal-reached group) or `NOT_INTERESTED` (goal-not-reached group).

**interested** `object` *deprecated*

Legacy version of `successCriteria`, kept for older integrations. `description` is the criterion description and `reached` mirrors `successCriteria.goalReached`.

### Hang-up reasons

**hangUpReason** `string`

Why the call ended:

| Value | Meaning |
| - | - |
| `CALL_FINISHED` | The conversation reached its end. |
| `USER_REQUESTED_HANGUP` | The contact asked to end the call. |
| `USER_MANUAL_HANGUP` | The contact hung up. |
| `ADMIN_MANUAL_HANGUP` | Someone ended the call from the dashboard. |
| `SILENCE_DETECTED` | The call ended after a period of silence. |
| `VOICE_MAIL_DETECTED` | Voicemail was detected. |
| `SCREENING_RESPONSE_TIMEOUT` | A call-screening assistant answered and no person joined in time. |
| `SCREENING_RESPONSE_HANGUP` | A call-screening assistant hung up. |
| `CALL_FORWARDED` | The call was transferred. |
| `GUARDRAIL_FAILED` | A [guardrail](https://docs.callers.ai/documentation/build-your-agent/instructions-and-guardrails.md) ended the conversation. |
| `TRANSFERRED_TO_TRIGGER_BRIDGE` | The call was handed to a Trigger Bridge agent. |
| `FORCE_FINISHED` | The call was force-ended by the system. |
| `CONVERSATION_LLM_TECHNICAL_ISSUES` | The AI model had a technical problem. |
| `OTHER_TECHNICAL_ISSUES` | Another technical problem. |
| `SOCKET_DISCONNECTED` | The audio connection dropped. |
| `UNKNOWN` | The reason couldn't be determined. |

### Durations and recording

**duration** `number`

Seconds the contact spent with the AI agent.

**totalDuration** `number`

Total call length in seconds, including any transferred part.

**recordingUrl** `string`

Recording link, when recording is on and the recording is ready. Most complete on `CALL_FINISHED`. Links can expire; get a fresh one with `GET /agents/{agentId}/calls/{callId}/recording`.

### Conversation

**transcript** `object[]`

The conversation, in order. Each item has `role` (`assistant` for the agent, `user` for the contact) and `content`.

**conversationFlow** `string[]`

Titles of the script blocks the conversation passed through, in order.

**events** `object[]`

Actions that happened during the call. Each item has a `type` and `data`:

| `type` | `data` fields |
| - | - |
| `SMS` | `sent`, `status`, `sentAt`, `sentTo`, `sentFrom`, `text`, `uri`, `smsSid`, `failedReason`, `errorCode` |
| `MEETING` | `duration`, `eventTypeId`, `invitationSent`, `email`, `name`, `sentAt`, `slot`, `meetingUrl`, `failedReason` |
| `FORWARD` | `phoneNumber`, `contactAsCallerId`, `warmTransfer`, `warmTransferPrompt`, `warmTransferAgentOnly`, `warmTransferPlayOnDTMF` |

Other event types are included with `data: null`.

**booking** `object`

Present when a meeting was booked: `{ "meetingUrl": "…" }`.

**forwardInfo** `object`

Details of a call transfer, when the call was forwarded.

### Post-call analysis

**summary** `object`

Answers to the agent's [key questions](https://docs.callers.ai/documentation/using-callers/key-questions.md), keyed by question name. Each entry has:

| Field | Description |
| - | - |
| `value` | The answer. Its type depends on `type`: string, number, boolean, or array. |
| `type` | `OPEN_QUESTION`, `SINGLE_CHOICE`, `MULTIPLE_CHOICE`, `YES_NO`, `NUMBER`, `DATE`, `TIME`, or `LINEAR_SCALE`. |
| `description` | The question's instructions. |
| `options` | Allowed answers for choice questions. |
| `order` | Position of the question in the agent. |

Read an answer with `summary["Question name"].value`.

**extractedVariables** `object`

Values the AI extracted for variables used in your webhooks and actions, keyed by variable name. Each entry has `value`, and may include `type` and `description`.

**unansweredQuestions** `object[]`

Questions the contact asked that the agent couldn't answer: `question` and optional `context`. Useful for improving your knowledge base.

**promisedActions** `object[]`

Commitments the agent made during the call: `commitment`, `commitmentType` (a short free-text category such as `email`, `call`, or `follow-up`), and optional `context`.

### Provider details

**twilio** `object`

Telephony identifiers. `accountSid` is the Twilio account SID for calls placed through Twilio; `callSessionId` is the call session ID.

### Agent configuration fields

The payload also includes the agent's legacy notification settings: `webhookUrl`, `webhooks`, `webhookEvents`, `webhookStatuses`, `slackUrl`, `slackEvents`, and `webhookTriggerEvents`. They describe configuration, not the call, and may be removed in the future. Don't build logic on them; use `webhookEvent` to know why you received a delivery.

## Text and Email payloads

Webhooks for Text and Email agents share the identifiers (`id`, `campaign`, `account`, `organizationId`, `createdAt`, `summary`, `unansweredQuestions`, `promisedActions`), with conversation-specific fields:

| Field | Text | Email | Description |
| - | - | - | - |
| `sessionId` | Yes | Yes | Conversation session ID. |
| `webhookEvents` | Yes | Yes | The event names that triggered this delivery (simple webhooks), for example `["POST_PROCESSING_FINISHED:INTERESTED"]`. There is no `webhookEvent` field. |
| `contact` | `id`, `name`, `phoneNumber`, `data` | `id`, `name`, `email`, `data` | The contact. |
| `messages` | Yes | Yes | The conversation: `role` and `content` per message (instead of `transcript`). |
| `status` | Yes | Yes | Conversation status, for example `COMPLETED`, `FAILED`, `UNSUBSCRIBED`. |
| `postProcessStatus` | Yes | Yes | Outcome, for example `INTERESTED` or `FOLLOW_UP_PENDING`. |
| `goal` | Yes | Yes | Matched success criterion: `reached`, `name`, `description`, `reason`, `type`. |
| `successCriteriaResults` | Yes | Yes | Every criterion evaluated: `name`, `description`, `isReached`, `reason`, `confidence`, `type`, `achievementLevel`, `order`. |
| `contactStatus` | `unsubscribed`, `wrongNumber`, `followUpPending`, `reason` | A status string | What the contact asked for. |
| `fromEmail`, `toEmail`, `subject` | — | Yes | Email addresses and subject. |
| `extractedVariables` | Yes | — | Extracted variable values. |

## Common tasks

| You want | Read |
| - | - |
| Was the goal reached? | `successCriteria.goalReached` |
| Which outcome? | `successCriteria.name`, or `postProcessStatus` |
| A key question's answer | `summary["Question name"].value` |
| Your own record ID | `contact.data.<your key>` |
| Should we stop contacting this person? | `contactStatus.doNotCall` or `contactStatus.wrongNumber` |
| Meeting link | `booking.meetingUrl` |
| Skip test traffic | `isTestCall` |

## Related

> **[Webhook events](https://docs.callers.ai/documentation/webhook/webhook-events.md)**
>
> When each event fires.

> **[Advanced webhooks](https://docs.callers.ai/documentation/webhook/advanced-webhook.md)**
>
> Send only the fields you need, in your format.

> **[Statuses and outcomes](https://docs.callers.ai/documentation/using-callers/call-statuses.md)**
>
> How statuses are assigned.

> **[Post-call analysis](https://docs.callers.ai/documentation/using-callers/key-questions.md)**
>
> Configure the questions behind `summary`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.