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.
| 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#).
// 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.
// Node
.play('https://example.com/greeting.mp3')
.playFiles(['file1.wav', 'file2.wav'], 'en-US')// C#
.Instructions.PlayFiles(new[] { "file1.wav", "file2.wav" }, "en-US")Store a string that survives between ICE, PIE, ACE callbacks on the same call.
.setCookie('language', 'en-US').Instructions.SetCookie("language", "en-US").sendDtmf('1234#').Instructions.SendDtmf("1234#")// 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()return new IceSvamlBuilder().say('Goodbye!').hangup().build();return Ok(new IceSvamletBuilder().Instructions.Say("Goodbye!").Action.Hangup().Build());Let the call proceed to the next callback.
.continue().Action.Continue()// 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('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('room-42', { moh: 'music', enableDice: true }).Action.ConnectConf("room-42", new ConnectConfOptions { MusicOnHold = "music" }).connectMxp('alice').Action.ConnectMxp("alice")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,
})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 })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 syntax | Result in PIE (menuResult.value) |
|---|---|
'return' | The DTMF digit pressed. |
'return(sales)' | 'sales' |
'menu(submenu)' | Navigates to another menu; PIE fires after submenu |
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 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
AceSvamlBuilderto connect calls — ACE only supportscontinueandhangup. - Don't use
parkin a PIE response — ICE only.
- SVAML concept — what SVAML is, why builders
- Voice callbacks — which callbacks return SVAML and when
- developers.sinch.com SVAML spec — full specification with every option