BashBrainTeammatesPricingDevelopersGet a test key

Serve many users at once

Each of your users gets a Bash of their own, named by your own id. Ask them all at once, cap what each can spend, and see what each one cost.

Name your users

Bash makes a user the first time you act for them. This step adds their name and the time zone their routines run in (users).

What happens Run it again any time: nothing is made twice, and user.created reaches your webhooks once.

TypeScript
// Your id for them, a name, and the time zone their routines read.await vestra.users.create('traveller-42', {  name: 'Priya Sharma',  timezone: 'Asia/Kolkata',});
Python
# Your id for them, a name, and the time zone their routines read.vestra.users.create(    "traveller-42", name="Priya Sharma", timezone="Asia/Kolkata")
HTTP

The CLI names a user with --user on any command. Names and time zones are set from an SDK or HTTP.

# Your id for them, a name, and the time zone their routines read.curl "$VESTRA_BASE_URL/v1/users" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-42", "name": "Priya Sharma", "timezone": "Asia/Kolkata" }'
HTTP
# Your id for them, a name, and the time zone their routines read.curl "$VESTRA_BASE_URL/v1/users" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-42", "name": "Priya Sharma", "timezone": "Asia/Kolkata" }'
HTTP

An agent names a user with user on any tool. Names and time zones are set from an SDK or HTTP.

# Your id for them, a name, and the time zone their routines read.curl "$VESTRA_BASE_URL/v1/users" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-42", "name": "Priya Sharma", "timezone": "Asia/Kolkata" }'

Ask them all at once

One message for many users, each in a conversation of their own (ask).

What happens Bash works through them within the batch's concurrency, and each finishes on its own.

Show outputHide output

The CLI prints

Asked 2 users in bat_7xk. Follow it: vestra events bat_7xk
TypeScript
// One conversation each.const message = 'Is your flight hit by the storm?';const batch = vestra.ask(  travellers.map((user) => ({ user, message })),);
Python
# One conversation each.message = "Is your flight hit by the storm?"batch = vestra.ask(    [{"user": u, "message": message} for u in travellers])
CLI
# One conversation each: travellers.csv has a user id per line.vestra ask "Is your flight hit by the storm?" --users travellers.csv
HTTP
# One conversation each.curl "$VESTRA_BASE_URL/v1/batches" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "items": [      { "user_id": "traveller-42", "message": "Is your flight hit by the storm?" },      { "user_id": "traveller-77", "message": "Is your flight hit by the storm?" }    ]  }'
HTTP

Over MCP, one ask is one user. A batch is made from an SDK, the CLI or HTTP.

# One conversation each.curl "$VESTRA_BASE_URL/v1/batches" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "items": [      { "user_id": "traveller-42", "message": "Is your flight hit by the storm?" },      { "user_id": "traveller-77", "message": "Is your flight hit by the storm?" }    ]  }'

Follow the batch

Each user's answer as it finishes, then the totals.

What happens An item's result.text is its work's answer. batch.completed reaches your webhooks once every item has finished.

Show outputHide output

The CLI prints

  traveller-42: succeeded: Your flight 6E 2134 is on time.
  traveller-77: succeeded: AI 865 leaves 40 minutes late.
bat_7xk: completed. 2 succeeded, 0 failed; $0.0610.
TypeScript
// Until every item has finished: each answer, then the totals.const done = await batch.wait();for await (const item of batch.items())  console.log(item.user_id, item.status, item.result?.text);console.log(done.counts, done.spend_usd);
Python
# Until every item has finished: each answer, then the totals.done = batch.wait()for item in batch.items():    answer = item.result.text if item.result else None    print(item.user_id, item.status, answer)print(done.counts, done.spend_usd)
CLI
# Each item as it finishes, then the totals.vestra events bat_7xk
HTTP
# Its status and counts, then each item's text: the work's answer.curl "$VESTRA_BASE_URL/v1/batches/bat_7xk" \  -H "Authorization: Bearer $VESTRA_API_KEY" curl "$VESTRA_BASE_URL/v1/batches/bat_7xk/items" \  -H "Authorization: Bearer $VESTRA_API_KEY"
HTTP

Batches are followed from an SDK, the CLI or HTTP.

# Its status and counts, then each item's text: the work's answer.curl "$VESTRA_BASE_URL/v1/batches/bat_7xk" \  -H "Authorization: Bearer $VESTRA_API_KEY" curl "$VESTRA_BASE_URL/v1/batches/bat_7xk/items" \  -H "Authorization: Bearer $VESTRA_API_KEY"

Cap what each can spend

A monthly cap for each user, and alerts on the way (usage).

What happens A user at their cap is refused new work with 402 user_cap_reached. usage.threshold_reached and usage.cap_reached reach your webhooks.

TypeScript
// 20 dollars a month for each user, with alerts at 50% and 80%.await vestra.usage.caps({  user_monthly_usd: 20,  alert_thresholds: [50, 80],});
Python
# 20 dollars a month for each user, with alerts at 50% and 80%.vestra.usage.caps(user_monthly_usd=20, alert_thresholds=[50, 80])
CLI
# 20 dollars a month for each user, with alerts at 50% and 80%.vestra usage caps --customer-monthly 20 --alerts 50,80
HTTP
# 20 dollars a month for each user, with alerts at 50% and 80%.curl -X PATCH "$VESTRA_BASE_URL/v1/limits" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_monthly_usd": 20, "alert_thresholds": [50, 80] }'
HTTP

Caps stay with people: set them from an SDK, the CLI or HTTP.

# 20 dollars a month for each user, with alerts at 50% and 80%.curl -X PATCH "$VESTRA_BASE_URL/v1/limits" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_monthly_usd": 20, "alert_thresholds": [50, 80] }'

See what each spent

Billed US dollars, grouped by user.

What happens One row per user, and the total. Line items are the receipt behind each one.

TypeScript
// Since the 1st, by user, in billed US dollars.const spend = await vestra.usage({  from: '2026-10-01',  group_by: 'user',});console.log(spend.total.spend_usd);
Python
# Since the 1st, by user, in billed US dollars.spend = vestra.usage(from_="2026-10-01", group_by="user")print(spend.total.spend_usd)
CLI
# Since the 1st, by user, in billed US dollars.vestra usage --from 2026-10-01 --by user
HTTP
# Since the 1st, by user, in billed US dollars.curl "$VESTRA_BASE_URL/v1/usage?from=2026-10-01&group_by=user" \  -H "Authorization: Bearer $VESTRA_API_KEY"
MCP
{  "method": "tools/call",  "params": {    "name": "usage",    "arguments": { "from": "2026-10-01", "group_by": "user" }  }}

The whole thing

Name, cap, ask and count, in one program.

What happens Every user answered in their own conversation, inside the cap you set.

TypeScript
import { Vestra } from '@vestra/sdk'; const vestra = new Vestra();const travellers = ['traveller-42', 'traveller-77']; // Name your usersfor (const user of travellers)  await vestra.users.create(user, { timezone: 'Asia/Kolkata' }); // Cap what each can spendawait vestra.usage.caps({  user_monthly_usd: 20,  alert_thresholds: [50, 80],}); // Ask them all at once, and follow the batchconst message = 'Is your flight hit by the storm?';const batch = vestra.ask(  travellers.map((user) => ({ user, message })),);console.log((await batch.wait()).counts); // See what each spentconst spend = await vestra.usage({  from: '2026-10-01',  group_by: 'user',});console.log(spend.total.spend_usd);
Python
from vestra import Vestra vestra = Vestra()travellers = ["traveller-42", "traveller-77"] # Name your usersfor user in travellers:    vestra.users.create(user, timezone="Asia/Kolkata") # Cap what each can spendvestra.usage.caps(user_monthly_usd=20, alert_thresholds=[50, 80]) # Ask them all at once, and follow the batchmessage = "Is your flight hit by the storm?"batch = vestra.ask(    [{"user": u, "message": message} for u in travellers])print(batch.wait().counts) # See what each spentspend = vestra.usage(from_="2026-10-01", group_by="user")print(spend.total.spend_usd)
CLI
# Cap what each can spendvestra usage caps --customer-monthly 20 --alerts 50,80 # Ask them all at once: travellers.csv has a user id per linevestra ask "Is your flight hit by the storm?" --users travellers.csv # Follow the batchvestra events bat_7xk # See what each spentvestra usage --from 2026-10-01 --by user
HTTP
# Name your userscurl "$VESTRA_BASE_URL/v1/users" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-42", "timezone": "Asia/Kolkata" }' # Cap what each can spendcurl -X PATCH "$VESTRA_BASE_URL/v1/limits" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_monthly_usd": 20, "alert_thresholds": [50, 80] }' # Ask them all at oncecurl "$VESTRA_BASE_URL/v1/batches" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "items": [      { "user_id": "traveller-42", "message": "Is your flight hit by the storm?" },      { "user_id": "traveller-77", "message": "Is your flight hit by the storm?" }    ]  }' # Follow the batchcurl "$VESTRA_BASE_URL/v1/batches/bat_7xk/items" \  -H "Authorization: Bearer $VESTRA_API_KEY" # See what each spentcurl "$VESTRA_BASE_URL/v1/usage?from=2026-10-01&group_by=user" \  -H "Authorization: Bearer $VESTRA_API_KEY"
HTTP

An agent reads spend with the usage tool. Batches and caps are set from an SDK, the CLI or HTTP.

# Name your userscurl "$VESTRA_BASE_URL/v1/users" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_id": "traveller-42", "timezone": "Asia/Kolkata" }' # Cap what each can spendcurl -X PATCH "$VESTRA_BASE_URL/v1/limits" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "user_monthly_usd": 20, "alert_thresholds": [50, 80] }' # Ask them all at oncecurl "$VESTRA_BASE_URL/v1/batches" \  -H "Authorization: Bearer $VESTRA_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "items": [      { "user_id": "traveller-42", "message": "Is your flight hit by the storm?" },      { "user_id": "traveller-77", "message": "Is your flight hit by the storm?" }    ]  }' # Follow the batchcurl "$VESTRA_BASE_URL/v1/batches/bat_7xk/items" \  -H "Authorization: Bearer $VESTRA_API_KEY" # See what each spentcurl "$VESTRA_BASE_URL/v1/usage?from=2026-10-01&group_by=user" \  -H "Authorization: Bearer $VESTRA_API_KEY"