Webhooks

Send automated HTTP requests before, during or after conversations. Use webhooks to pass call details into your own systems, trigger workflows, store outcomes or make an action available for the agent to use.

How Webhooks Work

When a webhook trigger fires, the platform sends an HTTP request to your configured URL containing call data as a JSON payload.

You can configure multiple webhooks per agent, each pointing to a different endpoint. Every webhook fires independently, so a failure in one does not block the others.

Webhook Types

Two webhook types are available, each fired in different circumstances:

TypeWhen it firesTypical use
End Call Report end_call_reportAutomatically when a call ends.Push the call summary, transcript metadata, and extracted structured data into your CRM, data warehouse, or workflow tool.
Queue Handoff queue_handoffMid-call when the agent invokes its handoff_to_queue tool against a contact-centre queue target that does not define its own per-target webhookUrl.Pull the call back into a contact-centre queue without using SIP REFER; your platform receives the active call ID and routes it to a live queue using your provider's API.

Pick the type at the top of the Add Webhook dialog. Both types share the same URL, method, headers, parameters, and log surface; only the trigger and payload contents differ.

Choose When a Webhook Runs

Use When to run in the webhook editor to choose the point in the conversation when the request is available:

  • Pre conversation: runs once before the opening greeting. Use it to prepare information the agent needs immediately.
  • During conversation: gives the agent an action it can run when needed. Add a clear description explaining when it should be used.
  • Post conversation: runs after the final call record is ready. This is the default and suits summaries, follow-ups and reporting.

Existing webhooks keep their timing

Existing standard webhooks continue to run after the conversation unless you change them. Queue handoff webhooks remain available during the conversation.

Creating a Webhook

Open the agent editor

Navigate to the agent you want to configure and open the editor.

Go to the Webhooks tab

Open the editor menu and select Webhooks. The tab opens in the main workspace.

Click Add Webhook

Click the Add Webhook button to create a new webhook entry.

Choose when it runs

Select Pre conversation, During conversation or Post conversation. For a during-conversation webhook, explain clearly when the agent should use it.

Configure the webhook fields

Fill in the fields described below and save your changes.

Name

A descriptive label for your webhook, e.g. "CRM Sync" or "Slack Notification". This name appears in the webhook list and in execution logs.

URL

The fully qualified HTTPS endpoint that will receive the webhook payload. The URL must be publicly accessible and respond within the timeout window.

Method

The HTTP method used for the request. Defaults to POST. Other supported methods include PUT and PATCH.

Headers

Optional custom HTTP headers sent with every request, defined as JSON key-value pairs. Use this to pass authentication tokens, API keys, or content-type overrides to your endpoint.

Parameters

Additional parameters to include in the request body alongside the standard call data. This is useful for passing static identifiers or routing information your server needs.

Return a 2xx response quickly

Your endpoint must return a 2xx HTTP status code within 30 seconds for end-call report webhooks, and within 3 seconds for queue handoff webhooks (which fire mid-call, so the caller is actively waiting). If the request times out or returns a non-2xx code, it will be marked as failed in the webhook logs. Offload any heavy processing to a background job so your handler can respond immediately.

Controlling When a Webhook Fires

These outcome filters apply to post-conversation webhooks, when the final call outcome is available. By default an end_call_report webhook fires after every call. The Send this webhook when… section lets you narrow that down, for example to transferred calls or interested leads.

Rules come in two kinds. Exclusions are evaluated first and always win: if a call matches an exclusion, the webhook is skipped even when a positive trigger also matched. Positive triggers then decide whether to send.

Positive triggers

The webhook is sent only when the call matches:

TriggerSends only when…
Transferredthe call was transferred (warm or cold).
DNC requestthe caller asked to opt out / go on the Do-Not-Call list.
Disposition is one of…the call's disposition matches one of the values you pick.
Call status is one of…the technical call status matches one of the values you pick.

When you set two or more positive triggers, a Match any / Match all switch appears: any sends when at least one matches (OR), all requires every one to match (AND).

Exclusions

If a call matches any exclusion, the webhook is skipped:

ExclusionSkips the send when…
Skip if transferredthe call was transferred (e.g. only log calls that still need a human).
Skip if DNC requestthe caller requested Do-Not-Call. This is useful for a CRM sync that must not touch opt-outs.
Except these dispositionsthe disposition is one of the values you pick.
Except these call statusesthe call status is one of the values you pick.

Disposition and call-status values

Dispositions describe the outcome of the conversation: interested, not_interested, callback_requested, appointment_scheduled, dnc_request, wrong_number, contact_verified, information_provided, transferred, voicemail, no_answer, busy, failed, no_response, unknown.

Call statuses describe the technical result of the call: success, transferred, no_answer, busy, failed, error, forbidden, service_unavailable, not_found, not_picked.

Setting triggers directly in the body

The controls above are stored in a reserved _options object inside the webhook body. It is stripped out before the request is sent, so it never reaches your endpoint. If you prefer to edit the body JSON directly, the keys are:

Trigger options
{
  "_options": {
    "only_on_transfer": true,
    "only_on_dnc": true,
    "only_on_dispositions": ["interested", "callback_requested"],
    "only_on_call_statuses": ["success"],
    "match": "any",

    "only_if_not_transferred": true,
    "only_if_not_dnc": true,
    "except_on_dispositions": ["voicemail"],
    "except_on_call_statuses": ["no_answer", "busy"]
  }
}

The list keys accept either a single string or an array of strings. match is "any" (default) or "all". Unknown values in a list are simply never matched, so a typo is harmless.

Existing webhooks are unaffected

All trigger keys default to off. A webhook with no triggers set behaves exactly as before; it fires on every end-of-call. Add a rule only when you want to narrow delivery.

Payload Format

End-call report webhooks send a JSON body made up of a handful of top-level objects. A trimmed example:

End-call payload
{
  "webhook_info": {
    "webhook_id": "wh_abc123",
    "webhook_name": "CRM Sync",
    "webhook_type": "end_call_report",
    "executed_at": "2026-03-10T14:26:09.000Z"
  },
  "call_log": {
    "call_id": "call_abc123def456",
    "call_type": "phone",
    "call_status": "completed",
    "started_at": "2026-03-10T14:23:00.000Z",
    "ended_at": "2026-03-10T14:26:07.000Z",
    "duration": 187,
    "call_direction": "inbound",
    "from_number": "+442079460958",
    "to_number": "+442079460000",
    "phone_number_id": "pn_789",
    "recording_url": null,
    "transferred": false,
    "transferred_to": null,
    "ended_by_agent": true,
    "ended_by_user": false,
    "total_messages": 14,
    "metadata": { ... },
    "call_metrics": { ... }
  },
  "call_summary": {
    "summary_text": "Caller asked about opening hours...",
    "key_topics": ["opening hours"],
    "action_items": [],
    "sentiment": "positive",
    "user_satisfaction": "high",
    "resolution_status": "resolved",
    "disposition": "information_provided"
  },
  "structured_data": { ... },
  "custom_metadata": { ... },
  "assistant_info": {
    "assistant_id": "asst_345mno",
    "user_id": "user_789ghi"
  }
}
End-call payload objects
ObjectContents
webhook_infoThe webhook's ID, name, type, and the execution timestamp.
call_logThe full call record: identifiers, timing (started_at, ended_at, duration in seconds), direction, from/to numbers, status, transfer details, who ended the call, message counts, the nullable recording URL, and call metrics.
call_summaryThe AI-generated summary (summary_text), key topics, action items, sentiment, user satisfaction, resolution status, and disposition.
structured_dataFields extracted by the AI according to the webhook's extraction configuration.
custom_metadataMetadata passed verbatim to /api/v1/calls/initiate when the call was started via the API, e.g. your own account or correlation IDs.
assistant_infoThe agent (assistant_id) and owning user (user_id).

All timestamps are in ISO 8601 format (UTC). The duration field is in seconds. If you configured additional parameters, they are merged into the top level of the payload.

Recording URLs require your own object storage

The recording_url field is populated only when your workspace subscribes to the additional paid option to use your own object storage for call recordings. Without that option, the field is null. Recordings held in default Cloudax storage remain encrypted and secure, and direct public access is disabled for privacy. You can still play them through authenticated Cloudax experiences.

Customising the Payload

If your endpoint expects a different shape from the standard envelope above, open the Payload editor in the webhook dialog and design the exact body you want. Write the JSON your system needs and drop in variables wherever a live value should go. Type {{ inside any string to see the full list with autocomplete.

Custom payload
{
  "id": "{{call.id}}",
  "phone": "{{call.customer_number}}",
  "summary": "{{summary.summary_text}}",
  "outcome": "{{summary.disposition}}",
  "fields": "{{structured}}"
}

You can reference whole objects like {{call}}, {{summary}}, {{structured}} (your extracted variables), {{metadata}}, {{assistant}} and {{webhook}}, or individual fields such as {{summary.sentiment}} and {{structured.<your_variable>}}. To send a value Base64-encoded, wrap it as {{base64(summary.summary_text)}}. As with tools, the format dropdown lets you send JSON, form-encoded, or a custom content type, and it controls the request's Content-Type so you do not need to set that as a header.

The recording URL variable is nullable

{{call.recording_url}} has a value only for workspaces subscribed to the additional paid option to use their own object storage. Otherwise it resolves to null; default Cloudax recordings are encrypted and are not exposed through a direct public URL.

A conversation namespace gives you the call transcript directly in a custom payload: {{conversation.messages}} resolves to an array of { "role": "user" | "assistant", "content": "..." } objects (one per transcript turn, with function calls, agent handoffs and blank turns filtered out), and {{conversation.text}} resolves to the same transcript flattened into a single readable block formatted as Customer: ... / Agent: ... lines. Used as the entire value of a field (e.g. "transcript": "{{conversation.messages}}"), {{conversation.messages}} is preserved as a real JSON array in the payload rather than being stringified, just like {{call}} and other whole-value placeholders. If a call has no transcript (for example a HIPAA/PCI-locked assistant, or a campaign with transcribeCalls off), both resolve to empty values ([] and "") instead of being omitted, so templates referencing them never break.

Existing webhooks keep their format

Leave the Payload editor untouched and your webhook sends the standard envelope shown above, exactly as before. Customise it only when you want to match a specific format.

Queue Handoff Webhooks

Queue handoff webhooks let your platform pull an active call back into a contact-centre queue when the agent has finished triaging, without requiring SIP REFER. They're fired mid-call by the agent's handoff_to_queue tool and are used as a fallback when a Contact Centre queue target does not define its own per-target webhookUrl.

The payload includes the active call's identifiers (the Cloudax callLogId, the captured providerCallId and the captured SIP headers in sipHeaders), the chosen queue name, the from/to numbers, the target's name and type, and the conversation transcript so far. Your endpoint should respond 2xx as soon as it has accepted the handoff; the actual queue pull is performed by your contact-centre platform using whatever API or SIP signalling it normally uses to route calls.

Per-target overrides win

If a Contact Centre queue target has its own webhookUrl configured, that URL is fired instead of the assistant-level queue_handoff webhook. Use the assistant-level webhook as a catch-all default and override on a per-queue basis only when a specific queue needs to hit a different endpoint.

The outcome of the handoff request (success, timeout, HTTP status, error message, and the URL that was dialled) is surfaced on the call details page so you can verify, troubleshoot, and audit individual handoffs from the Calls UI as well as from the webhook logs.

Securing Your Endpoint

Webhook deliveries hit a public URL on your server, so it's up to you to confirm that an inbound request really came from Cloudax Connect. The recommended approach is to use the Headers field on the webhook (see Headers above) to attach a shared secret on every delivery.

For example, generate a long random token, store it in your server's environment, and add it as an Authorization header on the webhook:

Webhook authorization
Authorization: Bearer wh_••••••••••••••••

Reject any incoming request whose Authorization header does not match the expected value. You can use any header name you like: X-Webhook-Token, X-Api-Key, etc., as long as your endpoint is configured to verify it. This prevents anyone who happens to know the URL from sending fake events.

Restrict by IP if you can

If your webhook endpoint runs behind a firewall or reverse proxy, you can additionally allow-list the IP range that Cloudax Connect calls from. Combine that with the shared-token check above for defence in depth.

Webhook Logs

Every webhook execution is recorded and visible on the Webhook Logs page. Each log entry shows:

  • Status: success, fail, or timeout.
  • HTTP response code: the status code returned by your endpoint (e.g. 200, 400, 500).
  • Execution time: how long the request took in milliseconds.
  • Request payload: the full JSON body that was sent.
  • Response payload: the body returned by your server, if any.

Use the logs to diagnose delivery failures, inspect payloads, and verify that your endpoint is processing data correctly. Webhook logs are retained for as long as the call record they belong to (12 months by default).

Troubleshooting

Timeouts

If your endpoint takes longer than the timeout window to respond (30 seconds for end-call reports, 3 seconds for queue handoffs), the request will be marked as a timeout. Move heavy processing (database writes, third-party API calls, file generation) into a background queue and return a 200 response immediately from your handler.

Failed Deliveries

A delivery is marked as failed when your endpoint returns a non-2xx status code. Common causes include:

  • Incorrect URL: double-check the endpoint address and ensure it is publicly reachable.
  • Authentication errors: if your server requires an API key or token, add it in the Headers field.
  • Server errors (5xx): check your server logs for unhandled exceptions or resource limits.

Authentication Errors

If your endpoint returns 401 or 403, verify that the correct authentication credentials are configured in the webhook's custom headers. Ensure tokens have not expired and that the endpoint accepts requests from external sources.

Test with webhook.site

During development, use a service like webhook.site to inspect incoming payloads without setting up your own server. Copy the unique URL it provides, paste it into the webhook URL field, and trigger a test call to see the full request.