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.
// 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',});# Your id for them, a name, and the time zone their routines read.vestra.users.create( "traveller-42", name="Priya Sharma", timezone="Asia/Kolkata")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" }'# 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" }'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
// One conversation each.const message = 'Is your flight hit by the storm?';const batch = vestra.ask( travellers.map((user) => ({ user, message })),);# One conversation each.message = "Is your flight hit by the storm?"batch = vestra.ask( [{"user": u, "message": message} for u in travellers])# One conversation each: travellers.csv has a user id per line.vestra ask "Is your flight hit by the storm?" --users travellers.csv# 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?" } ] }'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.
// 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);# 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)# Each item as it finishes, then the totals.vestra events bat_7xk# 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"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.
// 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],});# 20 dollars a month for each user, with alerts at 50% and 80%.vestra.usage.caps(user_monthly_usd=20, alert_thresholds=[50, 80])# 20 dollars a month for each user, with alerts at 50% and 80%.vestra usage caps --customer-monthly 20 --alerts 50,80# 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] }'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.
// 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);# Since the 1st, by user, in billed US dollars.spend = vestra.usage(from_="2026-10-01", group_by="user")print(spend.total.spend_usd)# Since the 1st, by user, in billed US dollars.vestra usage --from 2026-10-01 --by user# 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"{ "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.
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);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)# 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# 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"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"