# Account API

> Your company's account: who is calling, its keys, its caps, what it spent and the models it may use.

Send `Authorization: Bearer vk_test_...` on every call.

## `GET /v1/me`

Its own page: [GET /v1/me](https://vestra.ai/developers/reference/api/account/get-v1-me.md)

Who is calling: the company, the key, and whether it is test or live.

Your API key, any scope. On the card: [`keys`](https://vestra.ai/developers/keys.md). Every call here is a checked sample.

### Request

Nothing to send but the route.

### Answers 200

- `object` (`"me"`): Always `me`.
- `org` (`object`): The company account the call acts for.
  - `id` (`string`): Your company account's id. Example: `org_2f8k`.
  - `name` (`string`): Your company's name.
  - `status` (`string`): `active` while the account works. Any other value means it is switched off, and calls answer 403 `org_disabled`.
- `mode` (`"live" | "test"`): `live` or `test`: the mode of the key (or console session) in use. A test key sees only test users, test deployments and test money.
- `key` (`object | null`): The API key in use; null when the call comes from a console session.
  - `id` (`string`): The key's id. Example: `key_8f2k`.
  - `name` (`string | null`): The name you gave the key; null when it has none.
  - `last4` (`string`): The last four characters of the key's secret, to tell keys apart.
- `member` (`object | null`): Who is signed in to the console; null when the call uses an API key.
  - `role` (`"owner" | "admin" | "developer"`): Your role in the company: `owner`, `admin` or `developer`.

### Code

**TypeScript**

```ts
const me = await vestra.keys.me();
```

**Python**

```python
me = vestra.keys.me()
```

**CLI**

```bash
vestra login
```

**HTTP**

```bash
# Check your key. See your company, this key, and whether it is test or live.
curl "$VESTRA_BASE_URL/v1/me" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Create a key for CI. This one can only set things up. Its secret is shown only once.
curl "$VESTRA_BASE_URL/v1/keys" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "ci", "scopes": ["setup"] }'

# Roll a key. You get a new secret, and the old one keeps working for an hour.
curl "$VESTRA_BASE_URL/v1/keys/key_3vd/roll" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "grace_seconds": 3600 }'

# Revoke a key. It stops working at once.
curl -X DELETE "$VESTRA_BASE_URL/v1/keys/key_3vd" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**: No sample in your language for this route, so here it is over HTTP.

## `GET /v1/org`

Its own page: [GET /v1/org](https://vestra.ai/developers/reference/api/account/get-v1-org.md)

The company account: name, status, the wallet in billed dollars, day and month caps, alert thresholds, allowed return URLs, allowed embed origins, default deployment.

Your API key, scope `admin` or `all`. On the card: [`keys`](https://vestra.ai/developers/keys.md). Over HTTP it is made from the contract: fill in every value.

### Request

Nothing to send but the route.

### Answers 200

- `id` (`string`): The company account's id. Example: `org_2f8k`.
- `object` (`"org"`): Always `org`.
- `name` (`string`): The company's name.
- `status` (`string`): `active` while the account works. Any other value means it is switched off, and calls answer 403 `org_disabled`.
- `mode` (`"live" | "test"`): The mode these settings and this wallet belong to: `live` or `test`, the mode of the key in use.
- `allowed_return_urls` (`string[]`): Where a connect link may send a person back to (`return_url`). Empty: no return URL is allowed.
- `allowed_embed_origins` (`string[]`): The https origins whose pages may show Bash in an iframe, and open a voice call.
- `default_deployment` (`string | null`): The deployment a conversation runs when neither it nor its user names one; null: Bash as it comes, with no deployment of yours.
- `require_user` (`boolean`): When true, a request that names no `user_id` answers 400 `user_required` instead of reaching the company's own Bash.
- `memory_panel` (`boolean`): Whether a session may show the user what Bash remembers (POST /v1/sessions with `memory: true`). Off until you turn it on.
- `memory_mode` (`"vestra" | "customer" | "none"`): Who keeps what Bash remembers about each user, unless a deployment says otherwise: `vestra` (Bash keeps it), `customer` (your side keeps it, at `memory_endpoint`) or `none`.
- `memory_endpoint` (`string | null`): With `memory_mode: customer`, the https URL Bash asks for what to remember about a user; null otherwise.
- `otlp_endpoint` (`string | null`): Where Bash sends its traces, as OpenTelemetry (OTLP over HTTP); null: none are sent.
- `otlp_headers_secret` (`string | null`): The name of your secret that holds the headers sent with each trace; its value is never returned. Null: none.
- `balance_usd` (`number`): This mode's wallet, in billed US dollars, up to 5 decimals. Work stops with 402 `wallet_empty` when it runs out.
- `caps` (`object`): This mode's spending caps, for the company and per user, per day and per month.
  - `org_daily_usd` (`number | null`): The company's spending cap per day (UTC), in US dollars, across all its users; null: none, up to the account's ceiling. Limits: 0 to 1000000.
  - `user_daily_usd` (`number | null`): The default cap per user per day (UTC), in US dollars; a user's own `spend_cap_usd` wins. Null: none. Limits: 0 to 1000000.
  - `max_org_daily_usd` (`number`): The highest the company's daily cap may be set, in US dollars. Ask Vestra to raise it. Limits: 0 to 1000000.
  - `org_monthly_usd` (`number | null`): The company's cap per calendar month (UTC), in US dollars. Null (the default): none. Limits: 0 to 1000000.
  - `user_monthly_usd` (`number | null`): The default cap per user per calendar month (UTC), in US dollars; a user's own `monthly_cap_usd` wins. Null (the default): none. Limits: 0 to 1000000.
- `usage_alerts` (`object`): When Bash tells your webhooks that spend is nearing a cap.
  - `thresholds` (`integer[]`): Percents of a cap at which `usage.threshold_reached` fires; reaching 100 always fires `usage.cap_reached`. Limits: at most 5 items.
- `created_at` (`string`): When the account was made, as an ISO 8601 date and time in UTC.

### Code

**HTTP**

```bash
curl "$VESTRA_BASE_URL/v1/org" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `PATCH /v1/org`

Its own page: [PATCH /v1/org](https://vestra.ai/developers/reference/api/account/patch-v1-org.md)

Change the name, allowed return URLs, allowed embed origins, default deployment or default memory mode.

Your API key, scope `admin` or `all`. On the card: [`keys`](https://vestra.ai/developers/keys.md). Every call here is a checked sample.

### Request

Body:

- `name` (`string`): The company's name, 1 to 200 characters. Limits: 1 to 200 characters.
- `allowed_return_urls` (`(string (URL))[]`): Where a connect link may send a person back to; replaces the whole list. At most 20. Limits: at most 20 items. Example: `["https://app.example.com/connected"]`.
- `allowed_embed_origins` (`(string (URL))[]`): The https origins whose pages may show Bash in an iframe; replaces the whole list. At most 20. Limits: at most 20 items. Example: `["https://app.example.com"]`.
- `default_deployment` (`string | null`): The deployment a conversation runs when it names none, by name or `dep_` id; null clears it. Limits: at least 1 character.
- `require_user` (`boolean`): True: every request must name a `user_id`, and none reaches the company's own Bash.
- `memory_panel` (`boolean`): True: a session may show the user what Bash remembers.
- `memory_mode` (`"vestra" | "customer" | "none"`): Who keeps what Bash remembers: `vestra`, `customer` (send `memory_endpoint` too) or `none`.
- `memory_endpoint` (`string (URL) | null`): With `memory_mode: customer`, the https URL Bash asks for what to remember; null clears it.
- `otlp_endpoint` (`string (URL) | null`): Where to send traces, as OpenTelemetry over HTTP; null stops them.
- `otlp_headers_secret` (`string | null`): The name of one of your secrets (PUT /v1/secrets/{name}) whose value is a JSON object of header names to values, sent with each trace; null clears it. Example: `OTLP_HEADERS`.

### Answers 200

- `id` (`string`): The company account's id. Example: `org_2f8k`.
- `object` (`"org"`): Always `org`.
- `name` (`string`): The company's name.
- `status` (`string`): `active` while the account works. Any other value means it is switched off, and calls answer 403 `org_disabled`.
- `mode` (`"live" | "test"`): The mode these settings and this wallet belong to: `live` or `test`, the mode of the key in use.
- `allowed_return_urls` (`string[]`): Where a connect link may send a person back to (`return_url`). Empty: no return URL is allowed.
- `allowed_embed_origins` (`string[]`): The https origins whose pages may show Bash in an iframe, and open a voice call.
- `default_deployment` (`string | null`): The deployment a conversation runs when neither it nor its user names one; null: Bash as it comes, with no deployment of yours.
- `require_user` (`boolean`): When true, a request that names no `user_id` answers 400 `user_required` instead of reaching the company's own Bash.
- `memory_panel` (`boolean`): Whether a session may show the user what Bash remembers (POST /v1/sessions with `memory: true`). Off until you turn it on.
- `memory_mode` (`"vestra" | "customer" | "none"`): Who keeps what Bash remembers about each user, unless a deployment says otherwise: `vestra` (Bash keeps it), `customer` (your side keeps it, at `memory_endpoint`) or `none`.
- `memory_endpoint` (`string | null`): With `memory_mode: customer`, the https URL Bash asks for what to remember about a user; null otherwise.
- `otlp_endpoint` (`string | null`): Where Bash sends its traces, as OpenTelemetry (OTLP over HTTP); null: none are sent.
- `otlp_headers_secret` (`string | null`): The name of your secret that holds the headers sent with each trace; its value is never returned. Null: none.
- `balance_usd` (`number`): This mode's wallet, in billed US dollars, up to 5 decimals. Work stops with 402 `wallet_empty` when it runs out.
- `caps` (`object`): This mode's spending caps, for the company and per user, per day and per month.
  - `org_daily_usd` (`number | null`): The company's spending cap per day (UTC), in US dollars, across all its users; null: none, up to the account's ceiling. Limits: 0 to 1000000.
  - `user_daily_usd` (`number | null`): The default cap per user per day (UTC), in US dollars; a user's own `spend_cap_usd` wins. Null: none. Limits: 0 to 1000000.
  - `max_org_daily_usd` (`number`): The highest the company's daily cap may be set, in US dollars. Ask Vestra to raise it. Limits: 0 to 1000000.
  - `org_monthly_usd` (`number | null`): The company's cap per calendar month (UTC), in US dollars. Null (the default): none. Limits: 0 to 1000000.
  - `user_monthly_usd` (`number | null`): The default cap per user per calendar month (UTC), in US dollars; a user's own `monthly_cap_usd` wins. Null (the default): none. Limits: 0 to 1000000.
- `usage_alerts` (`object`): When Bash tells your webhooks that spend is nearing a cap.
  - `thresholds` (`integer[]`): Percents of a cap at which `usage.threshold_reached` fires; reaching 100 always fires `usage.cap_reached`. Limits: at most 5 items.
- `created_at` (`string`): When the account was made, as an ISO 8601 date and time in UTC.

### Code

**HTTP**

```bash
# Describe the job, naming the deployment. Its questions arrive on the conversation the answer names: reply there, as the init line shows.
# A slug already taken (409 slug_taken, say by an init you stopped)? Send another, such as travel-desk-2.
curl "$VESTRA_BASE_URL/v1/deployments/interview" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Travel desk", "slug": "travel-desk", "job": "Answer questions from my travellers about their bookings. Look bookings up with get_booking." }'

# The interview saves the draft in test mode. Play its starter tests.
curl -X POST "$VESTRA_BASE_URL/v1/deployments/travel-desk/test-runs" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Make it your default deployment: every ask below that names no deployment reaches it. This replaces any default you had.
curl -X PATCH "$VESTRA_BASE_URL/v1/org" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "default_deployment": "travel-desk" }'
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `GET /v1/keys`

Its own page: [GET /v1/keys](https://vestra.ai/developers/reference/api/account/get-v1-keys.md)

List keys. Only the last four characters are ever shown again.

Your API key, scope `admin` or `all`. On the card: [`keys`](https://vestra.ai/developers/keys.md). Over HTTP it is made from the contract: fill in every value.

### Request

Nothing to send but the route.

### Answers 200

- `data` (`object[]`): The items on this page.
  - `id` (`string`): The key's id. Example: `key_8f2k`.
  - `object` (`"api_key"`): Always `api_key`.
  - `mode` (`"live" | "test"`): `live` or `test`: what the key reaches. A test key sees only test users, test deployments and test money.
  - `name` (`string | null`): The name you gave it; null when it has none.
  - `prefix` (`string`): The start of the secret, to tell keys apart: `vk_live_` or `vk_test_` and a few characters. Example: `vk_test_8f2k`.
  - `last4` (`string`): The last four characters of the secret.
  - `scopes` (`("all" | "runtime" | "setup" | "admin")[]`): What the key may call: `all`, or any of `runtime` (conversations, work, routines, connected apps, files, memory, usage), `setup` (deployments, tests, secrets) and `admin` (users and keys).
  - `status` (`"active" | "expiring" | "expired" | "revoked"`): `active`: works, with no end. `expiring`: works until `expires_at` (a rolled key in its grace period, or one made to expire). `expired` or `revoked`: refused.
  - `created_at` (`string`): When it was made, as an ISO 8601 date and time in UTC.
  - `last_used_at` (`string | null`): When it last made a call; null when it never has.
  - `expires_at` (`string | null`): When it stops working; null when it never does.
  - `revoked_at` (`string | null`): When it was revoked; null while it is not.
- `next_cursor` (`string | null`): Send it as `cursor` for the next page; null on the last page.

### Code

**TypeScript**

```ts
const keys = await vestra.keys.list();
```

**Python**

```python
keys = vestra.keys.list()
```

**CLI**

```bash
vestra keys
```

**HTTP**

```bash
curl "$VESTRA_BASE_URL/v1/keys" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**: No sample in your language for this route, so here it is over HTTP.

## `POST /v1/keys`

Its own page: [POST /v1/keys](https://vestra.ai/developers/reference/api/account/post-v1-keys.md)

Create a test or live key. The full key is shown once.

Your API key, scope `admin` or `all`. On the card: [`keys`](https://vestra.ai/developers/keys.md). Every call here is a checked sample.

### Request

Body, if you send one:

- `name` (`string`): A name for the key, to tell it apart later, up to 100 characters. Limits: 1 to 100 characters. Example: `production`.
- `scopes` (`("all" | "runtime" | "setup" | "admin")[]`): What it may call: `all` alone, or 1 to 3 of `runtime`, `setup` and `admin`. Absent: `all`. A key only gives the scopes it has. Limits: 1 to 3 items. Example: `["runtime"]`.
- `expires_at` (`string (date and time)`): When the key stops working, an ISO 8601 date and time in the future. Absent: never.

### Answers 201

- `id` (`string`): The key's id. Example: `key_8f2k`.
- `object` (`"api_key"`): Always `api_key`.
- `mode` (`"live" | "test"`): `live` or `test`: what the key reaches. A test key sees only test users, test deployments and test money.
- `name` (`string | null`): The name you gave it; null when it has none.
- `prefix` (`string`): The start of the secret, to tell keys apart: `vk_live_` or `vk_test_` and a few characters. Example: `vk_test_8f2k`.
- `last4` (`string`): The last four characters of the secret.
- `scopes` (`("all" | "runtime" | "setup" | "admin")[]`): What the key may call: `all`, or any of `runtime` (conversations, work, routines, connected apps, files, memory, usage), `setup` (deployments, tests, secrets) and `admin` (users and keys).
- `status` (`"active" | "expiring" | "expired" | "revoked"`): `active`: works, with no end. `expiring`: works until `expires_at` (a rolled key in its grace period, or one made to expire). `expired` or `revoked`: refused.
- `created_at` (`string`): When it was made, as an ISO 8601 date and time in UTC.
- `last_used_at` (`string | null`): When it last made a call; null when it never has.
- `expires_at` (`string | null`): When it stops working; null when it never does.
- `revoked_at` (`string | null`): When it was revoked; null while it is not.
- `secret` (`string`): The whole key. It is shown this once: store it now, as Vestra keeps only its hash. Example: `vk_test_...`.

### Code

**TypeScript**

```ts
const key = await vestra.keys.create();
```

**Python**

```python
key = vestra.keys.create()
```

**CLI**

```bash
vestra keys create
```

**HTTP**

```bash
# Check your key. See your company, this key, and whether it is test or live.
curl "$VESTRA_BASE_URL/v1/me" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Create a key for CI. This one can only set things up. Its secret is shown only once.
curl "$VESTRA_BASE_URL/v1/keys" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "ci", "scopes": ["setup"] }'

# Roll a key. You get a new secret, and the old one keeps working for an hour.
curl "$VESTRA_BASE_URL/v1/keys/key_3vd/roll" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "grace_seconds": 3600 }'

# Revoke a key. It stops working at once.
curl -X DELETE "$VESTRA_BASE_URL/v1/keys/key_3vd" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**: No sample in your language for this route, so here it is over HTTP.

## `POST /v1/keys/{key_id}/roll`

Its own page: [POST /v1/keys/{key_id}/roll](https://vestra.ai/developers/reference/api/account/post-v1-keys-key-id-roll.md)

Replace a key. The old one keeps working for a grace period you choose, then stops.

Your API key, scope `admin` or `all`. On the card: [`keys`](https://vestra.ai/developers/keys.md). Every call here is a checked sample.

### Request

Path:

- `key_id` (`string`, required): The API key's id (`key_...`).

Body, if you send one:

- `grace_seconds` (`integer`): How long the old key keeps working, in seconds, up to 7 days. Default 86400 (a day); 0 stops it at once. Default: `86400`. Limits: 0 to 604800.

### Answers 201

- `object` (`"api_key_roll"`): Always `api_key_roll`.
- `key` (`object`): The new key, with its secret, shown once.
  - `id` (`string`): The key's id. Example: `key_8f2k`.
  - `object` (`"api_key"`): Always `api_key`.
  - `mode` (`"live" | "test"`): `live` or `test`: what the key reaches. A test key sees only test users, test deployments and test money.
  - `name` (`string | null`): The name you gave it; null when it has none.
  - `prefix` (`string`): The start of the secret, to tell keys apart: `vk_live_` or `vk_test_` and a few characters. Example: `vk_test_8f2k`.
  - `last4` (`string`): The last four characters of the secret.
  - `scopes` (`("all" | "runtime" | "setup" | "admin")[]`): What the key may call: `all`, or any of `runtime` (conversations, work, routines, connected apps, files, memory, usage), `setup` (deployments, tests, secrets) and `admin` (users and keys).
  - `status` (`"active" | "expiring" | "expired" | "revoked"`): `active`: works, with no end. `expiring`: works until `expires_at` (a rolled key in its grace period, or one made to expire). `expired` or `revoked`: refused.
  - `created_at` (`string`): When it was made, as an ISO 8601 date and time in UTC.
  - `last_used_at` (`string | null`): When it last made a call; null when it never has.
  - `expires_at` (`string | null`): When it stops working; null when it never does.
  - `revoked_at` (`string | null`): When it was revoked; null while it is not.
  - `secret` (`string`): The whole key. It is shown this once: store it now, as Vestra keeps only its hash. Example: `vk_test_...`.
- `previous` (`object`): The old key: it works until its `expires_at`, then stops.
  - `id` (`string`): The key's id. Example: `key_8f2k`.
  - `object` (`"api_key"`): Always `api_key`.
  - `mode` (`"live" | "test"`): `live` or `test`: what the key reaches. A test key sees only test users, test deployments and test money.
  - `name` (`string | null`): The name you gave it; null when it has none.
  - `prefix` (`string`): The start of the secret, to tell keys apart: `vk_live_` or `vk_test_` and a few characters. Example: `vk_test_8f2k`.
  - `last4` (`string`): The last four characters of the secret.
  - `scopes` (`("all" | "runtime" | "setup" | "admin")[]`): What the key may call: `all`, or any of `runtime` (conversations, work, routines, connected apps, files, memory, usage), `setup` (deployments, tests, secrets) and `admin` (users and keys).
  - `status` (`"active" | "expiring" | "expired" | "revoked"`): `active`: works, with no end. `expiring`: works until `expires_at` (a rolled key in its grace period, or one made to expire). `expired` or `revoked`: refused.
  - `created_at` (`string`): When it was made, as an ISO 8601 date and time in UTC.
  - `last_used_at` (`string | null`): When it last made a call; null when it never has.
  - `expires_at` (`string | null`): When it stops working; null when it never does.
  - `revoked_at` (`string | null`): When it was revoked; null while it is not.

### Code

**TypeScript**

```ts
const rolled = await vestra.keys.roll('key_8sd');
```

**Python**

```python
rolled = vestra.keys.roll("key_8sd")
```

**CLI**

```bash
vestra keys roll key_8sd
```

**HTTP**

```bash
# Check your key. See your company, this key, and whether it is test or live.
curl "$VESTRA_BASE_URL/v1/me" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Create a key for CI. This one can only set things up. Its secret is shown only once.
curl "$VESTRA_BASE_URL/v1/keys" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "ci", "scopes": ["setup"] }'

# Roll a key. You get a new secret, and the old one keeps working for an hour.
curl "$VESTRA_BASE_URL/v1/keys/key_3vd/roll" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "grace_seconds": 3600 }'

# Revoke a key. It stops working at once.
curl -X DELETE "$VESTRA_BASE_URL/v1/keys/key_3vd" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**: No sample in your language for this route, so here it is over HTTP.

## `DELETE /v1/keys/{key_id}`

Its own page: [DELETE /v1/keys/{key_id}](https://vestra.ai/developers/reference/api/account/delete-v1-keys-key-id.md)

Revoke a key immediately.

Your API key, scope `admin` or `all`. On the card: [`keys`](https://vestra.ai/developers/keys.md). Every call here is a checked sample.

### Request

Path:

- `key_id` (`string`, required): The API key's id (`key_...`).

### Answers 200

- `id` (`string`): The key's id. Example: `key_8f2k`.
- `object` (`"api_key"`): Always `api_key`.
- `mode` (`"live" | "test"`): `live` or `test`: what the key reaches. A test key sees only test users, test deployments and test money.
- `name` (`string | null`): The name you gave it; null when it has none.
- `prefix` (`string`): The start of the secret, to tell keys apart: `vk_live_` or `vk_test_` and a few characters. Example: `vk_test_8f2k`.
- `last4` (`string`): The last four characters of the secret.
- `scopes` (`("all" | "runtime" | "setup" | "admin")[]`): What the key may call: `all`, or any of `runtime` (conversations, work, routines, connected apps, files, memory, usage), `setup` (deployments, tests, secrets) and `admin` (users and keys).
- `status` (`"active" | "expiring" | "expired" | "revoked"`): `active`: works, with no end. `expiring`: works until `expires_at` (a rolled key in its grace period, or one made to expire). `expired` or `revoked`: refused.
- `created_at` (`string`): When it was made, as an ISO 8601 date and time in UTC.
- `last_used_at` (`string | null`): When it last made a call; null when it never has.
- `expires_at` (`string | null`): When it stops working; null when it never does.
- `revoked_at` (`string | null`): When it was revoked; null while it is not.

### Code

**TypeScript**

```ts
await vestra.keys.revoke('key_8sd');
```

**Python**

```python
vestra.keys.revoke("key_8sd")
```

**CLI**

```bash
vestra keys revoke key_8sd
```

**HTTP**

```bash
# Check your key. See your company, this key, and whether it is test or live.
curl "$VESTRA_BASE_URL/v1/me" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Create a key for CI. This one can only set things up. Its secret is shown only once.
curl "$VESTRA_BASE_URL/v1/keys" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "ci", "scopes": ["setup"] }'

# Roll a key. You get a new secret, and the old one keeps working for an hour.
curl "$VESTRA_BASE_URL/v1/keys/key_3vd/roll" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "grace_seconds": 3600 }'

# Revoke a key. It stops working at once.
curl -X DELETE "$VESTRA_BASE_URL/v1/keys/key_3vd" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**: No sample in your language for this route, so here it is over HTTP.

## `GET /v1/limits`

Its own page: [GET /v1/limits](https://vestra.ai/developers/reference/api/account/get-v1-limits.md)

The day and month caps for the company and per user, the alert thresholds and the rate limits.

Your API key, scope `runtime` or `all`. On the card: [`usage`](https://vestra.ai/developers/usage.md). Over HTTP it is made from the contract: fill in every value.

### Request

Nothing to send but the route.

### Answers 200

- `object` (`"limits"`): Always `limits`.
- `mode` (`"live" | "test"`): The mode these limits belong to: the mode of the key in use.
- `org_daily_usd` (`number | null`): The company's spending cap per day (UTC), in US dollars, across all its users; null: none, up to the account's ceiling. Limits: 0 to 1000000.
- `user_daily_usd` (`number | null`): The default cap per user per day (UTC), in US dollars; a user's own `spend_cap_usd` wins. Null: none. Limits: 0 to 1000000.
- `max_org_daily_usd` (`number`): The highest the company's daily cap may be set, in US dollars. Ask Vestra to raise it. Limits: 0 to 1000000.
- `org_monthly_usd` (`number | null`): The company's cap per calendar month (UTC), in US dollars. Null (the default): none. Limits: 0 to 1000000.
- `user_monthly_usd` (`number | null`): The default cap per user per calendar month (UTC), in US dollars; a user's own `monthly_cap_usd` wins. Null (the default): none. Limits: 0 to 1000000.
- `alert_thresholds` (`integer[]`): Percents of a cap at which usage alerts fire, for the company and each user, per day and per month. Limits: at most 5 items.
- `rate_limits` (`object`): How fast you may call, fixed per account.
  - `requests_per_minute` (`integer`): Calls a key may make per minute before 429 `rate_limited`.
  - `concurrent_task_starts` (`integer`): How many computers your company may be waking at once with `warm`; past it, `warm` answers 429 `warm_limit`. Starting work is not limited by this number yet.

### Code

**TypeScript**

```ts
const limits = await vestra.usage.limits();
```

**Python**

```python
limits = vestra.usage.limits()
```

**CLI**

```bash
vestra usage caps
```

**HTTP**

```bash
curl "$VESTRA_BASE_URL/v1/limits" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**MCP**: No sample in your language for this route, so here it is over HTTP.

## `PATCH /v1/limits`

Its own page: [PATCH /v1/limits](https://vestra.ai/developers/reference/api/account/patch-v1-limits.md)

Set the day and month caps for the company and per user, and the alert thresholds, within what Vestra allows.

Your API key, scope `admin` or `all`. On the card: [`usage`](https://vestra.ai/developers/usage.md). Every call here is a checked sample.

### Request

Body:

- `org_daily_usd` (`number | null`): The company's cap per day (UTC), in US dollars with at most 2 decimals, up to `max_org_daily_usd`; null removes it. Limits: 0 to 1000000.
- `user_daily_usd` (`number | null`): The default cap per user per day (UTC), in US dollars, at most the company cap; null removes it. Limits: 0 to 1000000.
- `org_monthly_usd` (`number | null`): The company's cap per calendar month (UTC), in US dollars; null removes it. Limits: 0 to 1000000.
- `user_monthly_usd` (`number | null`): The default cap per user per calendar month (UTC), at most the company monthly cap; null removes it. Limits: 0 to 1000000.
- `alert_thresholds` (`integer[]`): Percents of a cap at which `usage.threshold_reached` fires, whole numbers from 1 to 100, at most four below 100. 100 always fires `usage.cap_reached`. Limits: at most 5 items. Example: `[50,80]`.

### Answers 200

- `object` (`"limits"`): Always `limits`.
- `mode` (`"live" | "test"`): The mode these limits belong to: the mode of the key in use.
- `org_daily_usd` (`number | null`): The company's spending cap per day (UTC), in US dollars, across all its users; null: none, up to the account's ceiling. Limits: 0 to 1000000.
- `user_daily_usd` (`number | null`): The default cap per user per day (UTC), in US dollars; a user's own `spend_cap_usd` wins. Null: none. Limits: 0 to 1000000.
- `max_org_daily_usd` (`number`): The highest the company's daily cap may be set, in US dollars. Ask Vestra to raise it. Limits: 0 to 1000000.
- `org_monthly_usd` (`number | null`): The company's cap per calendar month (UTC), in US dollars. Null (the default): none. Limits: 0 to 1000000.
- `user_monthly_usd` (`number | null`): The default cap per user per calendar month (UTC), in US dollars; a user's own `monthly_cap_usd` wins. Null (the default): none. Limits: 0 to 1000000.
- `alert_thresholds` (`integer[]`): Percents of a cap at which usage alerts fire, for the company and each user, per day and per month. Limits: at most 5 items.
- `rate_limits` (`object`): How fast you may call, fixed per account.
  - `requests_per_minute` (`integer`): Calls a key may make per minute before 429 `rate_limited`.
  - `concurrent_task_starts` (`integer`): How many computers your company may be waking at once with `warm`; past it, `warm` answers 429 `warm_limit`. Starting work is not limited by this number yet.

### Code

**TypeScript**

```ts
await vestra.usage.caps({ user_monthly_usd: 20 });
```

**Python**

```python
vestra.usage.caps(user_monthly_usd=20)
```

**CLI**

```bash
vestra usage caps --customer-monthly 20
```

**HTTP**

```bash
# See costs by user. This is what everything cost since the 1st, in billed US dollars.
curl "$VESTRA_BASE_URL/v1/usage?from=2026-10-01&group_by=user" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# See one user's spend.
curl "$VESTRA_BASE_URL/v1/usage?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Get a conversation's receipt. It lists every charge, newest first.
curl "$VESTRA_BASE_URL/v1/usage/line-items?conversation_id=conv_8f2k" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Set caps and alerts. This sets a monthly cap for each user, with alerts at 50% and 80% of it.
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**

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

## `GET /v1/usage`

Its own page: [GET /v1/usage](https://vestra.ai/developers/reference/api/account/get-v1-usage.md)

Exact billed spend and real task counts, by day, user, deployment, conversation, task, model or kind of spend, filtered by user, deployment or conversation. An erased user's spend stays in the totals, as the user unknown.

Your API key, scope `runtime` or `all`. On the card: [`usage`](https://vestra.ai/developers/usage.md). Every call here is a checked sample.

### Request

Query:

- `from` (`string`): The first day, inclusive, as YYYY-MM-DD (UTC). Absent: 29 days before `to`.
- `to` (`string`): The last day, inclusive, as YYYY-MM-DD (UTC). At most 31 days from `from` to `to`, counting both, so `to` is at most 30 days after `from`. Absent: today.
- `group_by` (`"day" | "user" | "deployment" | "conversation" | "task" | "model" | "category"`): How to split the spend: by `day` (the default), `user`, `deployment`, `conversation`, `task`, `model` or `category`.
- `user_id` (`string`): Only this user's spend, by your own id for them. Absent: every user's, and the company's own Bash's.
- `deployment_id` (`string`): Only this deployment's spend, by its `dep_` id.
- `conversation_id` (`string`): Only this conversation's spend, by its `conv_` id.

### Answers 200

- `object` (`"usage"`): Always `usage`.
- `mode` (`"live" | "test"`): The mode this spend is from: the mode of the key in use.
- `from` (`string`): The first day counted, as YYYY-MM-DD (UTC).
- `to` (`string`): The last day counted, as YYYY-MM-DD (UTC).
- `group_by` (`"day" | "user" | "deployment" | "conversation" | "task" | "model" | "category"`): How the rows are split.
- `data` (`object[]`): One row per group, with what it spent.
  - `key` (`string`): The group, as `group_by` says: the day (`2026-09-30`), your user id, the deployment's, conversation's or task's id, the model, or the category. `none` when the spend belongs to no such thing; `unknown` for spend with no user.
  - `spend_usd` (`number`): Billed US dollars, exact to 5 decimals. A group whose refunds exceed its charges reads 0.
  - `tasks` (`integer`): The pieces of work that spent in this group. Bash's own replies are not counted as work.
- `total` (`object`): The rows added up.
  - `spend_usd` (`number`): Billed US dollars across every row.
  - `tasks` (`integer`): Pieces of work across every row.

### Code

**TypeScript**

```ts
const usage = await vestra.usage();
```

**Python**

```python
usage = vestra.usage()
```

**CLI**

```bash
vestra usage
```

**HTTP**

```bash
# See costs by user. This is what everything cost since the 1st, in billed US dollars.
curl "$VESTRA_BASE_URL/v1/usage?from=2026-10-01&group_by=user" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# See one user's spend.
curl "$VESTRA_BASE_URL/v1/usage?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Get a conversation's receipt. It lists every charge, newest first.
curl "$VESTRA_BASE_URL/v1/usage/line-items?conversation_id=conv_8f2k" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Set caps and alerts. This sets a monthly cap for each user, with alerts at 50% and 80% of it.
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**

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

## `GET /v1/usage/line-items`

Its own page: [GET /v1/usage/line-items](https://vestra.ai/developers/reference/api/account/get-v1-usage-line-items.md)

The charges behind one task or one conversation, newest first: the receipt you can show your user.

Your API key, scope `runtime` or `all`. On the card: [`usage`](https://vestra.ai/developers/usage.md). Every call here is a checked sample.

### Request

Query:

- `limit` (`integer`): How many items per page, 1 to 100. Default 20.
- `cursor` (`string`): Where to go on from: the `next_cursor` of the page before, unchanged. Absent: the first page.
- `task_id` (`string`): The charges behind this piece of work, by its `task_` id. Send this or `conversation_id`.
- `conversation_id` (`string`): The charges behind this conversation, by its `conv_` id. Send this or `task_id`.

### Answers 200

- `data` (`object[]`): The items on this page.
  - `id` (`string`): The charge's id.
  - `object` (`"usage.line_item"`): Always `usage.line_item`.
  - `category` (`"orchestrator" | "intelligence" | "tools" | "compute" | "background" | "voice"`): The kind of spend: `orchestrator` (Bash's replies), `intelligence` (its thinking at work), `tools`, `compute`, `background` or `voice`.
  - `amount_usd` (`number`): Billed US dollars; a refund is negative. A subject's items add up to its `cost_usd`.
  - `model` (`string | null`): The model the charge was for; null when it was for no model.
  - `description` (`string`): What the charge was, in a few fixed words such as `Work by Bash`; a refund starts `Refund: `.
  - `created_at` (`string`): When it was charged, as an ISO 8601 date and time in UTC.
- `next_cursor` (`string | null`): Send it as `cursor` for the next page; null on the last page.

### Code

**TypeScript**

```ts
const receipt = await vestra.usage.lineItems({ conversation: 'conv_8f2k' });
```

**Python**

```python
receipt = vestra.usage.line_items(conversation="conv_8f2k")
```

**CLI**

```bash
vestra usage items conv_8f2k
```

**HTTP**

```bash
# See costs by user. This is what everything cost since the 1st, in billed US dollars.
curl "$VESTRA_BASE_URL/v1/usage?from=2026-10-01&group_by=user" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# See one user's spend.
curl "$VESTRA_BASE_URL/v1/usage?user_id=traveller-42" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Get a conversation's receipt. It lists every charge, newest first.
curl "$VESTRA_BASE_URL/v1/usage/line-items?conversation_id=conv_8f2k" \
  -H "Authorization: Bearer $VESTRA_API_KEY"

# Set caps and alerts. This sets a monthly cap for each user, with alerts at 50% and 80% of it.
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**

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

## `GET /v1/audit-log`

Its own page: [GET /v1/audit-log](https://vestra.ai/developers/reference/api/account/get-v1-audit-log.md)

Every key use, settings change and approval answer, newest first.

Your API key, scope `admin` or `all`. On the card: [`keys`](https://vestra.ai/developers/keys.md). Over HTTP it is made from the contract: fill in every value.

### Request

Query:

- `limit` (`integer`): How many items per page, 1 to 100. Default 20.
- `cursor` (`string`): Where to go on from: the `next_cursor` of the page before, unchanged. Absent: the first page.

### Answers 200

- `data` (`object[]`): The items on this page.
  - `id` (`string`): The entry's id.
  - `object` (`"audit_event"`): Always `audit_event`.
  - `mode` (`"live" | "test" | null`): The mode it happened in; null for a Vestra staff action on the company account.
  - `action` (`string`): What was done, such as `task.deleted` or `notebook.created`. Example: `task.deleted`.
  - `actor` (`object`): Who did it.
    - `kind` (`string`): Who did it: `key` (an API key), `console` (a person signed in), `client` (a session token) or `staff` (Vestra).
    - `id` (`string | null`): The key's id, the signed-in person's, or the session's; null when there is none to show.
  - `target` (`object`): What it was done to.
    - `type` (`string`): What kind of thing it was done to, such as `task`, `notebook` or `interaction`.
    - `id` (`string`): Its id.
  - `detail` (`map of any | null`): More about it, such as the fields that changed. It never holds a secret or a key; null when there is nothing more.
  - `created_at` (`string`): When it happened, as an ISO 8601 date and time in UTC.
- `next_cursor` (`string | null`): Send it as `cursor` for the next page; null on the last page.

### Code

**HTTP**

```bash
curl "$VESTRA_BASE_URL/v1/audit-log" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `GET /v1/models`

Its own page: [GET /v1/models](https://vestra.ai/developers/reference/api/account/get-v1-models.md)

The models a deployment may choose from.

Your API key, scope `runtime` or `all`. On the card: [`keys`](https://vestra.ai/developers/keys.md). Over HTTP it is made from the contract: fill in every value.

### Request

Nothing to send but the route.

### Answers 200

- `data` (`object[]`): The items on this page.
  - `id` (`string`): The model's id: what a deployment's `model` takes.
  - `object` (`"model"`): Always `model`.
  - `label` (`string`): Its name, as a person reads it.
- `next_cursor` (`string | null`): Send it as `cursor` for the next page; null on the last page.

### Code

**HTTP**

```bash
curl "$VESTRA_BASE_URL/v1/models" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `GET /v1/health`

Its own page: [GET /v1/health](https://vestra.ai/developers/reference/api/account/get-v1-health.md)

Is the API up. No key needed.

No key: anyone may call it. On the card: [`keys`](https://vestra.ai/developers/keys.md). Every call here is a checked sample.

### Request

Nothing to send but the route.

### Answers 200

- `object` (`"health"`): Always `health`.
- `ok` (`true`): Always true: the API is up and answering.
- `time` (`string`): The server's time, as an ISO 8601 date and time in UTC. Example: `2026-10-02T08:30:00.000Z`.

### Code

**HTTP**

```bash
# Nothing to install. Is the API up? No key needed.
curl "$VESTRA_BASE_URL/v1/health"
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.
