Skip to content

AutoCall Webhook Events

When a call occurs, Zorio POSTs an event to the URL you registered (HMAC-signed).

Events

EventWhen it fires
autocall.lead.addedA lead was successfully pushed into a campaign
autocall.lead.audio_readyTTS audio rendering finished, the lead is ready to dial
autocall.call.initiatedThe engine started originate
autocall.call.answeredB-leg answered
autocall.dtmf.pressedThe called party pressed a key (1 event per keypress)
autocall.call.transferredDTMF action = queue → transferred to the queue successfully
autocall.call.completedThe call ended (any outcome)
autocall.lead.completedThe lead has finished every retry (final status)
autocall.campaign.completedThe campaign has no pending leads left
autocall.tts.render_failedThe TTS provider errored out and the lead was skipped

Common envelope

Every event uses the same envelope structure:

FieldTypeDescriptionValid values
eventstringFull event nameautocall.<entity>.<action>
timestampdatetimeWhen Zorio published the event (ISO 8601 UTC)
tenant_idintegerAccount ID receiving the webhook
dataobjectEvent-specific payload (see each event below)

data fields per event

autocall.lead.added / autocall.lead.audio_ready / autocall.lead.completed

FieldTypeDescriptionValid values
lead_idintegerLead ID in AutoCall
campaign_idintegerID of the campaign containing the lead
phonestringLead phone numberE.164 or Vietnamese domestic
external_refstring|nullExternal CRM reference (if you pushed one)
audio_urlstring|nullTTS audio URL (only present on audio_ready)
final_statusstringFinal state (only present on lead.completed)completed / failed / dnc / max_attempts_reached
total_attemptsintegerTotal dial attempts (only on lead.completed)>= 1

autocall.call.initiated

FieldTypeDescriptionValid values
uuidstringCall UUID
campaign_idintegerCampaign ID
lead_idintegerLead ID
phonestringNumber being dialed
attempt_numberintegerWhich attempt this is>= 1
caller_idstringCaller-ID used for the call
started_atdatetimeOriginate time (ISO 8601 UTC)

autocall.call.answered

Includes all fields of call.initiated plus:

FieldTypeDescriptionValid values
answered_atdatetimeTime the B-leg answered (ISO 8601 UTC)

autocall.dtmf.pressed

FieldTypeDescriptionValid values
uuidstringCall UUID
campaign_idintegerCampaign ID
lead_idintegerLead ID
phonestringNumber being called
digitstringKey just pressed0-9, *, #
pressed_atdatetimeDTMF receive time (ISO 8601 UTC)

autocall.call.transferred

FieldTypeDescriptionValid values
uuidstringCall UUID
campaign_idintegerCampaign ID
lead_idintegerLead ID
transfer_targetstringTransfer destinationqueue:<queue_name> / extension:<ext>
transferred_atdatetimeTime the transfer succeeded (ISO 8601 UTC)

autocall.call.completed

FieldTypeDescriptionValid values
uuidstringCall UUID
campaign_idintegerCampaign ID
lead_idintegerLead ID
phonestringNumber that was called
attempt_numberintegerWhich attempt this is>= 1
started_atdatetimeOriginate time (ISO 8601 UTC)
answered_atdatetime|nullAnswer time (null if not answered)
ended_atdatetimeHangup time (ISO 8601 UTC)
duration_secintegerTotal call duration (seconds)>= 0
resultstringOutcome classificationconnected / no_answer / busy / failed / voicemail / dnc_blocked
hangup_causestringStandard Q.850 hangup code from Zorio PBXNORMAL_CLEARING / NO_ANSWER / USER_BUSY / CALL_REJECTED / ...
dtmf_pressedstring|nullThe last key the user pressed
final_actionstring|nullFinal action executed (per the DTMF script)queue:<name> / hangup / dnc_added / null

autocall.campaign.completed

FieldTypeDescriptionValid values
campaign_idintegerID of the campaign that just finished
campaign_namestringCampaign name
total_leadsintegerTotal leads in the campaign>= 0
total_connectedintegerLeads that answered successfully>= 0
completed_atdatetimeCampaign completion time (ISO 8601 UTC)

autocall.tts.render_failed

FieldTypeDescriptionValid values
lead_idintegerID of the skipped lead
campaign_idintegerCampaign ID
providerstringTTS provider that erroredtts_provider_01 / tts_provider_02 / tts_provider_03 / local
error_codestringError codeRATE_LIMIT / INVALID_VOICE / QUOTA_EXCEEDED / UNKNOWN
error_messagestringError description
failed_atdatetimeTTS failure time (ISO 8601 UTC)

Configuring webhooks (via Admin Console, NOT the Public API)

Webhooks are NOT registered via API

Per Zorio's security policy, webhook configuration is done manually through the Admin Console. The Public API only serves AutoCall business operations (campaign / lead / call lifecycle / reports).

Why:

  • Prevents abuse: if clients could self-register, a leaked token could redirect webhooks outside the account.
  • Ensures auditability: every webhook config change is logged in admin activity.
  • Separation of business vs config: the public API stays focused on call data.

Onboarding workflow:

  1. Contact your system administrator (within your organization) or the Zorio support team.
  2. Provide:
    • The webhook endpoint URL (HTTPS)
    • The list of events to subscribe to (see Events)
    • Optional: an IP allowlist for your infrastructure
  3. The system administrator configures it in the Admin Console + selects the events → saves.
  4. The system generates a secret (32 bytes, base64) → delivered to you over a secure channel.
  5. You deploy the webhook receiver + use the secret to verify the HMAC signature (see Headers).

Changing the configuration (changing URL, adding events, rotating the secret) is also done through the Admin Console, not via the API.

Sample delivery payload (event autocall.call.completed)

json
{
  "event": "autocall.call.completed",
  "timestamp": "2026-06-24T08:32:47Z",
  "tenant_id": 1,
  "data": {
    "uuid": "093e1024-...",
    "campaign_id": 36,
    "lead_id": 78912,
    "phone": "84912345678",
    "attempt_number": 1,
    "started_at": "2026-06-24T08:32:00Z",
    "answered_at": "2026-06-24T08:32:05Z",
    "ended_at": "2026-06-24T08:32:47Z",
    "duration_sec": 42,
    "result": "connected",
    "hangup_cause": "NORMAL_CLEARING",
    "dtmf_pressed": "2",
    "final_action": "queue:support_queue"
  }
}

Headers Zorio POSTs to your URL

HeaderTypeDescriptionValue
X-Zorio-SignaturestringHMAC-SHA256 of the raw body using the webhook secretsha256=<hex>
X-Zorio-TimestampintegerEpoch seconds (UTC) — clients should reject if skew > 300s
X-Zorio-EventstringConvenience copy of the event fieldautocall.<entity>.<action>
X-Zorio-DeliverystringDelivery UUID — used for dedup on retryUUID v4
Content-TypestringContent typeapplication/json; charset=utf-8

Verify HMAC

Pseudo-code:

expected = "sha256=" + hex(hmac_sha256(secret, raw_request_body))
if expected != header["X-Zorio-Signature"]: reject

Do NOT verify against the parsed JSON — you must use the raw body bytes.

Retry policy

  • 3 retries: 5s, 30s, 5 minutes
  • After 3 failures → dead-lettered (admins view via the Admin Console)
  • HTTP 2xx = success. 4xx/5xx = failure.

Client-side idempotency

The client MUST ensure the endpoint is idempotent (check the X-Zorio-Delivery UUID) because the webhook may be resent if HTTP timed out on Zorio's side after the client already executed it.

Cấp phép theo điều khoản sử dụng của Zorio.