> For the complete documentation index, see [llms.txt](https://docs.voiceb.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.voiceb.ai/webhook-call-outcome-integration.md).

# Webhook – Call Outcome Integration

## VoiceB.ai Webhook – Call Outcome Integration

Webhooks let you receive structured call outcomes, transcripts, and lead data after every call. Use this to:

* Save conversations to your database
* Trigger follow-ups or automations
* Push call results into any system that accepts JSON, in the structure your system expects

***

### 🔗 How it Works

1. Add the **Webhook** integration to your agent (see the steps below) and enter your endpoint URL.
2. Choose how VoiceB authenticates to your endpoint (a fixed token or a stored credential) and, optionally, enable **signature signing** for secure verification.
3. After every call, VoiceB sends a `POST` request with the call result to your URL. Use **Trigger Conditions** to choose which outcomes are sent.
4. Optionally, turn on a **custom body template** to send the JSON in your own structure.

***

### How to Add a Webhook Integration

1. **In your VoiceB account**, go to **Campaigns**.
2. Select the **Agent** you want to configure.
3. Once inside the **Agent Config** (Name, AI Provider, Goal, Script…), scroll down to **Integrations**.
4. Click on the **“+”** button.
5. Choose **Webhook** from the list.
6. You will now see the configuration screen with instructions and examples.

<figure><img src="https://2950912279-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggIr2RvhGm49D4vbjurT%2Fuploads%2FW1JekvQp7PMJZ7rabhSn%2FScreenshot%202025-09-03%20at%2009.37.09.png?alt=media&amp;token=ede820bb-f7fe-4592-9011-ae684e5ee737" alt="" width="563"><figcaption></figcaption></figure>

<figure><img src="https://2950912279-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggIr2RvhGm49D4vbjurT%2Fuploads%2FapZlmyI2Tvv5Jc8EjXeT%2FScreenshot%202025-09-03%20at%2009.37.20.png?alt=media&amp;token=31cb68f1-0390-455f-98b4-920c6825ac97" alt="" width="563"><figcaption></figcaption></figure>

<figure><img src="https://2950912279-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggIr2RvhGm49D4vbjurT%2Fuploads%2FAb1o5epGOhSABpmUnnyQ%2FScreenshot%202025-09-03%20at%2009.37.31.png?alt=media&amp;token=8062c592-606d-4602-acf3-3701383764b5" alt="" width="563"><figcaption></figcaption></figure>

***

### 🔑 Authentication

Choose how VoiceB authenticates to your endpoint:

* **Fixed token:** a static Bearer token that you type into the settings. It is sent on every request and never expires or refreshes on its own.
* **Stored credential:** pick one of the credentials stored for the campaign from a dropdown. VoiceB logs in to your system for you (for example with OAuth2 client credentials or a custom login request) and refreshes the token automatically. No secret is ever shown in the UI. Credentials are the same ones used by Agent Tools, see [Credentials](https://docs.voiceb.ai/credentials).

Use a stored credential when your system requires a login step before every request, so you never have to rotate tokens by hand.

***

### 📬 Default Webhook Payload

By default, VoiceB sends the following JSON object. Fields are sent in this order:

```json
{
  "event": "post_call",
  "agent": "AgentName",
  "from": "+1234567890",
  "to": "+0987654321",
  "id": 12345,
  "start": "2026-10-28T09:15:02.000Z",
  "end": "2026-10-28T09:17:41.000Z",
  "status": "SOLD",
  "type": "...",
  "channelUsed": "...",
  "transcript": [ ... ],
  "humanTranscript": "...",
  "summary": "...",
  "testCall": false,
  "language": "...",
  "transferred": false,
  "inbound": false,
  "agentPhone": "+1234567890",
  "customerPhone": "+0987654321",
  "variables": { },
  "previousCallId": null,
  "nextCallId": null,
  "previousRetryCallId": null,
  "nextRetryCallId": null,
  "tags": ["Tag name"],
  "collectedData": {
    "name": "John Doe",
    "email": "john.doe@example.com",
    "address": "Calle Ejemplo 123, 45678, Ciudad"
  }
}
```

These keys are only present on some calls:

* `retry` and `pendingRetry`: when the agent has a retry policy, the call is in a retry chain, or a retry is scheduled.
* `consent`: on calls that collected consent (widget and click-to-call).
* `reviewFlag`: on calls that were flagged for review.
* `sipHeaders`: on calls that came through a SIP trunk.
* `status` and `collectedData`: when the call has no lead.

The call recording URL is never included.

{% hint style="warning" %}
**Change in release 1.6.1:** the default payload is now built from a fixed list of fields. The fields `linkedCallsIndex`, `linkedCallsTotal`, `provider` and `sessionLegs` are **no longer sent**. If your system reads any of them, update it. If you need the payload in a different shape, use a custom body template.
{% endhint %}

***

### 🧩 Custom Body Template (Optional)

By default VoiceB sends the payload above. If your system expects a different JSON structure, you can define your own.

1. Open the Webhook integration settings.
2. Turn on **Use custom body template**.
3. Write your JSON in the editor. Type `{{` to see the available variables with their types, and use them wherever you need a value.
4. Save. The template must be valid JSON, otherwise it cannot be saved.

Example template:

```json
{
  "callId": "{{id}}",
  "outcome": "{{status}}",
  "customer": {
    "name": "{{collectedData.name}}",
    "phone": "{{customerPhone}}"
  },
  "messages": "{{transcript}}",
  "labels": "{{tags}}",
  "note": "Call {{id}} by {{agent}}"
}
```

The body VoiceB sends is this template with the variables filled in. The webhook URL can also contain `{{variables}}`.

The template root can be a JSON **object or an array**, for example `[ { "id": "{{id}}" } ]`, if your system expects a list.

#### Keeping data types

When a value is **exactly** one variable, for example `"messages": "{{transcript}}"`, VoiceB inserts the variable with its real type: lists stay lists, objects stay objects, and numbers and booleans stay numbers and booleans. In the example above, `callId` is a number, `messages` is the transcript as a list, and `labels` is a list of tag names.

When the variable is mixed with other text, for example `"Call {{id}} by {{agent}}"`, it is inserted as text.

#### Default values

To use a fallback when a value is missing, empty or null, add `||` and a default after the variable name. **The default must be a valid JSON value.**

| Variable with default                 | Default used    |
| ------------------------------------- | --------------- |
| `{{collectedData.name\|\|"Unknown"}}` | text `Unknown`  |
| `{{collectedData.age\|\|0}}`          | number `0`      |
| `{{collectedData.optIn\|\|false}}`    | boolean `false` |
| `{{collectedData.email\|\|null}}`     | `null`          |
| `{{tags\|\|[]}}`                      | empty list      |
| `{{collectedData\|\|{}}}`             | empty object    |

Because the template is itself JSON, a text default placed inside a text value needs escaped quotes:

```json
"name": "{{collectedData.name||\"Unknown\"}}"
```

A default such as `{{collectedData.name||Unknown}}` (text without quotes) is not valid, and the template cannot be saved until it is fixed.

If you do not set a default and a value is missing, VoiceB uses an empty value of the right type: empty text for text, `null` for numbers, dates and optional objects, `false` for booleans, `[]` for lists and `{}` for `collectedData`, `sipHeaders` and `variables`. Inside text or in the URL, a missing value becomes an empty string.

#### Available variables

| Variable                                                                                                                                 | Type         | Notes                                                                                                                      |
| ---------------------------------------------------------------------------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `event`, `agent`, `from`, `to`, `agentPhone`, `customerPhone`, `status`, `type`, `channelUsed`, `summary`, `humanTranscript`, `language` | text         |                                                                                                                            |
| `id`, `previousCallId`, `nextCallId`, `previousRetryCallId`, `nextRetryCallId`                                                           | number       |                                                                                                                            |
| `start`, `end`                                                                                                                           | date         | ISO format                                                                                                                 |
| `testCall`, `transferred`, `inbound`                                                                                                     | true / false |                                                                                                                            |
| `transcript`                                                                                                                             | list         | The parsed conversation messages                                                                                           |
| `tags`                                                                                                                                   | list         | Tag names                                                                                                                  |
| `collectedData`                                                                                                                          | object       | Also `collectedData.<field>` for any field of your Data Collection schema                                                  |
| `sipHeaders`                                                                                                                             | object       | Also `sipHeaders.<header>`                                                                                                 |
| `variables`                                                                                                                              | object       | Also `variables.<name>` for the call's dynamic variables                                                                   |
| `consent`                                                                                                                                | object       | Only on calls with consent. Fields: `id`, `ipAddress`, `userAgent`, `origin`, `documentVerified`, `createdAt`, `documents` |
| `reviewFlag`                                                                                                                             | object       | Only on flagged calls. Fields: `id`, `category`, `description`, `flaggedBy`, `createdAt`                                   |
| `retry`                                                                                                                                  | object       | Only with a retry policy. Fields: `attempt`, `max`                                                                         |
| `pendingRetry`                                                                                                                           | object       | Only when a retry is scheduled. Fields: `id`, `attemptNumber`, `scheduledFor`, `failureReason`                             |

The fields removed from the default payload (`linkedCallsIndex`, `linkedCallsTotal`, `provider`, `sessionLegs`), the recording URL and the integration sync details are not available as variables.

{% hint style="info" %}
Custom body templates are available on the **post-call webhook** only. Pre-call and pre-transfer webhooks work as before.
{% endhint %}

***

### 🛡️ Signature Verification (Optional)

Headers:

```ini
Authorization=Bearer XXXXXXXXXXXXXXXXXX
x-webhook-signature=YYYYYYYYYYYYYYYYYYY
```

Use your **signature secret** to verify the authenticity of the payload. The signature is computed over the exact body that VoiceB sends. If you use a custom body template, it is computed over the filled-in body.

Always verify the signature against the **raw request body** as you received it, not against a JSON object that you have parsed and re-serialized, because re-serializing can change spacing and key order and the check will fail.

***

### Trigger Conditions

By setting trigger conditions, you can define specific outcomes that will determine if the integration runs for that call or not. Allowing you to determine which ones ends in your system/CRM

<figure><img src="https://2950912279-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggIr2RvhGm49D4vbjurT%2Fuploads%2F3EnU74CQOlJGc6BF5Ryk%2Fimagen.png?alt=media&amp;token=74a21523-a104-4909-ae95-4cad5a9f1886" alt=""><figcaption></figcaption></figure>

***

### 🔍 Monitoring and Resync

Each call shows whether its webhook was delivered (**ok**) or not (**failed**). Open the integration drawer to see the **Debug Trace**, which records failures only. If a call failed, click **Resync** to send it again once the problem is fixed. Debug traces are visible to organization admins.

If a custom body template cannot be filled in correctly when the call ends, nothing is sent and the call is recorded as a failed sync, so you can fix the template and resync.

***

If you need help setting this up, reach out to <hello@voiceb.ai>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.voiceb.ai/webhook-call-outcome-integration.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
