# Serve many users at once

> Name your users, ask them all at once, cap what each can spend, and see what each one cost.

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.

About 10 minutes. It uses [`users`](https://vestra.ai/developers/users.md), [`ask`](https://vestra.ai/developers/ask.md), [`usage`](https://vestra.ai/developers/usage.md).

## 1. 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`](https://vestra.ai/developers/users.md)).

**TypeScript**

```ts
// 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**

```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"
)
```

**CLI**

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

**HTTP**

```bash
# 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" }'
```

**MCP**

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

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

## 2. Ask them all at once

One message for many users, each in a conversation of their own ([`ask`](https://vestra.ai/developers/ask.md)).

**TypeScript**

```ts
// One conversation each.
const message = 'Is your flight hit by the storm?';
const batch = vestra.ask(
  travellers.map((user) => ({ user, message })),
);
```

**Python**

```python
# One conversation each.
message = "Is your flight hit by the storm?"
batch = vestra.ask(
    [{"user": u, "message": message} for u in travellers]
)
```

**CLI**

```bash
# One conversation each: travellers.csv has a user id per line.
vestra ask "Is your flight hit by the storm?" --users travellers.csv
```

**HTTP**

```bash
# 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?" }
    ]
  }'
```

**MCP**

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

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

The CLI prints:

```text
Asked 2 users in bat_7xk. Follow it: vestra events bat_7xk
```

## 3. Follow the batch

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

**TypeScript**

```ts
// 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**

```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**

```bash
# Each item as it finishes, then the totals.
vestra events bat_7xk
```

**HTTP**

```bash
# 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"
```

**MCP**

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

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

The CLI prints:

```text
  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.
```

## 4. Cap what each can spend

A monthly cap for each user, and alerts on the way ([`usage`](https://vestra.ai/developers/usage.md)).

**TypeScript**

```ts
// 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**

```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**

```bash
# 20 dollars a month for each user, with alerts at 50% and 80%.
vestra usage caps --customer-monthly 20 --alerts 50,80
```

**HTTP**

```bash
# 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] }'
```

**MCP**

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

**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.

## 5. See what each spent

Billed US dollars, grouped by user.

**TypeScript**

```ts
// 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**

```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**

```bash
# Since the 1st, by user, in billed US dollars.
vestra usage --from 2026-10-01 --by user
```

**HTTP**

```bash
# 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**

```json
{
  "method": "tools/call",
  "params": {
    "name": "usage",
    "arguments": { "from": "2026-10-01", "group_by": "user" }
  }
}
```

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

## 6. The whole thing

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

**TypeScript**

```ts
import { Vestra } from '@vestra/sdk';

const vestra = new Vestra();
const travellers = ['traveller-42', 'traveller-77'];

// Name your users
for (const user of travellers)
  await vestra.users.create(user, { timezone: 'Asia/Kolkata' });

// Cap what each can spend
await vestra.usage.caps({
  user_monthly_usd: 20,
  alert_thresholds: [50, 80],
});

// Ask them all at once, and follow the batch
const 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 spent
const spend = await vestra.usage({
  from: '2026-10-01',
  group_by: 'user',
});
console.log(spend.total.spend_usd);
```

**Python**

```python
from vestra import Vestra

vestra = Vestra()
travellers = ["traveller-42", "traveller-77"]

# Name your users
for user in travellers:
    vestra.users.create(user, timezone="Asia/Kolkata")

# Cap what each can spend
vestra.usage.caps(user_monthly_usd=20, alert_thresholds=[50, 80])

# Ask them all at once, and follow the batch
message = "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 spent
spend = vestra.usage(from_="2026-10-01", group_by="user")
print(spend.total.spend_usd)
```

**CLI**

```bash
# Cap what each can spend
vestra usage caps --customer-monthly 20 --alerts 50,80

# Ask them all at once: travellers.csv has a user id per line
vestra ask "Is your flight hit by the storm?" --users travellers.csv

# Follow the batch
vestra events bat_7xk

# See what each spent
vestra usage --from 2026-10-01 --by user
```

**HTTP**

```bash
# Name your users
curl "$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 spend
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] }'

# Ask them all at once
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
curl "$VESTRA_BASE_URL/v1/batches/bat_7xk/items" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# See what each spent
curl "$VESTRA_BASE_URL/v1/usage?from=2026-10-01&group_by=user" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**

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

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

Next: [Routines that run themselves](https://vestra.ai/developers/guides/routines.md)
