# Scheduling Workflows (/docs/workflows/scheduling-workflows)

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

<Callout type="info">
    **Scheduling workflows is being rolled out.** If you don't see the **Schedule Workflow** node,
    it isn't on for your organization yet.
</Callout>

**Schedule Workflow** runs another published workflow once, at a time you choose, with the
variables you pass. **Cancel Scheduled Workflow** cancels one that hasn't run yet.

## Schedule a run

1. Add **Schedule Workflow** and pick the **Workflow** to run.
2. Set **Run At** (for example `2026-10-14T15:00`) and **Timezone**.
3. Under **Variables**, add each value the scheduled workflow needs. The editor lists the
   variables that workflow reads; click one to add it.
4. Optionally set **Save Job ID As** to keep the schedule's job ID in a variable, for example to
   cancel it later.

The scheduled run doesn't share this conversation. It starts with only the variables you list.

## Which version runs

The version that's published when the run fires. Publishing a fix also fixes runs that are
already scheduled. If you unpublish or delete the workflow first, the run fails and shows as
**Failed** in **Automations → Workflows → Scheduled**, with a reason code such as
`WORKFLOW_NOT_PUBLISHED` or `WORKFLOW_NOT_FOUND`.

## Keys

A **Key** names a schedule, for example `followup:$(sys.telephony.ani)`. Scheduling again with the
same key replaces the pending run, or keeps it and takes the **skipped** exit when **If Key
Exists** is **Keep**. Leave **skipped** unwired and the flow continues on **default** instead.

**Cancel Scheduled Workflow** cancels by key or by job ID. It takes its **notFound** exit when
nothing matches, or **default** if **notFound** is left unwired.

## From an AI agent

Add **Schedule Workflow** as a node tool on the agent. **Workflow** always stays fixed by you.
**Run At**, **Key**, and **If Key Exists** each have their own **Model-filled** switch: flip it to
let the model supply that value instead of setting it yourself. A **Run At** without an offset is
always read in the agent's timezone, the zone set on its
[current time tool](/docs/agents/voice-agents/built-in-tools#tell-the-caller-the-current-time)
(`America/New_York` while that tool is off). The node's **Timezone** field applies to the workflow
node only and has no effect on this tool. Click **Add Model-Filled Variable** to add a
value the model fills in from the conversation. It's passed to the scheduled workflow the same as
a row under **Variables**. **Save Job ID As** has no effect on this tool: the agent gets the job
ID back in the tool's result, not as a saved variable. When **If Key Exists** is **Keep** and a run
with that key is already pending, the result says the existing run was kept and gives its time, so
the agent can tell the caller.

## Test calls

A test call never schedules or cancels anything. The node takes its **default** exit.

## Limits and defaults

| Limit                | Default | Most you can set |
| -------------------- | ------- | ---------------- |
| Days ahead           | 30      | 90               |
| Pending schedules    | 1,000   | 10,000           |
| Schedules per minute | 60      | 600              |
| Schedules per run    | 5       | 50               |
| Chain depth          | 3       | 10               |

An owner or admin changes these from **Limits** on the **Flows → Scheduled** page. Past most limits, Schedule Workflow takes its
`fallback` exit; past Days ahead, it takes `invalidInput` instead, since that's a bad Run At value
rather than a load limit. Contact support to go past the most you can set.

## Next steps

- [Variables and data](/docs/workflows/variables-and-data)
- [Error handling](/docs/workflows/error-handling)
