Skip to content

sinch voice

sinch voice is the Voice API v2 surface — calls and services. The older Voice API v1 is still reachable, one level down, under sinch voice v1:

sinch voice
├── calls              — list, get, place, bridge, hang up and patch calls
├── services           — inbound call routing
└── v1                 — Voice API v1
    ├── applications   — callbacks + number assignments
    ├── callouts       — outbound calls (TTS, conference dial-in, custom SVAML)
    ├── calls          — query and control in-progress calls
    └── conferences    — manage conference rooms and participants

The two trees are separate APIs, not two versions of the same commands:

  • Credentials. v2 authenticates as the project with the Access Key ID + Secret. v1 needs voice application credentials (Application Key + Application Secret), which you supply per command with -a, --app-key or through the SINCH_APPLICATION_KEY / SINCH_APPLICATION_SECRET environment variables. See Authentication.
  • Identifiers. v2 call IDs are ULIDs and are not interchangeable with v1 call IDs.
  • Options. The 17 v1 leaf commands accept -a, --app-key <key> where applicable — it defaults to the authenticated app. The 11 v2 leaf commands do not: v2 has no concept of a voice application.
  • Scope. v2 covers list, get, create, bridge, hangup and patch. patch has no v1 equivalent: v1 can end a leg and play into one, but it cannot add a leg to a call that is already up. There is still no v2 equivalent of sinch voice v1 calls manage-leg — v2 addresses legs by name inside a patch, not by a caller/callee position.

The four v1 groups mirror the @sinch/voice SDK's four namespaces.

All 28 leaf commands accept --json for machine-readable output.


sinch voice calls

Place and inspect Voice API v2 calls.

sinch voice calls list

List v2 call records for the project. All filters are optional and combine.

sinch voice calls list [options]
OptionDescription
--service-id <id>Only calls handled by this service
--from <number>Only calls from this origin
--to <number>Only calls to this destination
--call-type <type>PHONE, SIP, STREAM or VOICE_RELAY (upper-cased before sending, so lowercase is fine)
--call-result <result>QUEUED, INITIATED, IN_PROGRESS, COMPLETED, REJECTED, NO_ANSWER, CANCEL, BUSY or FAILED (also upper-cased)
--start-time <iso>Calls that started at or after this RFC 3339 timestamp
--end-time <iso>Calls that ended before this RFC 3339 timestamp (exclusive)
-l, --limit <number>Page size, 1–100 (default 20)
--page <number>Page number, 1-based (default 1)
--jsonOutput as JSON

The API pages at 20, so a bare list on a busy project shows one page. The table output prints the total call and page count underneath; --json prints the calls array only, without those totals.

Example

sinch voice calls list --service-id svc-abc123 --call-result COMPLETED --limit 100

sinch voice calls get

Get one v2 call record. The table prints the whole call resource — project, service, session, bridge and batch IDs, direction, type, origination type, endpoints, result and reason, timestamps, duration, the per-minute rate and the call's resource URL. --json emits the API object untouched.

sinch voice calls get <call-id> [--json]
OptionDescription
<call-id>Call ID (ULID), as shown by sinch voice calls list
--jsonOutput as JSON

This is not sinch voice v1 calls get, which takes a v1 call ID. It is also not the sessionId returned by calls create — see below.

sinch voice calls create

Place a v2 call, optionally bridging a media stream or the Voice Relay onto it.

sinch voice calls create <to> [options]
OptionDescription
<to>Destination: an E.164 number (+15551234567) or a sip: / sips: URI
--from <origin>Caller identity presented to the callee (E.164 or SIP address)
--stream <endpoint>Bridge a WebSocket audio leg (wss://…). Mutually exclusive with --relay
--relay <endpoint>Bridge a Voice Relay leg — platform STT/TTS (wss://…). Mutually exclusive with --stream
--sample-rate <hz>Stream sample rate: 8000, 16000, 24000 or 48000 (default 16000; --stream only)
--tts-voice <name>Platform TTS voice (default Emma; --relay only)
--stt-language <tag>Speech-to-text BCP-47 tag (default en-US; --relay only)
--no-interruptionsDisable barge-in over the platform TTS (--relay only). Omit it and the platform default stands
--header <key=value>Custom call header, repeatable. Only the first = splits, so values may contain =
--bridge-name <name>Name of the bridge joining the two legs (default bridge)
--timeout <seconds>Seconds to wait for an answer (max 60)
--max-duration <seconds>Maximum call duration in seconds (max 14400)
--dry-runPrint the request body that would be sent, without placing a billable call
--jsonOutput as JSON

--stream and --relay take a full ws:// / wss:// URL when run from the CLI. The relative /path form the underlying package accepts is resolved against a function's public URL, which the CLI does not have — passing a path here fails with "this function's public URL is unknown".

Naming no events (no --stream, no --relay) hands the call to the webhook configured on the Voice service (callBehavior: WEBHOOK). --stream and --relay name events, so those calls are answered inline instead.

Example

sinch voice calls create +15551234567 --from +15559876543
sinch voice calls create +15551234567 --stream wss://example.com/audio --sample-rate 8000
sinch voice calls create +15551234567 --relay wss://example.com/relay --tts-voice Emma
sinch voice calls create sip:agent@pbx.example.com --dry-run

sinch voice calls bridge

Call two parties and bridge them together. <a> is dialled first; <b> only once <a> answers.

sinch voice calls bridge <a> <b> [options]
OptionDescription
<a>First party, dialled first: an E.164 number or a sip: / sips: URI
<b>Second party, dialled once <a> answers
--from <origin>Caller identity presented to both parties
--bridge-name <name>Name of the bridge joining the two legs (default bridge)
--timeout <seconds>Seconds to wait for an answer (max 60)
--max-duration <seconds>Maximum call duration in seconds (max 14400)
--dry-runPrint the request body, without placing a billable call
--jsonOutput as JSON

The two legs' lifetimes are tied together: either one hanging up, and every way <b> fails to connect, ends the other. The bridge name matters afterwards — calls patch --dial joins the same bridge and defaults to the same name, so a bridge left unnamed here needs no --bridge-name there either.

Example

sinch voice calls bridge +15551110000 +15552220000 --from +15559876543
sinch voice calls bridge +15551110000 sip:agent@pbx.example.com --dry-run

sinch voice calls hangup

End one or more legs of a live call.

sinch voice calls hangup <target> [options]
OptionDescription
<target>Call ID, or a session ID when --call-name is given
--call-name <name>Leg the commands run in — makes <target> a session ID
--leg <name>Leg to end, repeatable. Ends the addressed leg when omitted
--dry-runPrint the commands that would be sent, without touching the call
--jsonOutput as JSON

Ending a bridged leg does not necessarily end the call. Legs placed by this CLI tie their lifetimes together, so ending one usually takes the other with it — but a leg added later by calls patch may not. Name every leg you mean to end.

Example

sinch voice calls hangup 01JD3Q2XYZ
sinch voice calls hangup sess-abc123 --call-name origin --leg origin --leg stream

sinch voice calls patch

Run commands against a live call. --dial is the capability v1 has no equivalent of: it rings a third party and joins them to the bridge the call is already using, so everything already on that bridge — the other party, a media stream, the Voice Relay — stays attached through the handover. That is a transfer without dropping anyone, and it is the same operation a voice agent runs when it escalates to a human.

sinch voice calls patch <target> [options]
OptionDescription
<target>Call ID, or a session ID when --call-name is given
--call-name <name>Leg the commands run in — makes <target> a session ID
--dial <destination>Add a party to the call: an E.164 number or a sip: / sips: URI
--end-leg <name>Leg to end if --dial never answers, or hangs up afterwards. Strongly recommended
--dial-leg <name>Name for the leg --dial creates (default transfer)
--bridge-name <name>Bridge the live legs are already in (default bridge)
--from <origin>Caller identity presented to the dialled party
--timeout <seconds>Seconds to wait for --dial to answer (default 30, max 60)
--commands <source>Raw v2 command list: inline JSON, @file.json, or - for stdin
--dry-runPrint the commands that would be sent, without touching the call
--jsonOutput as JSON

Exactly one of --dial or --commands is required. --commands is the escape hatch for anything the flags do not cover; it is passed through unvalidated, so the API is what rejects a malformed command.

Pass --end-leg

Naming any outcome inline detaches the service webhook from all of them. So if --dial is busy or never answers, nothing at all runs, and whoever was just told they are being put through sits on a live, billed call with nobody on the other side of the bridge. --end-leg names the leg to end in that case — usually the leg you addressed.

Addressing a live call

v2 identifies a live call two ways, and they are alternatives rather than a pair:

FormSends to
patch <call-id>PATCH /calls/{callId}
patch <session-id> --call-name <leg>PATCH /sessions/{sessionId}/calls/{callName}

calls create and calls bridge print a session ID; calls list prints call IDs.

Example

sinch voice calls patch sess-abc123 --call-name origin --dial +15550001111 --end-leg origin
sinch voice calls patch 01JD3Q2XYZ --dial sip:agent@pbx.example.com --bridge-name agent
echo '[{"command":"hangup"}]' | sinch voice calls patch 01JD3Q2XYZ --commands -

sessionId is not callId

POST /calls — what calls create sends — returns a sessionId (plus serviceId, and batchId where applicable). calls get takes a callId, a ULID. They are different identifiers, and feeding the sessionId to calls get returns 404. To find the call the session produced:

sinch voice calls list --service-id <service-id>

SIP destinations need the scheme — the inverse of v1

The v2 sip.endpoint schema is pattern: "^sips?:", so the scheme is required: sip:agent@pbx.example.com. A bare agent@pbx.example.com is rejected.

Voice v1 is the exact opposite. sinch voice v1 callouts wants a bare address and fails a scheme-prefixed one with CONGESTION. Nothing on the wire distinguishes these two cases, so a working v1 destination string copied into voice calls create (or the reverse) fails, and the error does not say why.

The scheme also picks the transport when none is sent explicitly: sip: means UDP, sips: means TLS. --dry-run shows no transport key in that case, because the server is applying the default rather than the CLI.


sinch voice services

A service is the v2 object that owns inbound call routing. A function opts into it by declaring VOICE_SERVICE_ID; sinch functions deploy then points the service's webhook at the deployed function.

Voice v2 signs webhooks with Authorization: service <serviceId>:<HMAC-SHA256> plus an x-timestamp header. The runtime verifies the signature once VOICE_SERVICE_SECRET is set; until then it allows the callback through with a warning, because Sinch does not yet expose the per-service secret.

sinch voice services list

List the v2 services in this project — IDs, which one is the default, and where each one routes inbound calls.

sinch voice services list [--json]

GET /services does not return callBehavior, so the command reads each service back to fill the webhook column in. --json adds a derived webhookUrl alongside the untouched callBehavior, so a script can read the URL without walking the union.

sinch voice services get

Get one service, including its full callBehavior — the webhook URL and fallback, or the static SVAML script.

sinch voice services get <service-id> [--json]
OptionDescription
<service-id>Service ID, as shown by sinch voice services list
--jsonOutput as JSON, exactly as the API returned it

sinch voice services create

Create a service.

sinch voice services create [options]
OptionDescription
--name <name>Service name. Prompts if omitted and interactive
--description <text>Service description
--webhook-url <url>Inbound call webhook URL (sets callBehavior to WEBHOOK)
--jsonOutput as JSON (also implies non-interactive — --name is required)
--non-interactiveSkip prompts, fail if --name is missing

Without --webhook-url the service is created with no webhook, and inbound calls to it reach nothing — the command warns and prints the services update line to fix it.

Example

sinch voice services create --name "Support line" --webhook-url https://my-function.fn.sinch.com

sinch voice services update

Point a service's inbound webhook at a URL. This is the same call sinch functions deploy makes for a function that declares VOICE_SERVICE_ID, and it sets callBehavior to WEBHOOK — the mode inbound calls require.

sinch voice services update <service-id> --webhook-url <url> [--json]
OptionDescription
<service-id>Service ID
--webhook-url <url>Inbound call webhook URL (required)
--jsonOutput as JSON

sinch voice services delete

Delete a service. Prompts for confirmation by default; every function routed through the service stops receiving inbound calls.

sinch voice services delete [service-id] [--force] [--non-interactive] [--json]
OptionDescription
[service-id]Service ID. Prompts with a picker when omitted and interactive
--forceSkip the confirmation prompt
--jsonOutput as JSON (also implies non-interactive — the ID is required)
--non-interactiveSkip prompts, fail if the service ID is missing

sinch voice v1 applications

sinch voice v1 applications callbacks get

Get the webhook callback URLs configured for a Voice application.

sinch voice v1 applications callbacks get [options]
OptionDescription
-a, --app-key <key>Application key (defaults to the authenticated app)
--jsonOutput as JSON
--curlAlso print the equivalent curl command

sinch voice v1 applications callbacks set

Set the primary (and optionally fallback) callback URL for a Voice application.

sinch voice v1 applications callbacks set [url] [options]
OptionDescription
[url]Primary URL. Prompts if omitted and interactive.
-a, --app-key <key>Application key
--primary-onlyOnly set the primary URL, leave fallback unset
--fallback <url>Explicit fallback URL (default: same as primary)
--non-interactiveFail if URL is missing instead of prompting
--debugPrint full error details on failure
--jsonOutput as JSON

Example

sinch voice v1 applications callbacks set https://my-function.fn.sinch.com

sinch voice v1 applications numbers

NOTE: These commands manage numbers attached to a Voice application — i.e. which DIDs route to this app's webhook. This is not the same as the account-wide number inventory managed by sinch numbers.

sinch voice v1 applications numbers list

List voice-capable numbers on your account and their current application assignments. Pass -a to filter client-side to numbers bound to a specific app.

sinch voice v1 applications numbers list [options]
OptionDescription
-a, --app-key <key>Filter to numbers currently routed to this app
--jsonOutput as JSON

sinch voice v1 applications numbers get

Query metadata (country, type, rate) for any phone number. Returns number details, not app-routing information.

sinch voice v1 applications numbers get <number> [--json]

sinch voice v1 applications numbers assign

Assign one or more numbers to a Voice application.

sinch voice v1 applications numbers assign <numbers...> [-a <key>] [--capability voice|sms] [--json]

sinch voice v1 applications numbers unassign

Un-assign a number from a Voice application.

sinch voice v1 applications numbers unassign <number> [-a <key>] [--capability voice|sms] [--json]

sinch voice v1 callouts

Initiate outbound voice calls.

sinch voice v1 callouts tts

Place a text-to-speech outbound call.

sinch voice v1 callouts tts <destination> <text> [options]
OptionDescription
<destination>Phone number (+15551234567), sip:addr, or bare username
<text>Text to speak (≤600 characters)
--voice <locale>TTS voice or locale (default en-US)
--cli <number>Caller ID to display to the callee
--dtmf <seq>DTMF tones after pickup (0-9, #, w = 500ms pause)
--domain <domain>pstn or mxp (default: inferred from destination)
--enable-aceRequest an ACE callback when the call is answered
--enable-diceRequest a DiCE callback when the call disconnects
--enable-pieRequest a PIE callback after menu interaction
--jsonOutput as JSON

Example

sinch voice v1 callouts tts +15551234567 "Your package is arriving today" --voice en-US

sinch voice v1 callouts conference

Call a phone number and connect them to a conference room. Creates the conference if it doesn't exist.

sinch voice v1 callouts conference <destination> <conference-id> [options]
OptionDescription
--cli <number>Caller ID
--greeting <text>Spoken greeting before joining
--locale <locale>TTS locale for the greeting
--moh <music>Music on hold while alone: ring, music1, music2, music3
--max-duration <seconds>Maximum call duration
--dtmf <seq>DTMF tones after pickup
--domain <domain>pstn or mxp
--jsonOutput as JSON

sinch voice v1 callouts custom

Place a custom callout driven by inline SVAML (Sinch Voice Application Markup Language). At least one of --ice, --ace, or --pie is required.

sinch voice v1 callouts custom [destination] [options]
OptionDescription
--ice <svaml>ICE SVAML: inline JSON string or @path/to/file.json
--ace <svaml>ACE SVAML: inline JSON or @file.json
--pie <svaml>PIE SVAML: inline JSON or @file.json
--cli <number>Caller ID
--dtmf <seq>DTMF tones after pickup
--max-duration <seconds>Maximum call duration
--jsonOutput as JSON

Example

sinch voice v1 callouts custom +15551234567 --ice @./ice-payload.json

sinch voice v1 calls

Query and control in-progress calls. These methods apply to PSTN / SIP calls only.

NOTE — this is not sinch voice calls. Both trees have a calls get, and they take different identifier types. sinch voice v1 calls get takes a v1 call ID (the leg ID from a v1 callout, ICE/ACE/DiCE callback, or conferences get). sinch voice calls get takes a ULID from sinch voice calls list. An ID from one API returns 404 on the other. Both trees can control a live call, and v2 can do one thing v1 cannot: sinch voice calls patch --dial adds a party to a call that is already up. See sinch voice calls.

sinch voice v1 calls get

Get information about an ongoing or completed call.

sinch voice v1 calls get <call-id> [--json]

sinch voice v1 calls hangup

Hang up an in-progress call (thin wrapper around voice v1 calls update with the hangup SVAML action).

sinch voice v1 calls hangup <call-id> [--json]

sinch voice v1 calls update

Send a SVAML body to an in-progress call.

sinch voice v1 calls update <call-id> --svaml <json-or-@file> [--json]
OptionDescription
<call-id>The unique call ID
--svaml <svaml>SVAML body: inline JSON or @path/to/file.json (required)
--jsonOutput as JSON

SVAML body shape

The body is a JSON object with two fields:

  • actionrequired. The top-level call-control directive that runs after all instructions finish. An empty or missing action is rejected with errorCode: 40001 "SVAML 'action' must not be empty."
  • instructions — optional. An array of things to run before the action (say text, play a file, start recording, etc.).

Actions verified against calls update

These are actions confirmed against a live call on a PSTN leg. The endpoint accepts only a narrow subset of the full SVAML action list — actions that re-route a call (for example connectPstn, connectMxp, connectConf) are rejected with errorCode: 40003 "Unknown SVAML action '<name>'" and are intended for initial-call routing via ICE callback responses, not for runtime updates of a connected call.

ActionWorks?Notes
{"name": "hangup"}Terminates the call. calls get afterwards shows reason: MANAGERHANGUP.
{"name": "continue"}No-op "keep going" action. Used as a placeholder when instructions do the real work.
{"name": "Say", ...}Say is an instruction, not an action. See example below for the correct wrapping.
{"name": "connectPstn", ...}Re-routing is not supported on runtime updates. Use callouts custom to place a new leg instead.
{"name": "park"}Not recognized on this endpoint.

Case variants of connectPstn (ConnectPstn, ConnectPSTN, connectPSTN) all return the same 40003 error — the server preserves the name verbatim in the response.

Example: hang up a live call

sinch voice v1 calls update <call-id> --svaml '{"action":{"name":"hangup"}}'

Or equivalently, the thin wrapper:

sinch voice v1 calls hangup <call-id>

Example: say text to a live call mid-flight

Say is an instruction and must be wrapped with a required action. The minimal "speak and keep going" shape:

sinch voice v1 calls update <call-id> --svaml '{
  "instructions": [
    {"name": "Say", "text": "Your call is being transferred.", "locale": "en-US"}
  ],
  "action": {"name": "continue"}
}'

Note on conference audio paths: If the target call is a leg inside a conference, the server will accept this request (HTTP 200) but the Say audio may not be audible to the participant because the leg is mixed through the conference audio path. To play audio to a specific conference participant, use sinch voice v1 calls manage-leg with --leg callee instead (that endpoint is designed for per-leg audio injection).

sinch voice v1 calls manage-leg

Inject audio into a specific leg of a connected call. Intended for playing sound or speaking to one side of a PSTN / SIP call without affecting the other, including inside a conference.

sinch voice v1 calls manage-leg <call-id> --leg caller|callee|both --svaml <json-or-@file> [--json]
OptionDescription
<call-id>The unique call ID
--leg <leg>Which leg receives the audio: caller, callee, or both (required)
--svaml <svaml>SVAML body with PlayFiles or Say instructions (required)
--jsonOutput as JSON

The manage-leg endpoint is the SVAML-injection counterpart to calls update: instead of controlling call-control state, it plays audio into a specific leg's RTP path. Per the Sinch API docs only PlayFiles and Say instructions are valid here; other instruction types will be rejected by the server.

Known gap: The exact request-body layout for Say and PlayFiles on this endpoint was not fully reverse-engineered during the voice CLI rewrite's smoke testing — several payload variants returned 400 Bad Request. See the Sinch SVAML reference at </docs/voice/api-reference/svaml/> for the authoritative shapes. Known-working examples will be added here as they are confirmed in production.


sinch voice v1 conferences

Manage conference rooms and their participants. Participants are identified by their per-leg call ID (from conferences get), not by phone number.

sinch voice v1 conferences get

Get the participant list for a conference.

sinch voice v1 conferences get <conference-id> [--json]

sinch voice v1 conferences kick

Remove a single participant from a conference.

sinch voice v1 conferences kick <conference-id> <call-id> [--json]

sinch voice v1 conferences kick-all

Remove all participants from a conference. Prompts for confirmation by default.

sinch voice v1 conferences kick-all <conference-id> [--yes] [--non-interactive] [--json]

sinch voice v1 conferences manage

Mute/unmute or hold/resume a conference participant. Exactly one action flag is required.

sinch voice v1 conferences manage <conference-id> <call-id> [--mute|--unmute|--hold|--resume] [--moh <music>] [--json]

To play audio or speak text to a participant (as opposed to mute/hold them), use sinch voice v1 calls manage-leg instead — those operations hit a different SDK method.


Authentication

sinch voice calls and sinch voice services authenticate as the project with the Access Key ID + Access Key Secret — the same credentials .env exposes as PROJECT_ID_API_KEY / PROJECT_ID_API_SECRET, stored by sinch auth login. calls create sends them as HTTP Basic; the read and CRUD endpoints (calls list/get, services *) exchange them for an OAuth2 token like the CLI's other REST clients. No application key is involved, which is why none of these commands takes -a, --app-key.

sinch voice v1 commands (applications, callouts, calls, conferences) use the Application Key + Application Secret instead. sinch auth login no longer asks for that pair, so supply it per command with -a, --app-key <key>, or set SINCH_APPLICATION_KEY and SINCH_APPLICATION_SECRET in the environment.

Voice templates published by the template registry are Voice v2. They route inbound calls through a service rather than a voice application, so they carry VOICE_SERVICE_ID and no application key pair.

We'd love to hear from you!
Rate this content: