# Variables & Data (/docs/workflows/variables-and-data)

> 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.

Variables are how a flow carries data from one step to the next: a caller's
input, a value looked up in a database, a field from an API response. They're
scoped to the conversation and can be read by any node downstream of where
they're set. See [Scope](#scope) for what "the conversation" covers when a call
passes through more than one workflow.

## The `$(name)` syntax

Reference a variable anywhere a field accepts it by wrapping its name in
`$(...)`. When the node runs, `$(name)` is replaced with the variable's current
value.

For example, if a variable `customerName` holds `"Alex"`, a Send SMS body of:

```
Hi $(customerName), thanks for calling!
```

is sent as `Hi Alex, thanks for calling!`. You can reference several variables
in one field, and combine them with literal text.

<Callout>
    A variable must be **set before it's read**. Referencing `$(name)` on a path where that variable
    was never assigned yields no value. Order your flow so the node that sets a variable runs before
    the nodes that use it.
</Callout>

## Setting variables

Use a **Set Variable** node to create or update variables during a run. Beyond
plain assignment, Set Variable can compute a value with built-in operations, so
you can derive a value once and reuse it instead of recomputing it in several
places:

- **Arithmetic** — add, subtract, multiply, divide, round, min/max, and more.
- **String** — append, prepend, length, slice, and find-and-replace (including
  regex replace).
- **Date** — create, format, diff, and read or set parts of a date/time.
- **JSONPath** — extract a value from a JSON structure held in a variable.

The inputs to these operations can themselves be `$(name)` references, so you can
build a value from other variables.

### Patterns Regex Replace refuses

**Regex Replace** and **Regex Replace All** refuse a pattern whose matching time can grow
exponentially with the input. A pattern is refused when it has a group repeated by `+`, `*`,
or a brace quantifier that allows more than one repetition (`{2}`, `{2,}`, `{1,5}`), and that
group either:

- contains `+`, `*`, `?`, or a variable-count brace such as `{1,3}` or `{2,}`, at any depth, or
- has two alternatives that can start with the same character. An alternative that starts with
  a character class, a group, or something that can match nothing counts as starting with any
  character.

| Pattern            | Result   |
| ------------------ | -------- |
| `(a\|aa)+`         | Refused  |
| `(\d+)+`           | Refused  |
| `(ab\|cd)+`        | Accepted |
| `\d{1,3}(,\d{3})*` | Accepted |
| `(\.\d+)?`         | Accepted |

The check errs toward refusing. Some safe patterns are refused, for example `(\d|\s)+` and
`((ab)|(cd))+`. Rewrite one as a character class (`[\d\s]+`) or without the inner groups
(`(ab|cd)+`).

The check runs when the node runs, not when you save. A workflow with a refused pattern
saves and publishes normally. When the node runs, it takes its `fallback` exit and sets
none of its variables, including the ones that did not use the pattern.

## Scope

A conversation has **one** set of variables, shared by everything that handles it. A call that
starts in one workflow, hands off to an AI agent, and has that agent invoke a second workflow uses
the same set throughout: what the first workflow set, the agent's tools and the second workflow can
read.

That is what makes context carry across a hand-off, and it has one consequence worth designing
around. **Two workflows that write the same variable name overwrite each other.** The later write
wins, and neither one is told. Variables with different names never collide, so the fix is a naming
convention rather than avoiding shared variables.

If a workflow is meant to be reusable across calls that already carry data, prefix its own working
values with something specific to it — `refund_step`, not `step`.

Variables do not outlive the conversation, and two conversations never see each other's.

## System variables

Every run also carries values the platform fills in itself: the conversation's id,
the caller's number, the current date and time. They live under the `sys.` prefix and
are referenced the same way:

```
Thanks for calling from $(sys.telephony.ani).
```

You can't set or overwrite them; a Set Variable node writing to a `sys.` name is
rejected. See the [system variable reference](/docs/workflows/system-variables) for
every available token, its type, and when it's present.

## Getting data from a database

**SharpenDB** nodes read and write records directly (requires the Sharpen
integration):

- **SELECT** reads rows; capture the columns you need into variables.
- **INSERT**, **UPDATE**, and **DELETE** modify rows.

SELECT (and the other operations) save results into variables by **JSONPath**, so
a value returned by a query becomes available to later nodes as `$(name)`. See
the [node reference](/docs/workflows/reference) for each node's fields and exits.

## Getting data from an integration

**Integration** and **Invoke Lambda** nodes call external services and, like the
database nodes, capture fields from the JSON response into variables by JSONPath.
That response data is then usable downstream with `$(name)` — for instance,
branch on it in a Condition or include it in a message.

<Callout>
    Nodes that call out (database, integration, Lambda) expose a `timeout` exit. Wire it so the flow
    still behaves sensibly when a call is slow or fails — see [Error
    Handling](/docs/workflows/error-handling).
</Callout>

## Next steps

<Cards>
    <Card
        title="Error Handling"
        href="/docs/workflows/error-handling"
        description="Timeouts, no-reply, and unexpected input."
    />
    <Card
        title="Node Reference"
        href="/docs/workflows/reference"
        description="Fields and exits for every node."
    />
</Cards>
