Variables

How to use variables in SWML scripts
View as MarkdownOpen in Claude

SWML provides a variable system for accessing call or message information, storing intermediate state, and passing data between sections. This page covers how to use variables: syntax, scopes, accessing nested fields, and deployment-mode differences.

For the authoritative field-by-field reference of what each scope contains, see the per-flavor overviews:

For JavaScript expressions on top of variables, see Expressions. For template transformation functions, see Template Functions.

Syntax

Wrap a variable name in ${...} or %{...} anywhere inside a string value, and SignalWire substitutes the current value when the script runs. Use it to personalize a greeting with the caller’s number, pass a request body through to your backend, or pick a reply based on the inbound message body.

The two flavors of SWML accept slightly different forms.

Calling

In a Calling document, ${...} and %{...} are interchangeable — pick whichever reads more naturally in your YAML or JSON.

Reference a value. Use a plain path like ${call.from} or %{vars.user_choice}. Works for any field on call, params, envs, or vars. If the path is valid but the value isn’t set, the placeholder resolves to an empty string.

If the placeholder body isn’t a plain path — for example, it includes operators, method calls, or other JavaScript — it’s evaluated as a JavaScript expression instead. See Expressions.

1version: 1.0.0
2sections:
3 main:
4 - play:
5 url: 'say: This call is from ${call.from}'

Messaging

A Messaging document uses %{...} only.

Reference a value. Use a plain path like %{message.from} or %{vars.reply_message_id}. Works for any field on message, params, or vars. If the path is valid but the value isn’t set, the placeholder resolves to an empty string.

1version: 1.0.0
2sections:
3 main:
4 - reply:
5 body: 'Received message from %{message.from}: %{message.body}'

Variable scopes

The variables available at runtime are the same fields delivered on the inbound webhook payload for each SWML flavor. See the Calling webhook and variable payload and the Messaging webhook and variable payload for the authoritative list of scopes (call / message, params, vars, envs) and every field inside them.

Accessing nested data

Variables can hold simple values, nested objects, or arrays. Use dot notation (.) for object properties and bracket notation ([], zero-based) for array elements. The patterns below work identically inside ${...} and %{...}; the examples use calling syntax and the set method to seed the data.

1version: 1.0.0
2sections:
3 main:
4 - set:
5 user:
6 name: Alice
7 address:
8 city: Seattle
9 employees:
10 - name: Alice
11 role: Engineer
12 - name: Bob
13 role: Manager
14 - play:
15 url: 'say: ${user.name} lives in ${user.address.city}'
16 - play:
17 url: 'say: ${employees[0].name} is an ${employees[0].role}'
18 - play:
19 url: 'say: ${employees[1].name} is a ${employees[1].role}'

The same patterns apply in messaging — for example, %{message.media[0].url} accesses the first media attachment’s URL.

Deployment modes

SWML can be hosted in two places, which affects how variable values get into your script.

Serverless (Dashboard-hosted)

The SWML document lives in the SignalWire Dashboard. SignalWire evaluates the placeholders against the live runtime context — no HTTP fetch is involved.

1version: 1.0.0
2sections:
3 main:
4 - set:
5 department: sales
6 - play:
7 url: 'say: You are calling from ${call.from}'
8 - play:
9 url: 'say: Department is ${vars.department}'

Server-based (external URL)

SignalWire POSTs the runtime context to your server and your server returns the SWML document in response (see the Calling or Messaging payload reference for the request shape). You have two ways to use the variables:

1. Return SWML with placeholders. SignalWire substitutes at runtime — exactly the same syntax used in serverless mode. Best when you just need to interpolate values into otherwise-static SWML.

2. Substitute server-side before responding. Read variables from the request body in your server code, then build a SWML document with the values already filled in. Best when you need logic, transformations, or external lookups that can’t be expressed as a placeholder.

The ${...} in the example below is JavaScript template-literal syntax, not SWML variable expansion — it’s interpolated by Node.js before the response is sent.

1// Example: Node.js / Express server
2app.post('/swml-handler', (req, res) => {
3 const { call, vars, envs, params } = req.body;
4
5 // Pull values out of the request and apply your own logic
6 const department = params.department || 'support';
7 const callerNumber = call.from;
8
9 // Build SWML with concrete values already substituted
10 res.json({
11 version: '1.0.0',
12 sections: {
13 main: [
14 { play: { url: `say: Welcome to ${department}` } },
15 { play: { url: `say: Calling from ${callerNumber}` } },
16 ],
17 },
18 });
19});

See the Deployment Guide for complete server setup instructions.