# SVAML cheat sheet

The actions and instructions you will actually use. Node.js uses `createIceBuilder` / `createPieBuilder` / `createAceBuilder`. C# uses the equivalent `*Builder` classes with `.Instructions.*` and `.Action.*` accessors.

For the **concept** of SVAML (what it is, when it matters), see [the SVAML concept page](/docs/functions/functions/concepts/svaml). For the **authoritative spec**, see [developers.sinch.com/docs/voice/api-reference/svaml](/docs/voice/api-reference/svaml/).

## The three builders

| Builder | Where used | Available actions |
|  --- | --- | --- |
| `Ice*` | `ice` handler | Full set: `hangup`, `connectPstn/Sip/Mxp/Conf/Agent`, `runMenu`, `park`, `continue` |
| `Pie*` | `pie` handler | Same as ICE except no `park` / `answer` |
| `Ace*` | `ace` handler | Only `continue` and `hangup` |


Every builder chain ends with `.build()` (Node) or `.Build()` (C#).

## Instructions — things to do before the action

### `say` — text to speech

```typescript
// Node
.say('Welcome to Acme Corp!')
.say('Bonjour!', 'fr-FR')
```

```csharp
// C#
.Instructions.Say("Welcome to Acme Corp!")
.Instructions.Say("Bonjour!", "fr-FR")
```

Default locale: `en-US`.

### `play` / `playFiles` — play audio

```typescript
// Node
.play('https://example.com/greeting.mp3')
.playFiles(['file1.wav', 'file2.wav'], 'en-US')
```

```csharp
// C#
.Instructions.PlayFiles(new[] { "file1.wav", "file2.wav" }, "en-US")
```

### `setCookie` — persist a value across callbacks

Store a string that survives between ICE, PIE, ACE callbacks on the same call.

```typescript
.setCookie('language', 'en-US')
```

```csharp
.Instructions.SetCookie("language", "en-US")
```

### `sendDtmf` — send tones after connecting

```typescript
.sendDtmf('1234#')
```

```csharp
.Instructions.SendDtmf("1234#")
```

### `startRecording` / `stopRecording`

```typescript
// Node
.startRecording({
  destinationUrl: 'https://my-server.com/recordings',
  format: 'mp3',
  notificationEvents: true,
})
.stopRecording()
```

```csharp
// C#
.Instructions.StartRecording(new StartRecordingOptions {
    DestinationUrl = "https://my-server.com/recordings",
    Format = "mp3",
    NotificationEvents = true,
})
.Instructions.StopRecording()
```

## Actions — what the call does

### `hangup`

```typescript
return createIceBuilder().say('Goodbye!').hangup().build();
```

```csharp
return Ok(new IceSvamletBuilder().Instructions.Say("Goodbye!").Action.Hangup().Build());
```

### `continue`

Let the call proceed to the next callback.

```typescript
.continue()
```

```csharp
.Action.Continue()
```

### `connectPstn` — connect to a phone number

```typescript
// Node — simple
.connectPstn('+15551234567')

// Node — with options (all IntelliSense-discoverable)
.connectPstn('+15551234567', {
  cli: data.cli,            // show caller's number
  maxDuration: 3600,
  timeout: 30,
  enableAce: true,          // fire ACE when answered
  enableDice: true,
  amd: { enabled: true },   // answering machine detection
  indications: 'us',
})
```

```csharp
// C# — simple
.Action.ConnectPstn("+15551234567")

// C# — with options
.Action.ConnectPstn("+15551234567", new ConnectPstnOptions {
    Cli = data.Cli,
    MaxDuration = 3600,
    ConnectTimeout = 30,
    Indications = "us",
})
```

### `connectSip` — connect to a SIP URI

```typescript
.connectSip('sip:alice@sip.example.com', {
  cli: '+15551234567',
  headers: { 'X-Custom-Header': 'value' },
})
```

```csharp
.Action.ConnectSip("sip:alice@sip.example.com", new ConnectSipOptions {
    Cli = "+15551234567",
    Transport = "TLS",
    Headers = new Dictionary<string, string> { ["X-Custom"] = "value" },
})
```

### `connectConf` — join a conference

```typescript
.connectConf('room-42', { moh: 'music', enableDice: true })
```

```csharp
.Action.ConnectConf("room-42", new ConnectConfOptions { MusicOnHold = "music" })
```

### `connectMxp` — connect to a Sinch SDK user

```typescript
.connectMxp('alice')
```

```csharp
.Action.ConnectMxp("alice")
```

### `connectAgent` — connect to an AI voice agent

```typescript
import { AgentProvider } from '@sinch/functions-runtime';

.connectAgent(AgentProvider.ElevenLabs, 'agent_abc123', { suppressCallbacks: true })
```

```csharp
using SinchFunctions.AI;

.Action.ConnectAgent(AgentProvider.ElevenLabs, "agent_abc123", new ConnectAgentOptions {
    SuppressCallbacks = true,
})
```

### `park` — put the caller on hold

ICE only. Not available on PIE.

```typescript
.park('Your call is important to us. Please hold.', { maxDuration: 600 })
```

```csharp
.Action.Park("Your call is important to us. Please hold.", new ParkOptions { MaxDuration = 600 })
```

### `runMenu` — run an IVR menu (PIE fires after)

```typescript
import { createMenu, MenuTemplates } from '@sinch/functions-runtime';

// Template
.runMenu(MenuTemplates.business('Acme Corp'))

// Custom
const menu = createMenu()
  .prompt('Press 1 for sales, press 2 for support.')
  .option('1', 'return(sales)')
  .option('2', 'return(support)')
  .maxDigits(1)
  .timeout(8000)
  .build();
.runMenu(menu)
```

```csharp
// Template
.Action.RunMenu(MenuTemplates.Business("Acme Corp"))

// Custom
var menu = new MenuBuilder()
    .Prompt("Press 1 for sales, press 2 for support.")
    .Option("1", "return(sales)")
    .Option("2", "return(support)")
    .MaxDigits(1)
    .Timeout(8000)
    .Build();
.Action.RunMenu(menu)
```

## Menu option action values

| Action syntax | Result in PIE (`menuResult.value`) |
|  --- | --- |
| `'return'` | The DTMF digit pressed. |
| `'return(sales)'` | `'sales'` |
| `'menu(submenu)'` | Navigates to another menu; PIE fires after submenu |


## Pre-built menu templates

Import from the runtime package. Available in both languages unless noted.

| Template | What it does | Returns |
|  --- | --- | --- |
| `business(companyName?)` | Sales (1), Support (2), Operator (0) | `'sales'`, `'support'`, `'operator'` |
| `yesNo(question?)` | Yes/No confirmation | `'yes'`, `'no'` |
| `language(languages?)` | Language selection | language value strings |
| `afterHours(name?, hours?)` | After-hours message | `'voicemail'`, `'website'`, `'emergency'` |
| `recordingConsent()` | Recording consent prompt | `'consent'`, `'no_consent'` |
| `numericInput(prompt, digits?)` | Collect multi-digit input (Node only) | the entered digits |
| `CallbackRequest(waitTime?)` | Hold or callback (C# only) | `'hold'`, `'callback'` |
| `CustomerService()` | 4-option customer service (C# only) | `'billing'`, `'technical'`, … |
| `SatisfactionSurvey()` | Post-call survey (C# only) | `'excellent'`, `'good'`, `'fair'`, `'poor'` |


## Don't

- **Don't** return raw JSON objects from voice handlers — always `.build()` through a builder.
- **Don't** call `.build()` without setting an action — it throws.
- **Don't** add instructions after `.build()` — the builder is frozen.
- **Don't** use `createAceBuilder` to connect calls — ACE only supports `continue` and `hangup`.
- **Don't** use `park` in a PIE response — ICE only.


## Related

- [SVAML concept](/docs/functions/functions/concepts/svaml) — what SVAML is, why builders
- [Voice callbacks](/docs/functions/functions/concepts/voice-callbacks) — which callbacks return SVAML and when
- [developers.sinch.com SVAML spec](/docs/voice/api-reference/svaml/) — full specification with every option