Skip to content
Last updated

SVAML cheat sheet

The actions and instructions you will actually use. Node.js uses IceSvamlBuilder / PieSvamlBuilder / AceSvamlBuilder. 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. For the authoritative spec, see developers.sinch.com/docs/voice/api-reference/svaml.

The three builders

BuilderWhere usedAvailable actions
Ice*ice handlerFull set: hangup, connectPstn/Sip/Mxp/Conf/Agent, runMenu, park, continue
Pie*pie handlerSame as ICE except no park / answer
Ace*ace handlerOnly continue and hangup

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

Instructions — things to do before the action

say — text to speech

// Node
.say('Welcome to Acme Corp!')
.say('Bonjour!', 'fr-FR')
// C#
.Instructions.Say("Welcome to Acme Corp!")
.Instructions.Say("Bonjour!", "fr-FR")

Default locale: en-US.

play / playFiles — play audio

// Node
.play('https://example.com/greeting.mp3')
.playFiles(['file1.wav', 'file2.wav'], 'en-US')
// 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.

.setCookie('language', 'en-US')
.Instructions.SetCookie("language", "en-US")

sendDtmf — send tones after connecting

.sendDtmf('1234#')
.Instructions.SendDtmf("1234#")

startRecording / stopRecording

// Node
.startRecording({
  destinationUrl: 'https://my-server.com/recordings',
  format: 'mp3',
  notificationEvents: true,
})
.stopRecording()
// C#
.Instructions.StartRecording(new StartRecordingOptions {
    DestinationUrl = "https://my-server.com/recordings",
    Format = "mp3",
    NotificationEvents = true,
})
.Instructions.StopRecording()

Actions — what the call does

hangup

return new IceSvamlBuilder().say('Goodbye!').hangup().build();
return Ok(new IceSvamletBuilder().Instructions.Say("Goodbye!").Action.Hangup().Build());

continue

Let the call proceed to the next callback.

.continue()
.Action.Continue()

connectPstn — connect to a phone number

// 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',
})
// 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

.connectSip('sip:alice@sip.example.com', {
  cli: '+15551234567',
  headers: { 'X-Custom-Header': 'value' },
})
.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

.connectConf('room-42', { moh: 'music', enableDice: true })
.Action.ConnectConf("room-42", new ConnectConfOptions { MusicOnHold = "music" })

connectMxp — connect to a Sinch SDK user

.connectMxp('alice')
.Action.ConnectMxp("alice")

connectAgent — connect to an AI voice agent

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

.connectAgent(AgentProvider.ElevenLabs, 'agent_abc123', { suppressCallbacks: true })
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.

.park('Your call is important to us. Please hold.', { maxDuration: 600 })
.Action.Park("Your call is important to us. Please hold.", new ParkOptions { MaxDuration = 600 })

runMenu — run an IVR menu (PIE fires after)

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)
// 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)
Action syntaxResult 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.

TemplateWhat it doesReturns
business(companyName?)Sales (1), Support (2), Operator (0)'sales', 'support', 'operator'
yesNo(question?)Yes/No confirmation'yes', 'no'
language(languages?)Language selectionlanguage 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 AceSvamlBuilder to connect calls — ACE only supports continue and hangup.
  • Don't use park in a PIE response — ICE only.