# System Variables (/docs/workflows/system-variables)

> For the complete documentation index, see [llms.txt](https://docs.tandemcx.ai/llms.txt). Any page on this site can be fetched as Markdown by appending `.md` to its URL.

Alongside the variables your flow sets, every run carries a set of **system
variables** the platform fills in for you: which workflow is running, who called,
what the current time is. Reference them exactly like your own
variables — `$(sys.ctx.conversation_id)` — anywhere a field accepts `$( )`.

<Callout type="info">
    System variables are **read-only** from a Set Variable node — it cannot create or overwrite
    anything under `sys.`, the write is rejected.
</Callout>

## Two ways to write a reference

`$(sys.date.now)` and `{{sys.date.now}}` are equivalent — same grammar, same
result. Use whichever you prefer; the editor's suggestion menu completes
whichever one you start typing. To write a literal delimiter instead of a
reference, escape it with a backslash: `\$(` or `\{{`.


Every reference below follows your choice.

## Two clocks: `sys.date` and `sys.utc`

Date and time come in two namespaces with identical fields. `$(sys.date.*)` is the
workflow's own timezone, set in workflow settings; `$(sys.utc.*)` is UTC. A workflow
with no timezone configured falls back to `America/New_York`.

Within a single node, every date reference is evaluated once, so `$(sys.date.minute)`
and `$(sys.date.second)` in the same field can't straddle a tick.

## When a variable isn't there

Anything marked **Optional** depends on how the conversation started — telephony
fields need a phone call, SMS fields need an inbound message, and a carrier may not
supply a caller name at all. An optional value that wasn't supplied resolves to an
**empty string** when you interpolate it into text, so a message body of `$(sys.telephony.ani)` renders as nothing at all on a conversation that
didn't start with a phone call. To branch on whether a value was supplied,
use a Condition node's **Exists** or **Does Not Exist** operator rather than
relying on the rendered text.

## Reference

## Execution Context

Identifiers and metadata about the running workflow and conversation.

*Always available.*

| Variable | Type | Description |
| --- | --- | --- |
| `$(sys.ctx.workflow_id)` | string | Current workflow ID |
| `$(sys.ctx.workflow_instance_id)` | string | Current workflow instance ID |
| `$(sys.ctx.conversation_id)` | string | Current conversation ID |
| `$(sys.ctx.organization_id)` | string | Current organization ID |
| `$(sys.ctx.conversation_type)` | string | Channel/type of the current conversation |
| `$(sys.ctx.workflow_name)` | string | Name of the current workflow |
| `$(sys.ctx.version)` | string | Version of the current workflow |
| `$(sys.ctx.timezone)` | string | Timezone of the current workflow |

## Telephony

Caller and dialed-number data supplied by the originating call.

*Only when a phone call starts the workflow, and only for the fields the carrier supplies.*

| Variable | Type | Description |
| --- | --- | --- |
| `$(sys.telephony.ani)` | string | Caller number (optional) |
| `$(sys.telephony.dnis)` | string | Dialed number (optional) |
| `$(sys.telephony.cnam)` | string | Caller name (optional) |
| `$(sys.telephony.callTag)` | string | Sharpen call identifier, when the call arrived from Sharpen (optional) |

## Inbound SMS

The message that started the workflow.

*Only when an inbound SMS starts the workflow.*

| Variable | Type | Description |
| --- | --- | --- |
| `$(sys.sms.body)` | string | Inbound message text that started the workflow (optional) |
| `$(sys.sms.from)` | string | Sender's phone number (optional) |
| `$(sys.sms.to)` | string | Bandwidth number that received the message (optional) |

## Web Chat

The website visitor and the message that started the workflow.

*Only when a chat widget starts the workflow.*

| Variable | Type | Description |
| --- | --- | --- |
| `$(sys.chat.message)` | string | The visitor's first message, which started the workflow (optional) |
| `$(sys.chat.visitorId)` | string | Opaque id for the visitor's browser, stable across their visits (optional) |
| `$(sys.chat.widgetId)` | string | Id of the chat widget the visitor opened (optional) |

## Caught Error

What failed inside a Try / Catch group.

*Only in a Try / Catch group's catch branch. Elsewhere they hold the last catch's values, or nothing at all if no catch has run in this conversation, so an empty sys.error.node does not mean nothing failed. A later catch overwrites them; if that write fails, the earlier catch's values stay in place.*

| Variable | Type | Description |
| --- | --- | --- |
| `$(sys.error.node)` | string | Name of the node inside the group whose outcome was caught (optional) |
| `$(sys.error.exit)` | string | The exit that node took, e.g. fallback or invalidInput; exits like timeout appear only with "Catch every unhandled exit" turned on (optional) |
| `$(sys.error.message)` | string | The failure text the node reported (optional) |

## Scheduled Run

Facts about a run that a Schedule Workflow node or agent started.

*Only on a run a schedule started.*

| Variable | Type | Description |
| --- | --- | --- |
| `$(sys.schedule.depth)` | number | How many scheduled runs led to this one; absent means 0 (optional) |

## Date & Time

The current date and time. Every field below is available twice: in the workflow's timezone as $(sys.date.*), and in UTC as $(sys.utc.*).

*Always available.*

| Variable | Type | Description |
| --- | --- | --- |
| `$(sys.date.now)`, `$(sys.utc.now)` | string | ISO date-time |
| `$(sys.date.day)`, `$(sys.utc.day)` | number | Day of month |
| `$(sys.date.weekday_num)`, `$(sys.utc.weekday_num)` | number | Weekday number (1=Mon) |
| `$(sys.date.weekday_long)`, `$(sys.utc.weekday_long)` | string | Full weekday name |
| `$(sys.date.weekday_short)`, `$(sys.utc.weekday_short)` | string | Short weekday name |
| `$(sys.date.month)`, `$(sys.utc.month)` | number | Month (1-12) |
| `$(sys.date.month_long)`, `$(sys.utc.month_long)` | string | Full month name |
| `$(sys.date.month_short)`, `$(sys.utc.month_short)` | string | Short month name |
| `$(sys.date.year)`, `$(sys.utc.year)` | number | Four-digit year |
| `$(sys.date.hour_24)`, `$(sys.utc.hour_24)` | number | Hour (0-23) |
| `$(sys.date.hour)`, `$(sys.utc.hour)` | string | Hour (1-12) |
| `$(sys.date.minute)`, `$(sys.utc.minute)` | number | Minute (0-59) |
| `$(sys.date.second)`, `$(sys.utc.second)` | number | Second (0-59) |
| `$(sys.date.timezone)`, `$(sys.utc.timezone)` | string | Timezone name |
| `$(sys.date.offset)`, `$(sys.utc.offset)` | number | UTC offset in hours |
| `$(sys.date.time_24)`, `$(sys.utc.time_24)` | string | 24h time |
| `$(sys.date.time)`, `$(sys.utc.time)` | string | 12h time |
| `$(sys.date.time_long)`, `$(sys.utc.time_long)` | string | Long time |
| `$(sys.date.time_long_12)`, `$(sys.utc.time_long_12)` | string | Long 12h time |
| `$(sys.date.date_short)`, `$(sys.utc.date_short)` | string | Short date |
| `$(sys.date.date_med)`, `$(sys.utc.date_med)` | string | Medium date |
| `$(sys.date.date_long)`, `$(sys.utc.date_long)` | string | Long date |
| `$(sys.date.date_huge)`, `$(sys.utc.date_huge)` | string | Full date |
| `$(sys.date.date_time_short)`, `$(sys.utc.date_time_short)` | string | Short date-time |
| `$(sys.date.date_time_med)`, `$(sys.utc.date_time_med)` | string | Medium date-time |
| `$(sys.date.date_time_long)`, `$(sys.utc.date_time_long)` | string | Long date-time |
| `$(sys.date.date_time_huge)`, `$(sys.utc.date_time_huge)` | string | Full date-time |

## The other namespaces

`sys.` is one of five namespaces a reference can name. The other four hold values you set
yourself, and they are read in different places: a workflow node reads its own variables
and `$(sys.*)`, while a voice agent's configuration reads three more.

| Namespace        | What it holds                                                      | Where it's set                                       | Where it resolves                                                                          |
| ---------------- | ------------------------------------------------------------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `sys.`           | Call and workflow context the platform fills in for you            | The platform                                         | Workflow nodes; in agent configuration, only the subset below                              |
| `org.<name>`     | Organization-wide values shared by every agent                     | **Admin → Secrets & Variables**                      | Voice agent configuration                                                                  |
| `secrets.<name>` | Organization-wide credentials. Write-only — no API returns a value | **Admin → Secrets & Variables**                      | Only in an integration tool's constant parameters and request path, as the request is sent |
| `agent.<name>`   | One agent's own values                                             | The agent's **Variables** section                    | That agent's configuration                                                                 |
| `vars.<name>`    | Per-call values a workflow hands to an agent                       | The **AI Voice Agent** node's **Pass to agent** list | That agent's configuration, for the call it was passed on                                  |

<Callout type="warn">
    A **workflow node** resolves `sys.` and your own flow variables — and nothing else. `org.`,
    `secrets.`, `agent.`, and `vars.` all render as an empty string there, with no error. They are
    for voice agent configuration.
</Callout>

Full details, and how to create the values, are on
[Secrets & Variables](/docs/overview/secrets-and-variables).

### An organization-wide value

Create a variable named `brand` with the value `Northwind Traders`, then write an agent's
system prompt against it:

```
You are the virtual receptionist for {{org.brand}}.
```

Change the variable once and every agent that references it picks up the new value. Nothing needs
to be re-saved. Allow up to five minutes for the change to reach every call; [When a change takes
effect](/docs/overview/secrets-and-variables#when-a-change-takes-effect) explains why.

### A value for one agent

Add a variable named `queue` with the value `Billing` in that agent's **Variables**
section, then reference it in the same agent's prompt:

```
Transfer to the {{agent.queue}} team when the caller asks about an invoice.
```

An `agent.` name is visible only to the agent that declares it. Two agents can each have a
`queue` with different values.

### A secret resolves in one place only

A secret is deliberately harder to use than a variable. `{{secrets.stripe_key}}` is
resolved in one place only — the outbound HTTP request an integration tool makes — so it
may appear only in that tool's constant parameters and its request path. Anywhere else
(a prompt, a greeting, a disposition, an agent variable) is rejected when you save.

That is the point of the design: if a prompt leaks, it leaks the text
`{{secrets.stripe_key}}` rather than the key.

<Callout type="warn">
    **Only the `{{ }}` spelling works for a secret.** Everywhere else on the platform the two
    spellings are interchangeable. A secret is matched as the request leaves, against the exact form
    `{{secrets.name}}`, so a `$(secrets.name)` is refused rather than resolved. Saving an agent that
    references a secret also requires the organization administrator role.
</Callout>

### A value for one call

A workflow can hand an agent values that change call to call — a customer id, an account
tier, the result of a lookup it already did — through the **AI Voice Agent** node's **Pass
to agent** list. The agent reads them as `{{vars.<name>}}` in its prompt, greeting,
thinking cue, disposition descriptions, and an integration tool's constant parameters.

```
Greet the caller by name. Their account tier is {{vars.tier}}.
```

They travel one way. A name the workflow didn't pass reads as empty rather than failing the
call, and the values are fixed at the moment of the handover. See
[Secrets & Variables](/docs/overview/secrets-and-variables#values-for-a-single-call) for the
limits.

### Functions don't work in agent configuration

`{{UPPER(org.brand)}}` is valid in a workflow node. In agent configuration, and in an org
variable's value, a function call is rejected — use a plain reference instead.

### In a voice agent, only part of `sys.` resolves

An agent session has no workflow behind it, so the tokens that describe one have nothing
to read. These are the `sys.` tokens an agent's configuration accepts:

- Context: `$(sys.ctx.conversation_id)`, `$(sys.ctx.organization_id)`,
  and `$(sys.ctx.agent_id)` — the last exists only for agents, which is why it
  isn't in the reference table above.
- Telephony: `$(sys.telephony.ani)`, the other party's number. On an inbound
  call that's the caller's number. On an outbound call placed by a Convert to Voice node,
  it's the number that was dialed, not your caller ID. It is the only telephony field an
  agent session receives.
- Date and time: every `$(sys.utc.*)` field.

Everything else is refused when you save the agent, with `is not available to a voice
agent`. That covers `$(sys.ctx.workflow_id)`, `$(sys.ctx.workflow_instance_id)`, `$(sys.ctx.workflow_name)`, `$(sys.ctx.version)`, `$(sys.ctx.conversation_type)`, `$(sys.ctx.timezone)`, all three `$(sys.sms.*)` fields, `$(sys.telephony.dnis)`, `$(sys.telephony.cnam)`, and every `$(sys.date.*)` field.

<Callout type="info">
    Use `$(sys.utc.*)` for the time in an agent, not `$(sys.date.*)`. A
    workflow reads `$(sys.date.*)` in the timezone set in its settings, and an agent has
    no workflow behind it to supply one, so rather than answer in a zone you did not choose, an
    agent refuses the token at save time. UTC is correct without configuration, which is why it is
    the one that resolves.
</Callout>

## Next steps

<Cards>
    <Card
        title="Secrets & Variables"
        href="/docs/overview/secrets-and-variables"
        description="Organization-wide secrets and variables, and using them in an agent."
    />
    <Card
        title="Variables & Data"
        href="/docs/workflows/variables-and-data"
        description="Setting your own variables and capturing data from integrations."
    />
    <Card
        title="Node Reference"
        href="/docs/workflows/reference"
        description="Fields and exits for every node."
    />
</Cards>
