BashBrainTeammatesPricingDevelopersGet a test key

Routines that run themselves

A routine is an ask that runs by itself: on a schedule in plain words, or whenever your system calls. Each user can have their own.

Say when, in plain words

Words like weekday at 08:00 or mon, wed, fri at 18:30, read in the user's own time zone (routines).

What happens Bash answers with the cron it made and the next run. Each run lands in the routine's own conversation.

TypeScript
const traveller = vestra.user('traveller-42');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);
Python
traveller = vestra.user("traveller-42")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)
CLI
vestra routines add "Check my trips for today and tell me about any delay." --every "weekday at 08:00" --user traveller-42
HTTP
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"  }'
MCP
{  "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"    }  }}

Or run on a call

For when something happens in your system, such as a late train.

What happens Each call runs it once. Your payload rides along as data, never as instructions.

TypeScript
const traveller = vestra.user('traveller-42');const onDelay = await traveller.routines.create({  ask: 'A train of mine is late. Find me another way.',  on: 'call',}); // From your system, with what happened.await traveller.routines.trigger(onDelay.id, {  train: '12127',  delay_minutes: 45,});
Python
traveller = vestra.user("traveller-42")on_delay = traveller.routines.create(    "A train of mine is late. Find me another way.", on="call") # From your system, with what happened.traveller.routines.trigger(    on_delay.id, {"train": "12127", "delay_minutes": 45})
CLI
# It answers with its trigger_url: your system posts there, with your key.vestra routines add "A train of mine is late. Find me another way." --on call --user traveller-42
HTTP
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" }' # From your system, with what happened.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 }'
MCP
{  "method": "tools/call",  "params": {    "name": "routines_add",    "arguments": {      "user": "traveller-42",      "ask": "A train of mine is late. Find me another way.",      "on": "call"    }  }}

Give each user their own

A routine per user, from one loop.

What happens 08:00 is each user's own morning, in the time zone you named them with.

TypeScript
// One for each user: 08:00 is each one's own morning.for (const user of travellers) {  await vestra.user(user).routines.create({    ask: 'Check my trips for today and tell me about any delay.',    every: 'weekday at 08:00',  });}
Python
# One for each user: 08:00 is each one's own morning.for user in travellers:    vestra.user(user).routines.create(        "Check my trips for today and tell me about any delay.",        every="weekday at 08:00",    )
CLI
# One for each user: 08:00 is each one's own morning.vestra routines add "Check my trips for today and tell me about any delay." --every "weekday at 08:00" --user traveller-42vestra routines add "Check my trips for today and tell me about any delay." --every "weekday at 08:00" --user traveller-77
HTTP
# One for each user: 08:00 is each one's own morning.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" }' curl "$VESTRA_BASE_URL/v1/routines" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-77", "ask": "Check my trips for today and tell me about any delay.", "every": "weekday at 08:00" }'
HTTP

An agent adds one at a time with routines_add, naming each user.

# One for each user: 08:00 is each one's own morning.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" }' curl "$VESTRA_BASE_URL/v1/routines" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-77", "ask": "Check my trips for today and tell me about any delay.", "every": "weekday at 08:00" }'

Read its runs

Each run is an ask: its status, and the conversation it ran in.

What happens routine.fired reaches your webhooks each time one runs.

TypeScript
const traveller = vestra.user('traveller-42');const runs = await traveller.routines.runs('rtn_5kd');for (const run of runs.data)  console.log(run.status, run.conversation_id);
Python
traveller = vestra.user("traveller-42")for run in traveller.routines.runs("rtn_5kd").data:    print(run.status, run.conversation_id)
CLI
vestra routines runs rtn_5kd --user traveller-42
HTTP
curl "$VESTRA_BASE_URL/v1/routines/rtn_5kd/runs" \  -H "Authorization: Bearer $VESTRA_API_KEY"
HTTP

Runs are read from an SDK, the CLI or HTTP. An agent lists routines with routines_list.

curl "$VESTRA_BASE_URL/v1/routines/rtn_5kd/runs" \  -H "Authorization: Bearer $VESTRA_API_KEY"

Pause it

Paused, it skips its runs until you resume it.

What happens Resuming says when it runs next.

Show outputHide output

The CLI prints

Paused rtn_5kd.
TypeScript
const traveller = vestra.user('traveller-42');await traveller.routines.pause('rtn_5kd'); // Later: it says when it runs next.const back = await traveller.routines.resume('rtn_5kd');console.log(back.next_run_at);
Python
traveller = vestra.user("traveller-42")traveller.routines.pause("rtn_5kd") # Later: it says when it runs next.print(traveller.routines.resume("rtn_5kd").next_run_at)
CLI
vestra routines pause rtn_5kd --user traveller-42 # Later: it says when it runs next.vestra routines resume rtn_5kd --user traveller-42
HTTP
curl -X PATCH "$VESTRA_BASE_URL/v1/routines/rtn_5kd" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "enabled": false }' # Later: "enabled": true, and it says when it runs next.curl -X PATCH "$VESTRA_BASE_URL/v1/routines/rtn_5kd" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "enabled": true }'
HTTP

Pausing is done from an SDK, the CLI or HTTP.

curl -X PATCH "$VESTRA_BASE_URL/v1/routines/rtn_5kd" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "enabled": false }' # Later: "enabled": true, and it says when it runs next.curl -X PATCH "$VESTRA_BASE_URL/v1/routines/rtn_5kd" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "enabled": true }'

The whole thing

One for each user, one on a call, its runs and a pause, in one program.

What happens Bash runs each one by itself, and your webhooks hear every run.

TypeScript
import { Vestra } from '@vestra/sdk'; const vestra = new Vestra();const travellers = ['traveller-42', 'traveller-77']; // Say when, in plain words: one for each userfor (const user of travellers) {  await vestra.user(user).routines.create({    ask: 'Check my trips for today and tell me about any delay.',    every: 'weekday at 08:00',  });} // Or run on a call from your systemconst traveller = vestra.user('traveller-42');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' }); // Read its runs, and pause itconst runs = await traveller.routines.runs(onDelay.id);console.log(runs.data.length);await traveller.routines.pause(onDelay.id);
Python
from vestra import Vestra vestra = Vestra()travellers = ["traveller-42", "traveller-77"] # Say when, in plain words: one for each userfor user in travellers:    vestra.user(user).routines.create(        "Check my trips for today and tell me about any delay.",        every="weekday at 08:00",    ) # Or run on a call from your systemtraveller = vestra.user("traveller-42")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"}) # Read its runs, and pause itprint(len(traveller.routines.runs(on_delay.id).data))traveller.routines.pause(on_delay.id)
CLI
# Say when, in plain words: one for each uservestra routines add "Check my trips for today and tell me about any delay." --every "weekday at 08:00" --user traveller-42vestra routines add "Check my trips for today and tell me about any delay." --every "weekday at 08:00" --user traveller-77 # Or run on a call from your systemvestra routines add "A train of mine is late. Find me another way." --on call --user traveller-42 # Read its runs, and pause itvestra routines runs rtn_5kd --user traveller-42vestra routines pause rtn_5kd --user traveller-42
HTTP
# Say when, in plain wordscurl "$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" }' # Or run on a call from your systemcurl "$VESTRA_BASE_URL/v1/routines/rtn_7qp/trigger" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "train": "12127" }' # Read its runs, and pause itcurl "$VESTRA_BASE_URL/v1/routines/rtn_5kd/runs" \  -H "Authorization: Bearer $VESTRA_API_KEY" curl -X PATCH "$VESTRA_BASE_URL/v1/routines/rtn_5kd" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "enabled": false }'
HTTP

An agent adds routines with routines_add and lists them with routines_list.

# Say when, in plain wordscurl "$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" }' # Or run on a call from your systemcurl "$VESTRA_BASE_URL/v1/routines/rtn_7qp/trigger" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "train": "12127" }' # Read its runs, and pause itcurl "$VESTRA_BASE_URL/v1/routines/rtn_5kd/runs" \  -H "Authorization: Bearer $VESTRA_API_KEY" curl -X PATCH "$VESTRA_BASE_URL/v1/routines/rtn_5kd" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "enabled": false }'