# routines

> Run a job on a schedule, or each time your system calls a URL.

Run a job on a schedule, or each time your system calls a URL. A routine is a saved job that runs on a schedule written in plain words, such as `weekday at 08:00`, or when your system posts to its trigger URL. Each run appears in the routine's conversation like any other ask, so its replies, approvals and files are where you already look.

## TypeScript

```ts
const traveller = vestra.user('traveller-42');

// Run on a schedule. Write it in plain words, read in the traveller's own time zone. The response gives the
// cron expression and the next run.
const daily = await traveller.routines.create({
  ask: 'Check my trips for today and tell me about any delay.',
  every: 'weekday at 08:00',
});
console.log(daily.schedule?.cron, daily.next_run_at);

// Run when your system calls. Post a payload to the routine's trigger_url.
const onDelay = await traveller.routines.create({ ask: 'A train of mine is late. Find me another way.', on: 'call' });
await traveller.routines.trigger(onDelay.id, { train: '12127', delay_minutes: 45 });

// Manage a routine. Pause it, resume it, run it once now, and read its runs.
await traveller.routines.pause(daily.id);
await traveller.routines.resume(daily.id);
await traveller.routines.run(daily.id);
const runs = await traveller.routines.runs(daily.id);
console.log(runs.data.length);
```

## Python

```python
traveller = vestra.user("traveller-42")

# Run on a schedule. Write it in plain words, read in the traveller's own time zone. The response gives the
# cron expression and the next run.
daily = traveller.routines.create("Check my trips for today and tell me about any delay.", every="weekday at 08:00")
print(daily.schedule.cron, daily.next_run_at)

# Run when your system calls. Post a payload to the routine's trigger_url.
on_delay = traveller.routines.create("A train of mine is late. Find me another way.", on="call")
traveller.routines.trigger(on_delay.id, {"train": "12127", "delay_minutes": 45})

# Manage a routine. Pause it, resume it, run it once now, and read its runs.
traveller.routines.pause(daily.id)
traveller.routines.resume(daily.id)
traveller.routines.run(daily.id)
print(len(traveller.routines.runs(daily.id).data))
```

## CLI

```bash
# Run on a schedule. Write it in plain words, read in the traveller's own time zone.
vestra routines add "Check my trips for today and tell me about any delay." \
  --every "weekday at 08:00" --user traveller-42

# Run when your system calls. Your system posts to the routine's trigger_url.
vestra routines add "A train of mine is late. Find me another way." --on call --user traveller-42

# Manage a routine. List the user's routines, then run one now, pause it, resume it, and read its runs.
vestra routines --user traveller-42
vestra routines run rtn_5kd
vestra routines pause rtn_5kd
vestra routines resume rtn_5kd
vestra routines runs rtn_5kd
```

## HTTP

```bash
# Run on a schedule. Write it in plain words, read in the user's own time zone. The response gives the cron
# expression and the next run.
curl "$VESTRA_BASE_URL/v1/routines" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "traveller-42",
    "ask": "Check my trips for today and tell me about any delay.",
    "every": "weekday at 08:00"
  }'

# Run when your system calls. Post a payload to the routine's trigger_url.
curl "$VESTRA_BASE_URL/v1/routines" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "traveller-42", "ask": "A train of mine is late. Find me another way.", "on": "call" }'

curl "$VESTRA_BASE_URL/v1/routines/rtn_7qp/trigger" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "train": "12127", "delay_minutes": 45 }'

# Manage a routine. Pause it (enabled: true resumes it), run it once now, and list the user's routines.
curl -X PATCH "$VESTRA_BASE_URL/v1/routines/rtn_5kd" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'

curl -X POST "$VESTRA_BASE_URL/v1/routines/rtn_5kd/run" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

curl "$VESTRA_BASE_URL/v1/routines?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

## MCP

```json
{
  "method": "tools/call",
  "params": {
    "name": "routines_add",
    "arguments": {
      "user": "traveller-42",
      "ask": "Check my trips for today and tell me about any delay.",
      "every": "weekday at 08:00",
      "timezone": "Asia/Kolkata"
    }
  }
}
```

## Good to know

### Writing a schedule

`every` understands `day at 09:00`, `weekday at 08:00` (or `weekdays`), `weekend at 10:30`, days such as `mon, wed, fri at 18:30`, `30 minutes` (15, 20 or 30), `2 hours` (1, 2, 3, 4, 6, 8 or 12) and `month on the 1st at 09:00` (the 1st to the 28th). Times use the 24-hour clock. A phrase it cannot read fails with `400 schedule_unreadable`, along with examples that work. You can also pass a cron expression as `schedule`.

### Time zones

A schedule is read in the `timezone` you give. Without one, it uses the user's own time zone, and then UTC. The response includes the cron expression and the next run time.

### Routines Bash creates

If you ask Bash in a conversation to check your trips every weekday at 8, it creates the routine itself. It appears in the list like any other routine.

### Routine limits

Your company can have up to 200 routines in each mode (test or live), paused ones included. One more fails with `409 org_routine_limit_reached`. Running routines yourself with `run` and `trigger` is limited to 12 times an hour for one routine, and 240 times an hour for your company.

### A copy for each user

A routine your file declares with `for: each customer` gets one copy per user, made the first time that user talks to that deployment's Bash. Copies count toward the same limit, 200 per mode. A copy past the limit is skipped with `org_routine_limit_reached` and never retried.

### When a routine runs

Each run sends a `routine.fired` event that names the run and its conversation, on the stream and to your webhooks.

## The routes behind routines

- `POST` [/v1/routines](https://vestra.ai/developers/reference/api/routines/post-v1-routines.md): Create one: what to ask, and when: every (plain words such as "weekday at 08:00", read in the user's time zone), a cron schedule, or on "call" (your system posts to its trigger_url). For a user, or with no user_id for the company's own Bash. Each run is a message in the routine's conversation. Key scope: `runtime` (or `all`).
- `GET` [/v1/routines](https://vestra.ai/developers/reference/api/routines/get-v1-routines.md): The routines of one user (user_id), or of the company's own Bash. Key scope: `runtime` (or `all`).
- `GET` [/v1/routines/{routine_id}](https://vestra.ai/developers/reference/api/routines/get-v1-routines-routine-id.md): One routine. Key scope: `runtime` (or `all`).
- `PATCH` [/v1/routines/{routine_id}](https://vestra.ai/developers/reference/api/routines/patch-v1-routines-routine-id.md): Change what it asks, when it runs (every, schedule or timezone), its filters, or pause and resume it. Key scope: `runtime` (or `all`).
- `DELETE` [/v1/routines/{routine_id}](https://vestra.ai/developers/reference/api/routines/delete-v1-routines-routine-id.md): Delete it. Key scope: `runtime` (or `all`).
- `POST` [/v1/routines/{routine_id}/run](https://vestra.ai/developers/reference/api/routines/post-v1-routines-routine-id-run.md): Run it now. Key scope: `runtime` (or `all`).
- `POST` [/v1/routines/{routine_id}/trigger](https://vestra.ai/developers/reference/api/routines/post-v1-routines-routine-id-trigger.md): Fire it with a payload from your system: a train delayed, a refund posted. Key scope: `runtime` (or `all`).
- `GET` [/v1/routines/{routine_id}/runs](https://vestra.ai/developers/reference/api/routines/get-v1-routines-routine-id-runs.md): Past runs and the tasks they started. Key scope: `runtime` (or `all`).

Next: [`answer`](https://vestra.ai/developers/answer.md)
