{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-docs/voice-2.0/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"product":"Voice v2 (preview)","type":"markdown"},"seo":{"title":"Track Call Status","siteUrl":"https://developers.sinch.com","llmstxt":{"title":"Sinch Developer Documentation","description":"LLMs.txt containing a map of all the documentation files for Sinch.","sections":[{"title":"Numbers API","description":"The Numbers API enables you to search for, view, and activate numbers. It's considered a precursor to other APIs in the Sinch product family. The numbers API can be used in tandem with any of our APIs that perform messaging or calling.","includeFiles":["docs/numbers/**/*.md","docs/numbers/**/*.yaml"],"excludeFiles":["docs/numbers/index.md"]},{"title":"Conversation API","description":"Send and receive messages globally on many popular channels with ease and confidence when using Sinch's Conversation API. Conversation API is the preferred API for sending mobile messages on SMS and other social channels with Sinch. It is a simple API with unified error messages, consistent request payloads, and common webhook payloads that are channel-agnostic.","includeFiles":["docs/conversation/**/*.md","docs/conversation/**/*.yaml"],"excludeFiles":["docs/conversation/index.md"]},{"title":"Voice API","description":"The Voice API works as a big telephony switch. The Voice API handles incoming phone calls (also known as incoming call “legs”), sets up outgoing phone calls (or outgoing call “legs”), and bridges the two. The incoming call leg may come in over a data connection (from a smartphone or web application using the Sinch SDKs) or through a local phone number (from the PSTN network). Similarly, the outgoing call leg can be over data (to another smartphone or web application using the Sinch SDKs) or the PSTN network.","includeFiles":["docs/voice/**/*.md","docs/voice/**/*.yaml"],"excludeFiles":["docs/voice/index.md"]},{"title":"Voice API v2","description":"The Voice API works as a big telephony switch. The Voice API handles incoming phone calls (also known as incoming call “legs”), sets up outgoing phone calls (or outgoing call “legs”), and bridges the two. The incoming call leg may come in over a data connection (from a smartphone or web application using the Sinch SDKs) or through a local phone number (from the PSTN network). Similarly, the outgoing call leg can be over data (to another smartphone or web application using the Sinch SDKs) or the PSTN network.","includeFiles":["docs/voice-2.0/**/*.md","docs/voice-2.0/**/*.yaml"],"excludeFiles":["docs/voice-2.0/index.md"]},{"title":"Verification API","description":"The Verification API is a platform for phone number verification. It consists of the API and different software development kits (the Sinch SDKs) that you integrate with your smartphone or web application and cloud based back-end services. Together they enable SMS, Flashcall, Phone Call and Data verification in your application.","includeFiles":["docs/verification/**/*.md","docs/verification/**/*.yaml"],"excludeFiles":["docs/verification/index.md"]},{"title":"Provisioning API","description":"Provisioning API allows you to programmatically set up your senders, accounts and templates on your favorite messaging platforms on the Conversation API. For now, you can create your first WhatsApp channel through Meta's Embedded sign up, you can configure your first SMS App and configure your webhooks. As development continues, we will be adding the most commonly used channels.","includeFiles":["docs/provisioning-api/**/*.md","docs/provisioning-api/**/*.json"],"excludeFiles":["docs/provisioning-api/index.md"]},{"title":"Elastic SIP Trunking API","description":"With Elastic SIP Trunking you can create and manage your SIP trunks and phone numbers programmatically.","includeFiles":["docs/est/**/*.md","docs/est/**/*.yaml"],"excludeFiles":["docs/est/index.md"]},{"title":"Fax API","description":"Send and receive HIPAA compliant faxes on our modern fax platform using our developer-friendly API.","includeFiles":["docs/fax/**/*.md","docs/fax/**/*.yaml"],"excludeFiles":["docs/fax/index.md"]},{"title":"In-app Voice and Video SDK","description":"The In-app Voice and Video SDK enables you to add voice and video calling capabilities directly into your mobile or web application using the Sinch SDKs.","includeFiles":["docs/in-app-calling/**/*.md"],"excludeFiles":["docs/in-app-calling/index.md"]},{"title":"Number Lookup API","description":"The Number Lookup API is designed to provide in-depth information about phone numbers, helping enterprises enhance their communication strategies and prevent fraud. By identifying the type of phone line (for example, mobile, landline, VoIP) and the associated carrier, the API allows businesses to optimize routing, reduce unnecessary costs, and improve customer engagement.","includeFiles":["docs/number-lookup-api-v2/**/*.md","docs/number-lookup-api-v2/**/*.yaml"],"excludeFiles":["docs/number-lookup-api-v2/index.md"]},{"title":"Functions","description":"Serverless compute for voice and messaging. Deploy your code and Sinch routes live calls and messages to it — no infrastructure to run.","includeFiles":["docs/functions/functions/**/*.md","docs/functions/concepts/**/*.md","docs/functions/reference/**/*.md"],"excludeFiles":["docs/functions/functions/index.md"]},{"title":"CLI","description":"One command line for every Sinch API and the full Functions lifecycle — scaffold, run locally, deploy, and manage.","includeFiles":["docs/functions/cli/**/*.md"],"excludeFiles":["docs/functions/cli/index.md"]}]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"track-call-status","__idx":0},"children":["Track Call Status"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"overview","__idx":1},"children":["Overview"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Voice API v2 gives you two complementary ways to know what is happening with a call:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Webhook events (recommended for real-time)."]}," Sinch pushes lifecycle events"," ","(",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.incoming"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.answered"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.hangup"]},", and so on) to your HTTPS"," ","endpoint as they happen. This is the right choice whenever you need to ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["react"]}," ","to a call: update a dashboard, log an SLA timestamp, or write a CRM record the"," ","moment a call ends."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Polling endpoints (fallback / on-demand)."]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /calls/{callId}"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /sessions/{sessionId}"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /calls"]}," (list & filter), and"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /batches/{batchId}"]}," (batch summary) let you read the platform's"," ","authoritative state on demand. Use them when you ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["cannot"]}," run a public webhook"," ","receiver, for reconciliation and reporting jobs, or to back-fill state you may"," ","have missed."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you can run a public endpoint, use webhooks. Reach for polling only when you"," ","can't, or for after-the-fact reporting."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"prerequisites","__idx":2},"children":["Prerequisites"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://dashboard.sinch.com"},"children":["Sinch account"]}," with API credentials."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A Sinch virtual phone number."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["(Webhooks only) A publicly accessible HTTPS URL. ngrok works during development."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A way to start a call so there is something to track. The examples below start"," ","one for you; if you want to start calls separately, see"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/voice-2.0/tutorials/outbound-tts"},"children":["Outbound Call"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A runtime for the code examples: Node.js 18+ (built-in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fetch"]},"), or Python 3.9+"," ","with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["requests"]},", or PHP 8+ with the cURL extension."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"setup","__idx":3},"children":["Setup"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All requests use ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["HTTP Basic auth"]}," with your access key as username and secret"," ","as password. Instead of a config file, export the values into your shell so both"," ","the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["curl"]}," commands and the code examples can read them from the environment:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"export PROJECT_ID='...'            # your project UUID\nexport KEY_ID='...'                # access key ID  (Basic auth username)\nexport KEY_SECRET='...'            # access key secret (Basic auth password)\nexport SERVICE_ID='...'            # your voice service UUID\nexport SINCH_NUMBER='+1...'        # your Sinch virtual number (E.164)\nexport DESTINATION_NUMBER='+1...'  # number to call (E.164)\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["curl"]}," below authenticates with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-u \"$KEY_ID:$KEY_SECRET\""]},". The Node.js,"," ","Python, and PHP examples read the same variables via ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["process.env"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["os.environ"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["getenv()"]}," respectively, so export them once in the terminal"," ","where you run the code."]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Chicken-and-egg note."]}," Tracking needs a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sessionId"]}," (or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callId"]},") from a"," ","call that already exists. The tracking recipe below starts a call and captures"," ","its ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sessionId"]}," in one step. If you already started a call from"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/voice-2.0/tutorials/outbound-tts"},"children":["Outbound Call"]},", grab the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sessionId"]}," ","from the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /calls"]}," response and run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["export SESSION_ID=..."]}," before polling."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"first-success-track-a-call-in-real-time-with-webhooks-recommended","__idx":4},"children":["First success: track a call in real time with webhooks (recommended)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is the recommended path. Run a small receiver, point your service at it, and"," ","start a call; Sinch streams the lifecycle to your terminal."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"1-run-the-receiver","__idx":5},"children":["1. Run the receiver"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The receiver below logs every webhook event with its CloudEvents headers and"," ","acknowledges correctly. It answers ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.incoming"]}," with a short goodbye and acks"," ","every other event with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["commands: []"]},". Use it to learn exactly what Sinch sends"," ","for your flows. Note that there is no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["accept"]}," command in v2: you return the call"," ","flow directly, typically starting with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["answer"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Pick one language, save the file, and run it."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"nodejs","__idx":6},"children":["Node.js"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Save as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["server.mjs"]},", then run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["npm install express"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["node server.mjs"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"js","header":{"controls":{"copy":{}}},"source":"// server.mjs — minimal Sinch Voice v2 webhook receiver\nimport express from \"express\";\n\nconst app = express();\napp.use(express.json());\n\napp.post(\"/voice/events\", (req, res) => {\n  const ce = {\n    type:        req.header(\"ce-type\"),\n    source:      req.header(\"ce-source\"),\n    id:          req.header(\"ce-id\"),\n    time:        req.header(\"ce-time\"),\n    specversion: req.header(\"ce-specversion\"),\n  };\n  const { event, call } = req.body || {};\n  console.log(JSON.stringify({ ce, event, call }, null, 2));\n\n  if (event === \"call.incoming\") {\n    // Return the call flow directly. events.onHangup is honored on the\n    // response to call.incoming.\n    return res.status(200).json({\n      commands: [\n        { command: \"answer\" },\n        {\n          command: \"messages\",\n          messages: [\n            { type: \"SAY\", say: { text: \"Thank you for calling. Goodbye.\", voiceName: \"Emma\" } },\n          ],\n          events: { onFinish: [{ command: \"hangup\" }] },\n        },\n      ],\n      events: { onHangup: [{ command: \"hangup\" }] },\n    });\n  }\n\n  return res.status(200).json({ commands: [] });\n});\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => {\n  console.log(`Webhook receiver on :${PORT}`);\n  console.log(\"Point callBehaviors.webhook.url -> https://<your-ngrok>/voice/events\");\n});\n","lang":"js"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"python","__idx":7},"children":["Python"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Save as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["server.py"]},", then run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pip install flask"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["python server.py"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"# server.py — minimal Sinch Voice v2 webhook receiver\nimport os\nimport json\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\n\n\n@app.post(\"/voice/events\")\ndef voice_events():\n    ce = {\n        \"type\":        request.headers.get(\"ce-type\"),\n        \"source\":      request.headers.get(\"ce-source\"),\n        \"id\":          request.headers.get(\"ce-id\"),\n        \"time\":        request.headers.get(\"ce-time\"),\n        \"specversion\": request.headers.get(\"ce-specversion\"),\n    }\n    body = request.get_json(silent=True) or {}\n    event = body.get(\"event\")\n    call = body.get(\"call\")\n    print(json.dumps({\"ce\": ce, \"event\": event, \"call\": call}, indent=2))\n\n    if event == \"call.incoming\":\n        # Return the call flow directly; events.onHangup is honored here.\n        return jsonify({\n            \"commands\": [\n                {\"command\": \"answer\"},\n                {\n                    \"command\": \"messages\",\n                    \"messages\": [\n                        {\"type\": \"SAY\", \"say\": {\"text\": \"Thank you for calling. Goodbye.\", \"voiceName\": \"Emma\"}}\n                    ],\n                    \"events\": {\"onFinish\": [{\"command\": \"hangup\"}]},\n                },\n            ],\n            \"events\": {\"onHangup\": [{\"command\": \"hangup\"}]},\n        })\n\n    return jsonify({\"commands\": []})\n\n\nif __name__ == \"__main__\":\n    port = int(os.environ.get(\"PORT\", 3000))\n    print(f\"Webhook receiver on :{port}\")\n    print(\"Point callBehaviors.webhook.url -> https://<your-ngrok>/voice/events\")\n    app.run(port=port)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"php","__idx":8},"children":["PHP"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Save as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["server.php"]},", then run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["php -S 0.0.0.0:3000 server.php"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"php","header":{"controls":{"copy":{}}},"source":"<?php\n// server.php — minimal Sinch Voice v2 webhook receiver\n\n$path = parse_url($_SERVER[\"REQUEST_URI\"], PHP_URL_PATH);\nif ($_SERVER[\"REQUEST_METHOD\"] !== \"POST\" || $path !== \"/voice/events\") {\n    http_response_code(404);\n    exit;\n}\n\n// Custom headers arrive as HTTP_CE_* in $_SERVER.\n$ce = [\n    \"type\"        => $_SERVER[\"HTTP_CE_TYPE\"] ?? null,\n    \"source\"      => $_SERVER[\"HTTP_CE_SOURCE\"] ?? null,\n    \"id\"          => $_SERVER[\"HTTP_CE_ID\"] ?? null,\n    \"time\"        => $_SERVER[\"HTTP_CE_TIME\"] ?? null,\n    \"specversion\" => $_SERVER[\"HTTP_CE_SPECVERSION\"] ?? null,\n];\n\n$body  = json_decode(file_get_contents(\"php://input\"), true) ?: [];\n$event = $body[\"event\"] ?? null;\n$call  = $body[\"call\"] ?? null;\nerror_log(json_encode([\"ce\" => $ce, \"event\" => $event, \"call\" => $call], JSON_PRETTY_PRINT));\n\nheader(\"Content-Type: application/json\");\n\nif ($event === \"call.incoming\") {\n    // Return the call flow directly; events.onHangup is honored here.\n    echo json_encode([\n        \"commands\" => [\n            [\"command\" => \"answer\"],\n            [\n                \"command\"  => \"messages\",\n                \"messages\" => [\n                    [\"type\" => \"SAY\", \"say\" => [\"text\" => \"Thank you for calling. Goodbye.\", \"voiceName\" => \"Emma\"]],\n                ],\n                \"events\" => [\"onFinish\" => [[\"command\" => \"hangup\"]]],\n            ],\n        ],\n        \"events\" => [\"onHangup\" => [[\"command\" => \"hangup\"]]],\n    ]);\n    exit;\n}\n\necho json_encode([\"commands\" => []]);\n","lang":"php"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Once the receiver is running, expose it during development:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"ngrok http 3000    # in another terminal\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"2-point-your-service-at-it","__idx":9},"children":["2. Point your service at it"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Configure the service's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callBehaviors.webhook.url"]}," to"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://<your-ngrok>/voice/events"]},". A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dial"]}," whose ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["events"]}," property is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["omitted"]}," falls back to"," ","this service-level webhook for its lifecycle events."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"3-start-a-call-and-watch-the-events-arrive","__idx":10},"children":["3. Start a call and watch the events arrive"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Start an outbound call, or place an inbound call to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["$SINCH_NUMBER"]},". You'll see events like:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{ \"event\": \"call.answered\",\n  \"call\": { \"callId\": \"01ARZ...\", \"sessionId\": \"01BX5...\", \"callResult\": \"IN_PROGRESS\" } }\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{ \"event\": \"call.hangup\",\n  \"call\": { \"callId\": \"01ARZ...\", \"sessionId\": \"01BX5...\", \"callResult\": \"COMPLETED\", \"callReason\": \"CALLEE_HANGUP\" } }\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.hangup"]}," arrives with a final ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callResult"]},", the call is done. That's"," ","your signal to write the outcome to your CRM, dashboard, or log."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"events-youll-receive","__idx":11},"children":["Events you'll receive"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Event"},"children":["Event"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"When"},"children":["When"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.incoming"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Inbound call arrived on a service with webhook behaviour. Respond with the call-flow ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["commands"]}," directly (typically starting with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["answer"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.answered"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Outbound call answered by the recipient."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.busy"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Outbound call recipient was busy."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.rejected"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Outbound call rejected by the recipient."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.timeout"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Outbound call not answered within ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dialTimeoutDurationSeconds"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.hangup"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Call disconnected."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.failed"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Call could not be set up."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.webhook.<name>"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Dynamic event triggered by a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook"]}," SVAML command (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhookName"]}," is prepended with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.webhook."]},")."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"request-format","__idx":12},"children":["Request format"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Delivery uses CloudEvents binary mode. Each request looks like:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"http","header":{"controls":{"copy":{}}},"source":"POST /voice/events\nce-specversion: 1.0\nce-type:        com.sinch.voice.call.control.v1\nce-source:      projects/<projectId>/services/<serviceId>\nce-id:          <uuid>\nce-time:        2026-04-01T12:00:00Z\ncontent-type:   application/json\n\n{ \"event\": \"call.answered\",\n  \"call\": { \"callId\": \"...\", \"sessionId\": \"...\", \"callResult\": \"IN_PROGRESS\" } }\n","lang":"http"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"webhook-contract","__idx":13},"children":["Webhook contract"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Sinch enforces a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["5-second response timeout"]},". Slow responses may affect call quality."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Webhook requests are ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["blocking"]},": execution of the call flow pauses until the endpoint responds, and the next command runs once a response has been received."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Webhook URLs are configured on the service via the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/voice-2.0/api-reference/voice/services"},"children":["Services API"]}," or the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://dashboard.sinch.com/voice/services"},"children":["Dashboard"]},". The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook"]}," SVAML command carries its own ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["url"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fallbackUrl"]}," for mid-call events."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"what-counts-as-a-failure","__idx":14},"children":["What counts as a failure"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A delivery attempt has failed when:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The endpoint returns a non-successful status code."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The endpoint does not respond within the 5-second timeout."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Returned body does not contain proper SVAML commands."," ","Because the call flow is blocked while waiting, a delivery that ends with a failure disconnects the call. This happens when the primary URL fails and no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fallbackUrl"]}," is configured, or when both the primary URL and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fallbackUrl"]}," fail."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"failover-algorithm","__idx":15},"children":["Failover algorithm"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This section is the authoritative description of how the webhook URL is selected. When a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fallbackUrl"]}," is configured:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Requests are sent to the primary ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["url"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If a request to the primary URL fails, that same event is re-sent immediately to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fallbackUrl"]},". The primary URL continues to be used for subsequent requests."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["After several ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["consecutive failures"]}," (exact number may vary) of the primary URL, the primary URL is bypassed and requests are sent only to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fallbackUrl"]},". This avoids incurring the timeout on every request."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["While the primary URL is bypassed it is retried once every ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["60 seconds"]},". The interval is counted from the last failure of the primary URL that triggers the bypass. Each unsuccessful retry restarts the 60-second clock. As soon as a retry succeeds the primary URL is restored and processing returns to step 1."," ","If no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fallbackUrl"]}," is configured, a failed request is not retried and the event is not delivered."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"delivery-guarantees","__idx":16},"children":["Delivery guarantees"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Because a failed request is re-sent to the fallback URL, the same event can be delivered more than once — for example when the primary endpoint received and processed the request but responded too slowly. Webhook handlers should be idempotent and deduplicate on the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ce-id"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ce-source"]}," header pair."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"suppressing-webhooks-per-command","__idx":17},"children":["Suppressing webhooks per command"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you embed inline ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["events"]}," on a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dial"]},", those events are handled directly and"," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["no webhook fires"]}," for them:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["No ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["events"]}," key"]}," means all lifecycle events go to the service webhook."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["events: {}"]}," (empty object)"]}," means no webhook fires ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["and"]}," no inline handling"," ","occurs. Use sparingly; it's an easy trap that silently drops events."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"fallback--on-demand-polling","__idx":18},"children":["Fallback / on-demand: polling"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use polling when you can't run a public webhook endpoint, for reconciliation and"," ","reporting, or to recover state you may have missed. Polling reads the platform's"," ","authoritative state, so it's also the source of truth for post-call reporting."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["curl"]}," snippets below show the raw request and response for each endpoint."," ","The same Basic-auth pattern (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["KEY_ID"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["KEY_SECRET"]},") used in the code recipe at"," ","the end applies if you call these from Node.js, Python, or PHP."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"fetch-one-call-by-id","__idx":19},"children":["Fetch one call by id"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -u \"$KEY_ID:$KEY_SECRET\" \\\n  \"https://voice.api.sinch.com/v2/projects/$PROJECT_ID/calls/$CALL_ID\" | jq\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Returns a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call"]}," resource:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"callId\":          \"01ARZ3NDEKTSV4RRFFQ69G5FAA\",\n  \"projectId\":       \"...\",\n  \"serviceId\":       \"...\",\n  \"sessionId\":       \"01BX5ZZKBKACTAV9WEVGEMMVRB\",\n  \"direction\":       \"OUTBOUND\",\n  \"originationType\": \"SERVER\",\n  \"callType\":        \"PHONE\",\n  \"callResult\":      \"COMPLETED\",\n  \"callReason\":      \"CALLEE_HANGUP\",\n  \"startTime\":       \"2025-02-10T09:00:00Z\",\n  \"answerTime\":      \"2025-02-10T09:00:05Z\",\n  \"endTime\":         \"2025-02-10T09:00:47Z\",\n  \"callDurationSeconds\": 42,\n  \"from\": { \"type\": \"PHONE\", \"phone\": { \"number\": \"+15551234567\" } },\n  \"to\":   { \"type\": \"PHONE\", \"phone\": { \"number\": \"+15559876543\" } },\n  \"callRate\": { \"currencyCode\": \"USD\", \"amount\": \"0.0123\" },\n  \"callResourceUrl\": \"https://voice.api.sinch.com/v2/projects/.../calls/01ARZ3...\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The key state field is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callResult"]}]}," (verified against the spec; these are the"," ","only valid values):"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Value"},"children":["Value"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Meaning"},"children":["Meaning"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Kind"},"children":["Kind"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["QUEUED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Queued for initiation."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["transitional"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INITIATED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Connecting; recipient has not yet answered."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["transitional"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["IN_PROGRESS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Answered and in progress."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["transitional"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["COMPLETED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Answered and ended."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["final"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["REJECTED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Rejected by the recipient."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["final"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["NO_ANSWER"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Not answered by the recipient."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["final"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CANCEL"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Cancelled."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["final"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["BUSY"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Recipient was busy."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["final"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FAILED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Could not be completed."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["final"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For ended calls, ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callReason"]}]}," explains ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["why"]}," (verified against the spec;"," ","these are the only valid values): ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["OK"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["NOT_AVAILABLE"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CALLER_HANGUP"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CALLEE_HANGUP"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MANAGER_HANGUP"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DID_NOT_FOUND"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["INVALID_SCRIPT"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["UNKNOWN_PRODUCT"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["NO_MORE_ROUTES"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ERROR"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"fetch-a-session-all-legs","__idx":20},"children":["Fetch a session (all legs)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A session can contain multiple call legs: the original call plus any nested"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dial"]},"s or transfers. To inspect them together:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -u \"$KEY_ID:$KEY_SECRET\" \\\n  \"https://voice.api.sinch.com/v2/projects/$PROJECT_ID/sessions/$SESSION_ID\" | jq\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"sessionId\":  \"01BX5ZZKBKACTAV9WEVGEMMVRB\",\n  \"projectId\":  \"...\",\n  \"serviceId\":  \"...\",\n  \"state\":      \"COMPLETED\",\n  \"createTime\": \"2025-02-10T09:00:00Z\",\n  \"updateTime\": \"2025-02-10T09:00:47Z\",\n  \"endTime\":    \"2025-02-10T09:00:47Z\",\n  \"calls\": [\n    { \"callId\": \"...\", \"callResult\": \"COMPLETED\", \"callReason\": \"CALLEE_HANGUP\" },\n    { \"callId\": \"...\", \"callResult\": \"NO_ANSWER\" }\n  ]\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The session-level ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["state"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sessionStates"]},", verified against the spec) is one of:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Value"},"children":["Value"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Meaning"},"children":["Meaning"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Kind"},"children":["Kind"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["QUEUED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Queued; no calls initiated yet."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["transitional"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["IN_PROGRESS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["At least one call initiated or received."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["transitional"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["COMPLETED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Session completed; no more events."]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["final"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["COMPLETED"]}," is the only final state, so that's your stop condition when polling."," ","Inspect ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["calls[]"]}," for per-leg results; this is the right view for hunt or"," ","forwarding flows where the question is \"which leg won?\"."]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Field note."]}," Each entry in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["calls[]"]}," is a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call"]}," resource, which carries"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callId"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callResult"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callReason"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callDurationSeconds"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["answerTime"]},", and"," ","similar. It does ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["not"]}," carry ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callName"]},". That property exists only on the"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dial"]}," command you sent and as the path segment in"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /sessions/{sessionId}/calls/{callName}"]},". Don't expect ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callName"]}," in this"," ","response."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"list--filter-calls","__idx":21},"children":["List & filter calls"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /v2/projects/{projectId}/calls"]}," accepts query parameters:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Parameter"},"children":["Parameter"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Use"},"children":["Use"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["serviceId"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Restrict to one service."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["from"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["to"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["E.164 number filters."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callType"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PHONE"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SIP"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["STREAM"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callResult"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["One of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callResult"]}," enum values above."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callReason"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["One of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callReason"]}," enum values above."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["startTime"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Include calls that started ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["at or after"]}," this RFC 3339 timestamp."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["endTime"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Include calls that ended ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["before"]}," this RFC 3339 timestamp (exclusive)."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pageSize"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pageNumber"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Page size and 1-based page number."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Example, listing all ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FAILED"]}," calls since midnight UTC:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -u \"$KEY_ID:$KEY_SECRET\" \\\n  \"https://voice.api.sinch.com/v2/projects/$PROJECT_ID/calls?callResult=FAILED&startTime=$(date -u +%Y-%m-%dT00:00:00Z)&pageSize=50\" \\\n  | jq '.calls[] | {callId, callResult, callReason, from: .from.phone.number, to: .to.phone.number}'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Pagination uses the standard RFC 8288 ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Link"]}," header (relations such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["first"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["prev"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["next"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["last"]},"):"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"Link: <.../calls?pageNumber=2&pageSize=50>; rel=\"next\",\n      <.../calls?pageNumber=4&pageSize=50>; rel=\"last\"\n"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"batch-summary","__idx":22},"children":["Batch summary"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For batch operations:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -u \"$KEY_ID:$KEY_SECRET\" \\\n  \"https://voice.api.sinch.com/v2/projects/$PROJECT_ID/batches/$BATCH_ID\" | jq\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The response includes counts (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callCount"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["completed"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["failed"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["inProgress"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["queued"]},") and a per-session ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callSessions[]"]}," array, each entry having an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]}," and"," ","a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status"]}," of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["QUEUED"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["IN_PROGRESS"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["COMPLETED"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"recipe-start-a-call-and-poll-to-completion-bounded","__idx":23},"children":["Recipe: start a call and poll to completion (bounded)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This recipe starts an outbound call, captures its ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sessionId"]},", then polls the"," ","session until it reaches the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["COMPLETED"]}," final state. It uses a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["bounded"]}," ","number of attempts and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["backoff"]}," so it can never loop forever, and it stops"," ","immediately on the terminal state. Note that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callName"]}," is not returned by the"," ","API, so the final output does not include it."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Pick one language and run it in the same shell where you exported the variables."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"nodejs-1","__idx":24},"children":["Node.js"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Save as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["track-session.mjs"]},", then run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["node track-session.mjs"]}," (Node.js 18+)."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"js","header":{"controls":{"copy":{}}},"source":"// track-session.mjs — start an outbound call, then poll to completion.\nimport { randomUUID } from \"node:crypto\";\n\nconst BASE = `https://voice.api.sinch.com/v2/projects/${process.env.PROJECT_ID}`;\nconst auth =\n  \"Basic \" + Buffer.from(`${process.env.KEY_ID}:${process.env.KEY_SECRET}`).toString(\"base64\");\nconst sleep = (ms) => new Promise((r) => setTimeout(r, ms));\n\nconst body = {\n  commands: [\n    {\n      command: \"dial\",\n      callName: \"tracked\",\n      from: { type: \"PHONE\", phone: { number: process.env.SINCH_NUMBER } },\n      to:   { type: \"PHONE\", phone: { number: process.env.DESTINATION_NUMBER } },\n      dialTimeoutDurationSeconds: 30,\n      events: {\n        onAnswer: [\n          {\n            command: \"messages\",\n            messages: [\n              { type: \"SAY\", say: { text: \"This call is being tracked. Goodbye.\", voiceName: \"Emma\" } },\n            ],\n            events: { onFinish: [{ command: \"hangup\" }] },\n          },\n        ],\n        onHangup: [{ command: \"hangup\" }],\n      },\n    },\n  ],\n};\n\n// 1. Start the call. Inline events play a short message and hang up.\nconst startRes = await fetch(`${BASE}/calls`, {\n  method: \"POST\",\n  headers: {\n    Authorization: auth,\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": randomUUID(),\n  },\n  body: JSON.stringify(body),\n});\nconst { sessionId } = await startRes.json();\nif (!sessionId) throw new Error(\"No sessionId in response\");\nconsole.log(`Started sessionId=${sessionId}`);\n\n// 2. Poll until COMPLETED (the only final session state), bounded with backoff.\nlet delay = 2000, maxDelay = 15000, attempt = 0, maxAttempts = 40, state;\nwhile (attempt < maxAttempts) {\n  const res = await fetch(`${BASE}/sessions/${sessionId}`, { headers: { Authorization: auth } });\n  ({ state } = await res.json());\n  console.log(`state=${state}`);\n  if (state === \"COMPLETED\") break; // terminal state -> stop\n  attempt++;\n  await sleep(delay);\n  delay = Math.min(delay * 2, maxDelay);\n}\n\n// 3. Print per-leg results (callName is NOT returned by the API).\nconst finalRes = await fetch(`${BASE}/sessions/${sessionId}`, { headers: { Authorization: auth } });\nconst { calls = [] } = await finalRes.json();\nfor (const c of calls) {\n  const { callId, callResult, callReason, callDurationSeconds, answerTime } = c;\n  console.log(JSON.stringify({ callId, callResult, callReason, callDurationSeconds, answerTime }));\n}\n","lang":"js"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"python-1","__idx":25},"children":["Python"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Save as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["track_session.py"]},", then run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pip install requests"]}," and"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["python track_session.py"]},"."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"# track_session.py — start an outbound call, then poll to completion.\nimport os\nimport sys\nimport time\nimport json\nimport uuid\nimport requests\n\nBASE = f\"https://voice.api.sinch.com/v2/projects/{os.environ['PROJECT_ID']}\"\nAUTH = (os.environ[\"KEY_ID\"], os.environ[\"KEY_SECRET\"])\n\nbody = {\n    \"commands\": [\n        {\n            \"command\": \"dial\",\n            \"callName\": \"tracked\",\n            \"from\": {\"type\": \"PHONE\", \"phone\": {\"number\": os.environ[\"SINCH_NUMBER\"]}},\n            \"to\":   {\"type\": \"PHONE\", \"phone\": {\"number\": os.environ[\"DESTINATION_NUMBER\"]}},\n            \"dialTimeoutDurationSeconds\": 30,\n            \"events\": {\n                \"onAnswer\": [\n                    {\n                        \"command\": \"messages\",\n                        \"messages\": [\n                            {\"type\": \"SAY\", \"say\": {\"text\": \"This call is being tracked. Goodbye.\", \"voiceName\": \"Emma\"}}\n                        ],\n                        \"events\": {\"onFinish\": [{\"command\": \"hangup\"}]},\n                    }\n                ],\n                \"onHangup\": [{\"command\": \"hangup\"}],\n            },\n        }\n    ]\n}\n\n# 1. Start the call. Inline events play a short message and hang up.\nstart = requests.post(\n    f\"{BASE}/calls\",\n    auth=AUTH,\n    headers={\"Content-Type\": \"application/json\", \"Idempotency-Key\": str(uuid.uuid4())},\n    json=body,\n)\nsession_id = start.json().get(\"sessionId\")\nif not session_id:\n    sys.exit(f\"No sessionId in response: {start.text}\")\nprint(f\"Started sessionId={session_id}\")\n\n# 2. Poll until COMPLETED (the only final session state), bounded with backoff.\ndelay, max_delay, attempt, max_attempts = 2, 15, 0, 40\nwhile attempt < max_attempts:\n    state = requests.get(f\"{BASE}/sessions/{session_id}\", auth=AUTH).json().get(\"state\")\n    print(f\"state={state}\")\n    if state == \"COMPLETED\":  # terminal state -> stop\n        break\n    attempt += 1\n    time.sleep(delay)\n    delay = min(delay * 2, max_delay)\n\n# 3. Print per-leg results (callName is NOT returned by the API).\ncalls = requests.get(f\"{BASE}/sessions/{session_id}\", auth=AUTH).json().get(\"calls\", [])\nfields = (\"callId\", \"callResult\", \"callReason\", \"callDurationSeconds\", \"answerTime\")\nfor c in calls:\n    print(json.dumps({k: c.get(k) for k in fields}))\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"php-1","__idx":26},"children":["PHP"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Save as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["track-session.php"]},", then run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["php track-session.php"]}," (PHP 8+ with cURL)."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"php","header":{"controls":{"copy":{}}},"source":"<?php\n// track-session.php — start an outbound call, then poll to completion.\n\n$base = \"https://voice.api.sinch.com/v2/projects/\" . getenv(\"PROJECT_ID\");\n$auth = getenv(\"KEY_ID\") . \":\" . getenv(\"KEY_SECRET\");\n\nfunction api(string $method, string $url, string $auth, ?array $body = null, array $extraHeaders = []): array {\n    $ch = curl_init($url);\n    curl_setopt_array($ch, [\n        CURLOPT_CUSTOMREQUEST  => $method,\n        CURLOPT_RETURNTRANSFER => true,\n        CURLOPT_USERPWD        => $auth,\n        CURLOPT_HTTPHEADER     => array_merge([\"Content-Type: application/json\"], $extraHeaders),\n    ]);\n    if ($body !== null) {\n        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));\n    }\n    $resp = curl_exec($ch);\n    curl_close($ch);\n    return json_decode($resp, true) ?: [];\n}\n\n$body = [\n    \"commands\" => [[\n        \"command\"  => \"dial\",\n        \"callName\" => \"tracked\",\n        \"from\" => [\"type\" => \"PHONE\", \"phone\" => [\"number\" => getenv(\"SINCH_NUMBER\")]],\n        \"to\"   => [\"type\" => \"PHONE\", \"phone\" => [\"number\" => getenv(\"DESTINATION_NUMBER\")]],\n        \"dialTimeoutDurationSeconds\" => 30,\n        \"events\" => [\n            \"onAnswer\" => [[\n                \"command\"  => \"messages\",\n                \"messages\" => [\n                    [\"type\" => \"SAY\", \"say\" => [\"text\" => \"This call is being tracked. Goodbye.\", \"voiceName\" => \"Emma\"]],\n                ],\n                \"events\" => [\"onFinish\" => [[\"command\" => \"hangup\"]]],\n            ]],\n            \"onHangup\" => [[\"command\" => \"hangup\"]],\n        ],\n    ]],\n];\n\n// 1. Start the call. Inline events play a short message and hang up.\n$idem = bin2hex(random_bytes(16));\n$start = api(\"POST\", \"$base/calls\", $auth, $body, [\"Idempotency-Key: $idem\"]);\n$sessionId = $start[\"sessionId\"] ?? null;\nif (!$sessionId) {\n    fwrite(STDERR, \"No sessionId in response\\n\");\n    exit(1);\n}\necho \"Started sessionId=$sessionId\\n\";\n\n// 2. Poll until COMPLETED (the only final session state), bounded with backoff.\n$delay = 2; $maxDelay = 15; $attempt = 0; $maxAttempts = 40;\nwhile ($attempt < $maxAttempts) {\n    $state = api(\"GET\", \"$base/sessions/$sessionId\", $auth)[\"state\"] ?? null;\n    echo \"state=$state\\n\";\n    if ($state === \"COMPLETED\") break; // terminal state -> stop\n    $attempt++;\n    sleep($delay);\n    $delay = min($delay * 2, $maxDelay);\n}\n\n// 3. Print per-leg results (callName is NOT returned by the API).\n$calls = api(\"GET\", \"$base/sessions/$sessionId\", $auth)[\"calls\"] ?? [];\n$fields = [\"callId\", \"callResult\", \"callReason\", \"callDurationSeconds\", \"answerTime\"];\nforeach ($calls as $c) {\n    echo json_encode(array_intersect_key($c, array_flip($fields))) . \"\\n\";\n}\n","lang":"php"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"what-success-looks-like","__idx":27},"children":["What success looks like"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Polling a healthy outbound call typically walks the session through"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["QUEUED -> IN_PROGRESS -> COMPLETED"]},", and step 3 prints something like:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{ \"callId\": \"01ARZ...\", \"callResult\": \"COMPLETED\", \"callReason\": \"CALLEE_HANGUP\", \"callDurationSeconds\": 42, \"answerTime\": \"2025-02-10T09:00:05Z\" }\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If nobody answered, you'd instead see ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"callResult\": \"NO_ANSWER\""]}," (no"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callReason"]},"), and the session would still reach ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["COMPLETED"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"real-life-examples","__idx":28},"children":["Real-life examples"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["CRM activity log"]},": write each call's final ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["callResult"]}," to the customer"," ","record when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.hangup"]}," fires."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Operational dashboards"]},": poll ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /sessions/{sessionId}"]}," while a hunt is in"," ","progress to show which leg is currently ringing."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Reconciliation jobs"]},": a nightly job lists"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /calls?startTime=...&endTime=...&callResult=FAILED"]}," and re-triggers failed"," ","reminders. This is also how you back-fill any webhooks you missed."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["SLA reporting"]},": log the delta between ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.incoming"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["call.answered"]}," on"," ","the agent leg for every bridged call."]}]}]},"headings":[{"value":"Track Call Status","id":"track-call-status","depth":1},{"value":"Overview","id":"overview","depth":2},{"value":"Prerequisites","id":"prerequisites","depth":2},{"value":"Setup","id":"setup","depth":2},{"value":"First success: track a call in real time with webhooks (recommended)","id":"first-success-track-a-call-in-real-time-with-webhooks-recommended","depth":2},{"value":"1. Run the receiver","id":"1-run-the-receiver","depth":3},{"value":"Node.js","id":"nodejs","depth":4},{"value":"Python","id":"python","depth":4},{"value":"PHP","id":"php","depth":4},{"value":"2. Point your service at it","id":"2-point-your-service-at-it","depth":3},{"value":"3. Start a call and watch the events arrive","id":"3-start-a-call-and-watch-the-events-arrive","depth":3},{"value":"Events you'll receive","id":"events-youll-receive","depth":3},{"value":"Request format","id":"request-format","depth":3},{"value":"Webhook contract","id":"webhook-contract","depth":3},{"value":"What counts as a failure","id":"what-counts-as-a-failure","depth":4},{"value":"Failover algorithm","id":"failover-algorithm","depth":4},{"value":"Delivery guarantees","id":"delivery-guarantees","depth":4},{"value":"Suppressing webhooks per command","id":"suppressing-webhooks-per-command","depth":3},{"value":"Fallback / on-demand: polling","id":"fallback--on-demand-polling","depth":2},{"value":"Fetch one call by id","id":"fetch-one-call-by-id","depth":3},{"value":"Fetch a session (all legs)","id":"fetch-a-session-all-legs","depth":3},{"value":"List & filter calls","id":"list--filter-calls","depth":3},{"value":"Batch summary","id":"batch-summary","depth":3},{"value":"Recipe: start a call and poll to completion (bounded)","id":"recipe-start-a-call-and-poll-to-completion-bounded","depth":3},{"value":"Node.js","id":"nodejs-1","depth":4},{"value":"Python","id":"python-1","depth":4},{"value":"PHP","id":"php-1","depth":4},{"value":"What success looks like","id":"what-success-looks-like","depth":2},{"value":"Real-life examples","id":"real-life-examples","depth":2}],"frontmatter":{"seo":{"title":"Track Call Status"}},"lastModified":"2026-08-31T15:52:54.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/docs/voice-2.0/tutorials/track-call-status","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}