> ## Documentation Index
> Fetch the complete documentation index at: https://yukta.infenia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Schedule a workflow

> Run a workflow every few minutes or on a cron schedule: let SCHEDULE_TRIGGER keep time inside one open run, or start a short run from your own scheduler.

<dl />

**Use this when** a job should run every five minutes or every weekday at 09:00. There are two ways, and the choice matters.

![Two ways to repeat a job. SCHEDULE\_TRIGGER ticks inside one run that stays open; an external scheduler starts a short, separate run for every tick.](https://yukta.infenia.com/images/diagrams/guide-schedule-a-workflow.light.svg)

Left: Yukta keeps time inside one open run. Right: your scheduler starts one short run per tick.

| | `SCHEDULE_TRIGGER` | External scheduler |
| - | - | - |
| Who keeps time | Yukta, inside one run | Your scheduler (cron, CI, cloud) |
| Runs | One that stays open | One per tick |
| After a server restart | Start the workflow again; the position is lost | The next start goes through when the server is up |
| Holds a run slot | All the time | Only while a run is active |
| Gap longer than 30 s | Set `timeoutSeconds` on the trigger | Not an issue |

## Steps

**1. Put a SCHEDULE\_TRIGGER at the start**

Set exactly one of `intervalMs` and `cron`. The first tick comes after one interval, or at the next cron time. Each tick is a message `{"tick": 1, "firedAt": "..."}`; `tick` counts from 1.

| Option | Meaning |
| - | - |
| `intervalMs` | A tick every this many milliseconds, from when the run starts |
| `cron` | A 6-field expression: second, minute, hour, day of month, month, day of week |
| `zone` | Time zone of `cron`, such as `Europe/Berlin` (default: the server's) |
| `maxRuns` | Stop after this many ticks; the run then ends once the work is done |

`0 */5 * * * *` is every five minutes, `0 0 9 * * MON-FRI` is 09:00 on weekdays. Both set, neither set, a non-positive interval and a cron with no future date are refused at validation.

> [!WARNING]
>
> A trigger node has an idle timeout of 30 seconds like any other node. When the gap between ticks is longer, set `timeoutSeconds` on the trigger to more than the longest gap (above `86400` for a daily cron), or it times out between ticks.

**2. Write the workflow**

This probes a URL every minute and sends a notification when it fails. Save as `uptime.json`:

```json uptime.json theme={"system"}
{
  "sessionId": "uptime-watch", "name": "Uptime watch",
  "description": "Probe a URL every minute and notify on failure",
  "initiator": "docs", "projectPath": "/tmp",
  "workflows": {
    "probe": {
      "workflowId": "probe", "description": "Runs until stopped",
      "nodes": [
        { "nodeId": "every-minute", "type": "SCHEDULE_TRIGGER",
          "config": { "intervalMs": 60000, "timeoutSeconds": 120 } },
        { "nodeId": "check", "type": "HTTP_REQUEST",
          "config": { "url": "https://example.com/health", "timeout": 10,
                      "failureMode": "CONTINUE", "routeByStatus": true } },
        { "nodeId": "ok", "type": "CONSOLE_TERMINAL", "config": {} },
        { "nodeId": "alert", "type": "NOTIFY_TERMINAL",
          "config": { "url": "https://hooks.example.com/services/T000/B000/XXXX", "text": "Health check failed" } }
      ],
      "edges": [
        { "source": "every-minute", "target": "check" },
        { "source": "check", "target": "ok", "sourcePort": "success" },
        { "source": "check", "target": "alert", "sourcePort": "failure" }
      ]
    }
  }
}
```

**3. Apply it and start it once**

```bash theme={"system"}
curl -X POST localhost:9858/api/sessions -H 'Content-Type: application/json' -d @uptime.json
curl -s -X POST localhost:9858/api/workflow/start -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: uptime-start' -d '{"sessionId":"uptime-watch","workflowId":"probe"}'
```

Starting twice gives two runs and two independent schedules, so start it once; the `Idempotency-Key` makes a retried start return the first run.

**4. Stop it when you are done**

A run with a schedule stays open until you stop it, the trigger reaches `maxRuns`, or the server stops. The default `DRAIN` mode stops the trigger and lets in-flight work finish:

```bash theme={"system"}
curl -s -X POST localhost:9858/api/workflow/uptime-watch/probe/stop
```

CLI: `yukta-cli workflow stop uptime-watch probe`.

## Verify it worked

After a minute and a bit, the run is still `RUNNING` and the `ok` terminal has received a message with `tick` 1; a minute later, `tick` 2. Read it with the [results stream](https://yukta.infenia.com/api/streams.md) or the run log.

## Variations

* **A schedule that survives restarts.** The schedule position is not stored: after a restart nothing starts the workflow, missed ticks are not replayed, and a resumed run starts its schedule again from the beginning. Run the clock outside Yukta instead: start a run per tick with `POST /api/workflow/start`, `yukta-cli workflow start <session> <workflow> --payload '{...}'` or the MCP tool `start_workflow`, from a workflow that begins with `API_TRIGGER`. An `Idempotency-Key` such as `nightly-2026-10-15` stops a retried start from running twice.
* **Ticks arrive faster than the work.** All ticks belong to one run and an interval tick that arrives while the previous start is still running is dropped and logged; keep the interval longer than the work, or put `engine.edgeBuffer` on the slow node ([Delivery and back-pressure](https://yukta.infenia.com/concepts/delivery-and-backpressure.md)).
* **Limits on concurrent runs.** A schedule holds one slot all the time: [Tune capacity and limits](https://yukta.infenia.com/guides/operate/tune-capacity-and-limits.md).

## Related

* [Trigger reference](https://yukta.infenia.com/reference/plugins/triggers.md) for `SCHEDULE_TRIGGER`'s every option.
* [Control a run](https://yukta.infenia.com/guides/build/control-a-run.md) to pause or stop an open run.
* [Time limits](https://yukta.infenia.com/concepts/time-limits.md) for the idle timeout.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.