# 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](#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.

```sh
sinch voice calls list [options]
```

| Option | Description |
|  --- | --- |
| `--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) |
| `--json` | Output 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**

```sh
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.

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

| Option | Description |
|  --- | --- |
| `<call-id>` | Call ID (**ULID**), as shown by `sinch voice calls list` |
| `--json` | Output 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.

```sh
sinch voice calls create <to> [options]
```

| Option | Description |
|  --- | --- |
| `<to>` | Destination: an E.164 number (`+15551234567`) or a `sip:` / `sips:` URI. Prompted if omitted |
| `--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`, `--say`, `--flow` |
| `--relay <endpoint>` | Bridge a Voice Relay leg — platform STT/TTS (`wss://…`). Mutually exclusive with the above |
| `--say [text]` | Speak text on answer, then hang up. Prompts for text if the value is omitted |
| `--flow <source>` | Raw v2 command list to run on answer: inline JSON, `@file.json`, or `-` for stdin |
| `--sample-rate <hz>` | Stream sample rate: `8000`, `16000`, `24000` or `48000` (default `16000`; `--stream` only) |
| `--tts-voice <name>` | Platform TTS voice (default `Emma`; `--say` / `--relay` only) |
| `--stt-language <tag>` | Speech-to-text BCP-47 tag (default `en-US`; `--relay` only) |
| `--no-interruptions` | Disable 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-run` | Print the request body that would be sent, **without placing a billable call** |
| `--json` | Output as JSON |
| `--non-interactive` | Skip prompts, fail if `<to>` or `--say` text is not provided |


`--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`, `--relay`, `--say` or `--flow`) hands the call to the webhook configured on the Voice service (`callBehavior: WEBHOOK`). The other four each name events, so those calls are answered inline instead — and are mutually exclusive with each other, since only one thing can answer a call.

`--say` is the v2 counterpart of `sinch voice v1 callouts tts`: place a call, speak text once it answers, then hang up. `--flow` is the same idea for a raw v2 command list — `--say` is shorthand for `--flow` with a say-then-hangup list.

**Example**

```sh
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 +15551234567 --say "Hello from Sinch" --tts-voice Emma
sinch voice calls create +15551234567 --flow @ivr.json
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.

```sh
sinch voice calls bridge <a> <b> [options]
```

| Option | Description |
|  --- | --- |
| `<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-run` | Print the request body, **without placing a billable call** |
| `--json` | Output 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**

```sh
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.

```sh
sinch voice calls hangup <target> [options]
```

| Option | Description |
|  --- | --- |
| `<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-run` | Print the commands that would be sent, without touching the call |
| `--json` | Output 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**

```sh
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.

```sh
sinch voice calls patch <target> [options]
```

| Option | Description |
|  --- | --- |
| `<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) |
| `--flow <source>` | Raw v2 command list: inline JSON, `@file.json`, or `-` for stdin |
| `--dry-run` | Print the commands that would be sent, without touching the call |
| `--json` | Output as JSON |


Exactly one of `--dial` or `--flow` is required. `--flow` 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:

| Form | Sends 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**

```sh
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 --flow -
```

### `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:

```sh
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`; the Functions API then points the service's webhook at `{functionUrl}/webhook/voice` server-side, as part of `sinch functions deploy` itself.

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.

```sh
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.

```sh
sinch voice services get <service-id> [--json]
```

| Option | Description |
|  --- | --- |
| `<service-id>` | Service ID, as shown by `sinch voice services list` |
| `--json` | Output as JSON, exactly as the API returned it |


### sinch voice services create

Create a service.

```sh
sinch voice services create [options]
```

| Option | Description |
|  --- | --- |
| `--name <name>` | Service name. Prompts if omitted and interactive |
| `--description <text>` | Service description |
| `--webhook-url <url>` | Inbound call webhook URL (sets `callBehavior` to `WEBHOOK`) |
| `--json` | Output as JSON (also implies non-interactive — `--name` is required) |
| `--non-interactive` | Skip 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**

```sh
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, setting `callBehavior` to `WEBHOOK` — the mode inbound calls require. `sinch functions deploy` does not call this itself: the Functions API points `VOICE_SERVICE_ID` at the deployed function server-side. Use this for anything else — a manual URL, or a service with no function deployed behind it yet.

```sh
sinch voice services update <service-id> --webhook-url <url> [--json]
```

| Option | Description |
|  --- | --- |
| `<service-id>` | Service ID |
| `--webhook-url <url>` | Inbound call webhook URL (**required**) |
| `--json` | Output as JSON |


### sinch voice services delete

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

```sh
sinch voice services delete [service-id] [--force] [--non-interactive] [--json]
```

| Option | Description |
|  --- | --- |
| `[service-id]` | Service ID. Prompts with a picker when omitted and interactive |
| `--force` | Skip the confirmation prompt |
| `--json` | Output as JSON (also implies non-interactive — the ID is required) |
| `--non-interactive` | Skip 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.

```sh
sinch voice v1 applications callbacks get [options]
```

| Option | Description |
|  --- | --- |
| `-a, --app-key <key>` | Application key (defaults to the authenticated app) |
| `--json` | Output as JSON |
| `--curl` | Also print the equivalent curl command |


### sinch voice v1 applications callbacks set

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

```sh
sinch voice v1 applications callbacks set [url] [options]
```

| Option | Description |
|  --- | --- |
| `[url]` | Primary URL. Prompts if omitted and interactive. |
| `-a, --app-key <key>` | Application key |
| `--primary-only` | Only set the primary URL, leave fallback unset |
| `--fallback <url>` | Explicit fallback URL (default: same as primary) |
| `--non-interactive` | Fail if URL is missing instead of prompting |
| `--debug` | Print full error details on failure |
| `--json` | Output as JSON |


**Example**

```sh
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.

```sh
sinch voice v1 applications numbers list [options]
```

| Option | Description |
|  --- | --- |
| `-a, --app-key <key>` | Filter to numbers currently routed to this app |
| `--json` | Output as JSON |


#### sinch voice v1 applications numbers get

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

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

#### sinch voice v1 applications numbers assign

Assign one or more numbers to a Voice application.

```sh
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.

```sh
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.

```sh
sinch voice v1 callouts tts <destination> <text> [options]
```

| Option | Description |
|  --- | --- |
| `<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-ace` | Request an ACE callback when the call is answered |
| `--enable-dice` | Request a DiCE callback when the call disconnects |
| `--enable-pie` | Request a PIE callback after menu interaction |
| `--json` | Output as JSON |


**Example**

```sh
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.

```sh
sinch voice v1 callouts conference <destination> <conference-id> [options]
```

| Option | Description |
|  --- | --- |
| `--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` |
| `--json` | Output 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.

```sh
sinch voice v1 callouts custom [destination] [options]
```

| Option | Description |
|  --- | --- |
| `--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 |
| `--json` | Output as JSON |


**Example**

```sh
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-calls).


### sinch voice v1 calls get

Get information about an ongoing or completed call.

```sh
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).

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

### sinch voice v1 calls update

Send a SVAML body to an in-progress call.

```sh
sinch voice v1 calls update <call-id> --svaml <json-or-@file> [--json]
```

| Option | Description |
|  --- | --- |
| `<call-id>` | The unique call ID |
| `--svaml <svaml>` | SVAML body: inline JSON or `@path/to/file.json` (**required**) |
| `--json` | Output as JSON |


#### SVAML body shape

The body is a JSON object with two fields:

- `action` — **required**. 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.

| Action | Works? | 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

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

Or equivalently, the thin wrapper:

```sh
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:

```sh
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.

```sh
sinch voice v1 calls manage-leg <call-id> --leg caller|callee|both --svaml <json-or-@file> [--json]
```

| Option | Description |
|  --- | --- |
| `<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**) |
| `--json` | Output 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.

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

### sinch voice v1 conferences kick

Remove a single participant from a conference.

```sh
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.

```sh
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.

```sh
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.