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

# Execution payload reference

> Every field in a Bolna call execution: status and lifecycle, transcript, extracted data, telephony, cost, usage, latency and retries, with the traps that matter when you build on it.

An **execution** is the record of one call. The same payload is returned by
[Get execution](/docs/api-reference/executions/get_execution), pushed to your
[webhook](/docs/post-call/polling-call-status-webhooks), listed by
[Get agent executions](/docs/api-reference/agent/v2/get_all_agent_executions), and shown as *Raw Call Data* in
[Call History](/docs/agent-setup/call-history).

It carries the call record, the billing record and the diagnostic record in one object. Most integrations
need a dozen of its keys. This page groups them by what they are for.

<Note>
  List endpoints return a trimmed projection: `id`, `status`, `conversation_duration`, `created_at` and a
  short `telephony_data`. Fetch the execution by id, or receive it on a webhook, to get everything below.
  A payload can also contain keys that are not listed here; those are internal and carry no compatibility
  guarantee, so do not build on them.
</Note>

***

## Before you build on this

Three things account for most of the bugs people hit reading this payload.

### Nothing is final until the status is terminal

`conversation_duration`, `total_cost`, `telephony_data.recording_url`, `transcript` and `extracted_data`
are `null`, `0` or empty until the call finishes and post-processing runs. Read them earlier and you will
record zeros. See the [status lifecycle](/docs/api-reference/executions/get_execution#status-lifecycle).

### `status: "completed"` does not mean a conversation happened

`status` is derived from the telephony provider's call-lifecycle field, not from the hangup cause, so a
call the callee rejected can still arrive as `completed`. Check `conversation_duration > 0` before
treating a call as answered. `telephony_data.hangup_reason` and `hangup_provider_code` carry the real
ending.

### A webhook fires several times for one call

Bolna POSTs this payload as the call's status changes, so a single call produces multiple deliveries
carrying the same `id`. Make your handler idempotent and key it on `id`, or you will count the same call
more than once.

Together those three give the shape of a correct handler: ignore non-terminal deliveries, then decide
what actually happened before recording anything.

```python Handling a delivery theme={"system"}
TERMINAL = {"completed", "no-answer", "busy", "failed",
            "canceled", "stopped", "error", "balance-low"}

def handle(execution):
    if execution["status"] not in TERMINAL:
        return                                  # more deliveries are coming for this call

    telephony = execution["telephony_data"]

    # `completed` on its own is not enough; check that a conversation actually took place
    if execution["conversation_duration"] == 0:
        return record_no_conversation(
            execution["id"], telephony["hangup_reason"], telephony["hangup_provider_code"]
        )

    # the transcript is how you tell whether the caller ever spoke
    transcript = execution.get("transcript") or ""
    if not any(line.startswith("user:") for line in transcript.splitlines()):
        return record_agent_only(execution["id"])   # agent spoke, caller never did

    record_conversation(execution["id"], transcript, execution["extracted_data"])
```

<Note>
  `handle` is keyed on `execution["id"]`, so a repeated delivery for the same call overwrites rather than
  duplicates. That is what makes it safe against point 3.
</Note>

***

## A complete payload

A real completed call. The agent's welcome message played, the caller never spoke, and the agent hung up
after its silence timeout.

```json Completed execution theme={"system"}
{
  "id": "b7140255-af33-4608-8e97-04dd944b8e48",
  "agent_id": "5bc97541-e320-4d95-a3a5-242cfe45621d",
  "batch_id": null,
  "campaign_id": null,

  "status": "completed",
  "smart_status": null,
  "error_message": null,
  "conversation_duration": 28,
  "answered_by_voice_mail": null,

  "created_at": "2026-09-04T08:52:07.408883+00:00",
  "initiated_at": "2026-09-04T08:52:09.737874",
  "updated_at": "2026-09-04T08:52:59.320486+00:00",
  "scheduled_at": null,
  "rescheduled_at": null,

  "user_number": "+919876543210",
  "agent_number": "+918035739222",
  "provider": "vobiz",

  "transcript": "assistant: Hello, this is Sonam from Bolna's Community...\nassistant: Hello, are you still on the line?\n",
  "summary": null,
  "extracted_data": {},

  "telephony_data": {
    "duration": 28,
    "to_number": "+919876543210",
    "from_number": "+918035739222",
    "recording_url": "https://api.bolna.ai/recordings/call/b7140255-af33-4608-8e97-04dd944b8e48",
    "hosted_telephony": true,
    "provider_call_id": "a41e7c93-2b58-4e07-95d1-8f30c62b7a11",
    "call_type": "outbound",
    "provider": "vobiz",
    "hangup_by": "Caller",
    "hangup_reason": "inactivity_timeout",
    "hangup_provider_code": 4010,
    "ring_duration": null,
    "post_dial_delay": null,
    "to_number_carrier": null
  },

  "total_cost": 3,
  "cost_breakdown": {
    "platform": 1,
    "network": 0.25,
    "transcriber": 0.432,
    "synthesizer": 0.69,
    "llm": 0,
    "total_cost_to_deduct": 1.878,
    "llm_breakdown": { "conversation": 0, "extraction": 0, "summarization": 0, "hangup": 0, "routing": 0, "analytics": 0, "voicemail": 0 },
    "synthesizer_breakdown": { "conversation": 0.69, "welcome_message": 0 },
    "transcriber_breakdown": { "conversation": 0.432, "analytics": 0 },
    "transfer_cost": 0
  },

  "usage_breakdown": {
    "transcriber_provider": "deepgram",
    "transcriber_model": "nova-3",
    "transcriber_language": "en",
    "transcriber_duration": 28.199938,
    "synthesizer_provider": "elevenlabs",
    "synthesizer_model": "eleven_turbo_v2_5",
    "synthesizer_characters": 217,
    "voice_id": "IkSv4tkouLJ6kYsQA7XD",
    "llmTokens": 0,
    "llmModel": { "azure/gpt-4.1-mini": { "input": 0, "output": 0 } },
    "provider_source": { "llm": "bolna", "synthesizer": "bolna", "transcriber": "bolna" },
    "llm_usage_breakdown": {
      "conversation": { "model": "azure/gpt-4.1-mini", "provider": "azure", "input": 0, "output": 0 },
      "disposition": { "model": null, "input": 0, "output": 0, "cached_tokens": 0 }
    },
    "synthesizer_usage_breakdown": { "conversation_characters": 69, "welcome_message_characters": 148, "welcome_message_cache": false, "provider_connected": false },
    "transcriber_usage_breakdown": { "transcriber_duration": 28.199938, "provider_connected": false },
    "endpointing": 200,
    "incremental_delay": 300,
    "buffer_size": 220,
    "hangup_after_silence": 15,
    "check_if_user_online": true,
    "trigger_user_online_message_after": 10,
    "voicemail_detection_enabled": false
  },

  "latency_data": {
    "time_to_first_audio": 262.37671,
    "stream_id": 257.49268,
    "region": "in",
    "transcriber": { "time_to_connect": 17, "turns": [] },
    "llm": { "time_to_connect": null, "turns": [] },
    "synthesizer": { "time_to_connect": 88, "turns": [{ "turn": -1, "time_to_first_token": 110, "time_to_last_token": 311 }] },
    "rag": null,
    "interruption_stats": {
      "total_interruptions": 0,
      "user_interrupted_agent_count": 0,
      "agent_interrupted_user_count": 0,
      "user_speaking_ms": 0,
      "agent_speaking_ms": 0,
      "longest_agent_monologue_ms": 0,
      "talk_to_listen_ratio": null,
      "barge_in_recovery_rate": null,
      "barge_in_recovery_count": 0
    }
  },

  "tool_call_logs": [],
  "transfer_call_data": null,
  "context_details": {
    "recipient_phone_number": "+919876543210",
    "recipient_data": { "timezone": "Asia/Kolkata" }
  },
  "retry_count": 0,
  "retry_config": null,
  "retry_history": null
}
```

***

## Identity and linkage

| Field         | Type         | Notes                                                                                                                                                    |
| ------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`          | uuid         | The execution id. Use it with [Get execution](/docs/api-reference/executions/get_execution) and [raw logs](/docs/api-reference/executions/get_execution_raw_logs). |
| `agent_id`    | uuid         | Agent that ran the call                                                                                                                                  |
| `batch_id`    | uuid \| null | Set for [batch](/docs/api-reference/batches/overview) calls                                                                                                   |
| `campaign_id` | uuid \| null | Set for [workflow campaign](/docs/workflows/campaigns) calls                                                                                                  |

## Status and lifecycle

| Field                    | Type            | Notes                                                                                                                                                         |
| ------------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`                 | string          | The status you should branch on. See the [full lifecycle](/docs/api-reference/executions/get_execution#status-lifecycle), and the warning above about `completed`. |
| `smart_status`           | string \| null  | An outcome read from the transcript by a model. `null` for zero-duration calls, so treat it as a hint on top of `status`, never a replacement.                |
| `error_message`          | string \| null  | Populated on `error` and some `failed` calls                                                                                                                  |
| `conversation_duration`  | number          | Conversation seconds. `0` means no conversation took place, whatever `status` says.                                                                           |
| `answered_by_voice_mail` | boolean \| null | Set when voicemail detection is enabled and fires                                                                                                             |

## Timing

| Field            | Type              | Notes                                           |
| ---------------- | ----------------- | ----------------------------------------------- |
| `created_at`     | timestamp         | When the execution record was created           |
| `initiated_at`   | timestamp         | When dialling actually started                  |
| `updated_at`     | timestamp         | Last write, normally the end of post-processing |
| `scheduled_at`   | timestamp \| null | For scheduled calls                             |
| `rescheduled_at` | timestamp \| null | Set when the call was rescheduled               |

<Note>
  `created_at` and `initiated_at` differ by the queue wait. Use `initiated_at` when measuring
  dial-to-answer behaviour, and `created_at` when reconciling against the moment you made the request.
  Scheduled calls also use the statuses `scheduled` and `rescheduled`, which sit before `queued`.
</Note>

## Conversation content

| Field            | Type           | Notes                                                                                                                                                                                                                                                      |
| ---------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `transcript`     | string         | Newline-delimited `speaker: text`. Speakers are `assistant` and `user`. This is how you tell whether the caller ever spoke.                                                                                                                                |
| `summary`        | string \| null | Populated only when the agent's `call_summary_enabled` toggle is on. Off by default.                                                                                                                                                                       |
| `extracted_data` | object         | Post-call [extractions](/docs/prompting/using-extractions), nested `Category` then `Extraction` then `{ subjective, objective, confidence, confidence_label, reasoning_subjective, reasoning_objective, validation }`. `{}` when the agent has no dispositions. |

## Telephony

All under `telephony_data`.

| Field                       | Type            | Notes                                                                                                                                       |
| --------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `duration`                  | number          | Call duration in seconds as reported by the provider. Compare with the top-level `conversation_duration`, which is the figure billing uses. |
| `to_number` / `from_number` | string          | Also mirrored at the top level as `user_number` / `agent_number`                                                                            |
| `recording_url`             | string \| null  | `null` if recording is off or the call never connected                                                                                      |
| `call_type`                 | string          | `outbound` or `inbound`                                                                                                                     |
| `provider`                  | string          | `twilio`, `plivo`, `exotel`, `vobiz`, `sip-trunk`                                                                                           |
| `hosted_telephony`          | boolean         | Whether the number is Bolna-hosted rather than your own                                                                                     |
| `provider_call_id`          | string          | The provider's id for the call. Quote this in a provider support ticket.                                                                    |
| `hangup_by`                 | string          | Which side ended the call, for example `Caller`, or the provider name when the platform ended it                                            |
| `hangup_reason`             | string          | Normalised reason, for example `inactivity_timeout`                                                                                         |
| `hangup_provider_code`      | integer \| null | The provider's raw code. Carries rejection detail that `status` can miss. See the warning above.                                            |
| `ring_duration`             | number \| null  | Seconds spent ringing, when the provider reports it                                                                                         |
| `post_dial_delay`           | number \| null  | Dial to first ring, when the provider reports it                                                                                            |
| `to_number_carrier`         | string \| null  | Carrier lookup for the dialled number, when available                                                                                       |

## Cost

`total_cost` and `cost_breakdown` are the billing figures, reported **in cents**. The dashboard presents
the same numbers as credits. See [Call pricing](/docs/pricing/call-pricing).

| Field                                                | Notes                                                                                                                                                                          |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `total_cost`                                         | Total for the call                                                                                                                                                             |
| `cost_breakdown.platform` / `network`                | Bolna platform fee and telephony                                                                                                                                               |
| `cost_breakdown.transcriber` / `synthesizer` / `llm` | Per-component cost                                                                                                                                                             |
| `cost_breakdown.total_cost_to_deduct`                | The wallet deduction for this call. It can differ from `total_cost`, so reconcile billing against this field.                                                                  |
| `cost_breakdown.llm_breakdown`                       | LLM cost split by purpose: `conversation`, `extraction`, `summarization`, `hangup`, `routing`, `analytics`, `voicemail`. A call with extractions bills more than one LLM pass. |
| `cost_breakdown.synthesizer_breakdown`               | Splits `conversation` from `welcome_message`, which is cached separately                                                                                                       |
| `cost_breakdown.transcriber_breakdown`               | Splits `conversation` from `analytics`                                                                                                                                         |
| `cost_breakdown.transfer_cost`                       | Added when the call was transferred                                                                                                                                            |

## Usage

`usage_breakdown` is the unit-level detail behind the cost. Read it when you want to know *why* a call
cost what it did, or which models actually ran.

| Field                                                                                      | Notes                                                                                                                             |
| ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| `transcriber_provider` / `transcriber_model` / `transcriber_language`                      | The transcriber that ran                                                                                                          |
| `transcriber_duration`                                                                     | Seconds of audio transcribed, fractional                                                                                          |
| `synthesizer_provider` / `synthesizer_model` / `voice_id`                                  | The voice that ran                                                                                                                |
| `synthesizer_characters`                                                                   | Characters synthesised                                                                                                            |
| `llmModel` / `llmTokens`                                                                   | Model used and total tokens                                                                                                       |
| `llm_usage_breakdown`                                                                      | Per-purpose token detail for `conversation`, `disposition`, `extraction` and `summarization`, each with `input` and `output`      |
| `provider_source`                                                                          | Whether each component billed to `bolna` or to your own connected key                                                             |
| `synthesizer_usage_breakdown`                                                              | Splits `conversation_characters` from `welcome_message_characters`, and reports whether the welcome message was served from cache |
| `transcriber_usage_breakdown`                                                              | Transcribed duration, and whether your own key served the call                                                                    |
| `endpointing`, `incremental_delay`, `buffer_size`, `hangup_after_silence`                  | The latency-relevant settings this call ran with. Useful when comparing two calls that behaved differently.                       |
| `check_if_user_online`, `trigger_user_online_message_after`, `voicemail_detection_enabled` | The presence-detection settings this call ran with                                                                                |

## Latency

`latency_data` is the diagnostic record. See [Call latencies](/docs/concepts/call-latencies) for how to read it.

| Field                                 | Notes                                                                                                                                                                                                       |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `time_to_first_audio`                 | Milliseconds from the end of the caller's utterance to the start of the agent's audio. The number callers actually feel.                                                                                    |
| `region`                              | Region the media ran in                                                                                                                                                                                     |
| `transcriber` / `llm` / `synthesizer` | Each has `time_to_connect` and a `turns` array of per-turn timings                                                                                                                                          |
| `interruption_stats`                  | Barge-in behaviour: counts in each direction, speaking time each side, `longest_agent_monologue_ms`, `talk_to_listen_ratio` and barge-in recovery. The first place to look when an agent talks over people. |
| `rag`                                 | Knowledge-base retrieval timings, when the agent uses one                                                                                                                                                   |
| `routing`                             | Semantic-routing timings, when the agent uses routes                                                                                                                                                        |

<Warning>
  If PII masking is enabled on your account, phone numbers, `telephony_data.recording_url` and the string
  values inside `extracted_data` come back as `null`. The keys are still present, so code that reads them
  sees `null` rather than a missing field. Check with your account settings before building on those
  values.
</Warning>

## Transfers, context and retries

| Field                   | Notes                                                                                                                                                                                                |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tool_call_logs`        | Always returned as an empty array. Per-invocation [function tool](/docs/tool-calling/custom-function-calls) detail is not included in API responses; read it from the execution in the dashboard instead. |
| `transfer_call_data`    | Populated when the call was [transferred](/docs/tool-calling/transfer-calls)                                                                                                                              |
| `context_details`       | The recipient context the call ran with, including `recipient_phone_number` and any `recipient_data` variables you passed                                                                            |
| `agent_context_details` | Agent-side context, when set                                                                                                                                                                         |
| `batch_run_details`     | Batch row detail for batch calls                                                                                                                                                                     |
| `retry_count`           | Attempts so far                                                                                                                                                                                      |
| `retry_config`          | The [auto-retry](/docs/guides/outbound/auto-retry) policy in force                                                                                                                                        |
| `retry_history`         | Prior attempts                                                                                                                                                                                       |
| `workflow_retries`      | Retries driven by a [workflow](/docs/workflows/introduction)                                                                                                                                              |

***

## Reading the payload well

<AccordionGroup>
  <Accordion title="Did a real conversation happen?">
    Check `conversation_duration > 0`, then look for a line starting with `user:` in `transcript`.
    `status` alone is not enough, because it comes from the telephony provider's lifecycle field and can
    read `completed` for a call the callee rejected. `telephony_data.hangup_reason` and
    `hangup_provider_code` tell you how it really ended.
  </Accordion>

  <Accordion title="Why did this call cost what it did?">
    Start at `cost_breakdown.llm_breakdown`, because extractions and summarisation are separate LLM passes
    on top of the conversation, so an agent with many dispositions costs more per call. Then check
    `usage_breakdown.provider_source`: components billing to `bolna` are charged to your wallet, while
    ones billing to your own connected key are not.
  </Accordion>

  <Accordion title="Why was the agent slow?">
    `latency_data.time_to_first_audio` is what the caller felt. Split it with the per-component
    `time_to_connect` and `turns` values to see which leg was slow, then compare
    `usage_breakdown.endpointing` and `incremental_delay` against a call that felt fine. See
    [Call latencies](/docs/concepts/call-latencies).
  </Accordion>

  <Accordion title="Why did the agent talk over the caller?">
    `latency_data.interruption_stats`, specifically `agent_interrupted_user_count`,
    `longest_agent_monologue_ms` and `barge_in_recovery_rate`. The settings that change this behaviour,
    interruption thresholds and endpointing, live in the [Engine tab](/docs/agent-setup/engine-tab).
  </Accordion>

  <Accordion title="Can I get this pushed to me instead of polling?">
    Yes. Set `webhook_url` on the agent and Bolna POSTs this same payload as the call progresses. Whitelist
    `13.203.39.153`, `13.126.9.249` and `13.202.133.53`. See
    [Webhooks](/docs/post-call/polling-call-status-webhooks).
  </Accordion>
</AccordionGroup>
