> For a complete index of all SignalWire documentation pages, fetch https://signalwire.com/docs/llms.txt

# Variables

> Syntax, scopes, deployment modes, and how to access nested data with SWML variables.

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:

* [Calling webhook and variable payload](/docs/swml/reference/calling#webhook-payload) — `call`, `params`, `vars`, `envs`
* [Messaging webhook and variable payload](/docs/swml/reference/messaging#webhook-payload) — `message`, `params`, `vars`

For JavaScript expressions on top of variables, see [Expressions](/docs/swml/reference/expressions).
For template transformation functions, see [Template Functions](/docs/swml/reference/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](/docs/swml/reference/expressions).

#### YAML

```yaml
version: 1.0.0
sections:
  main:
    - play:
        url: 'say: This call is from ${call.from}'
```

#### JSON

```json
{
  "version": "1.0.0",
  "sections": {
    "main": [
      { "play": { "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.

#### YAML

```yaml
version: 1.0.0
sections:
  main:
    - reply:
        body: 'Received message from %{message.from}: %{message.body}'
```

#### JSON

```json
{
  "version": "1.0.0",
  "sections": {
    "main": [
      { "reply": { "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](/docs/swml/reference/calling#webhook-payload) and the
[Messaging webhook and variable payload](/docs/swml/reference/messaging#webhook-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`](/docs/swml/reference/calling/set) method to seed the data.

#### YAML

```yaml
version: 1.0.0
sections:
  main:
    - set:
        user:
          name: Alice
          address:
            city: Seattle
        employees:
          - name: Alice
            role: Engineer
          - name: Bob
            role: Manager
    - play:
        url: 'say: ${user.name} lives in ${user.address.city}'
    - play:
        url: 'say: ${employees[0].name} is an ${employees[0].role}'
    - play:
        url: 'say: ${employees[1].name} is a ${employees[1].role}'
```

#### JSON

```json
{
  "version": "1.0.0",
  "sections": {
    "main": [
      {
        "set": {
          "user": {
            "name": "Alice",
            "address": { "city": "Seattle" }
          },
          "employees": [
            { "name": "Alice", "role": "Engineer" },
            { "name": "Bob", "role": "Manager" }
          ]
        }
      },
      { "play": { "url": "say: ${user.name} lives in ${user.address.city}" } },
      { "play": { "url": "say: ${employees[0].name} is an ${employees[0].role}" } },
      { "play": { "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.

#### YAML

```yaml
version: 1.0.0
sections:
  main:
    - set:
        department: sales
    - play:
        url: 'say: You are calling from ${call.from}'
    - play:
        url: 'say: Department is ${vars.department}'
```

#### JSON

```json
{
  "version": "1.0.0",
  "sections": {
    "main": [
      { "set": { "department": "sales" } },
      { "play": { "url": "say: You are calling from ${call.from}" } },
      { "play": { "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](/docs/swml/reference/calling#webhook-payload) or
[Messaging](/docs/swml/reference/messaging#webhook-payload) 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.

```javascript
// Example: Node.js / Express server
app.post('/swml-handler', (req, res) => {
  const { call, vars, envs, params } = req.body;

  // Pull values out of the request and apply your own logic
  const department = params.department || 'support';
  const callerNumber = call.from;

  // Build SWML with concrete values already substituted
  res.json({
    version: '1.0.0',
    sections: {
      main: [
        { play: { url: `say: Welcome to ${department}` } },
        { play: { url: `say: Calling from ${callerNumber}` } },
      ],
    },
  });
});
```

See the [Deployment Guide](/docs/swml/guides/deployment) for complete server setup instructions.