# POST /v1/deployments/{deployment_id}/versions

> Save a new version: instructions, language, MCP servers and this version's own tool policy for them (mcp_tool_policy, on top of each server's floor), allowed apps, approval rules, model, memory mode, notebooks, limits per task.

In [Deployments API](https://vestra.ai/developers/reference/api/deployments.md): How your Bash is set up, in versions: apply a whole vestra.yaml, promote, roll back, and the interview that drafts the file.

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

Save a new version: instructions, language, MCP servers and this version's own tool policy for them (mcp_tool_policy, on top of each server's floor), allowed apps, approval rules, model, memory mode, notebooks, limits per task.

Your API key, scope `setup` or `all`. On the card: [`deploy`](https://vestra.ai/developers/deploy.md). Over HTTP it is made from the contract: fill in each {…} and every value.

## Request

Path:

- `deployment_id` (`string`, required): The deployment's id (`dep_...`). The /v1/deployments routes also take its name.

Headers:

- `Idempotency-Key` (`string`): Send the same key to retry this call safely for 24 hours.

Body:

- `manifest` (`map of any`, required): The version's settings as a manifest: `instructions`, `language`, `model`, `mcp_servers`, `mcp_tool_policy`, `apps`, `approvals`, `memory`, `notebooks`, `skills`, `tools`, `environment`, `browser` and `limits`. A field left out takes its default.
- `note` (`string`): A note on this version, up to 500 characters, such as what changed. Limits: at most 500 characters. Example: `Adds the refund tool.`.

## Answers 201

- `object` (`"deployment_version"`): Always `deployment_version`.
- `deployment_id` (`string`): The deployment's id. Example: `dep_8f2k`.
- `version` (`integer`): A version number: 1 is the first, and each save adds one.
- `manifest` (`object`): The version's settings, every default filled in.
  - `base` (`"bash"`): What the deployment is built on: always `bash`. Default: `bash`.
  - `instructions` (`string`): What Bash does here, and how, in plain words, up to 8,000 characters. Empty by default. Default: empty. Limits: at most 8000 characters.
  - `language` (`string`): The language Bash speaks: a tag such as `en`, `hi` or `pt-BR`, or `auto` (the default) to follow each user. Default: `auto`.
  - `mcp_servers` (`string[]`): The MCP servers Bash may call, by their `mcp_` ids (POST /v1/mcp-servers). Default: `[]`.
  - `mcp_tool_policy` (`object[]`, may be absent): This version's own tool policy for its MCP servers, on top of each server's floor. Absent (every version saved before it): each server's own policy alone. Limits: at most 50 items.
    - `server` (`string`): The MCP server, by its `mcp_` id, one `mcp_servers` lists.
    - `deny` (`string[]`): Tools of the server this version never calls, on top of the server's own floor. Default: `[]`. Limits: at most 200 items.
    - `require_approval` (`string[]`): Tools of the server that wait for the person's yes in this version, on top of the server's own floor. Default: `[]`. Limits: at most 200 items.
    - `run_without_asking` (`string[]`, may be absent): Tools of the server this version runs without waiting for the person's yes. Every other tool of the server waits for it, unless the server marks the tool read-only (readOnlyHint). Limits: at most 200 items.
  - `apps` (`object`): Which apps its users may connect. Default: `{"allow":"all"}`.
    - `allow` (`"all" | string[]`): The apps its users may connect: `all` (the default), or a list such as `["gmail"]`. Default: `all`.
  - `approvals` (`object`): What Bash asks the person before doing. Default: `{"always":["payment","booking","cancellation"]}`.
    - `always` (`("payment" | "booking" | "cancellation" | "send_email" | "publish" | "delete")[]`): What Bash asks the person before doing: any of `payment`, `booking`, `cancellation`, `send_email`, `publish` and `delete`. Default the first three. A system approval enforces what `approvals` lists for your own functions and your MCP servers' tools that wait for a yes; in a browser or a connected app, Bash is instructed to ask. Default: `["payment","booking","cancellation"]`.
  - `model` (`string`): `default` (the default), or a model id from GET /v1/models. Default: `default`.
  - `memory` (`object`): What Bash remembers about each user here. Default: `{}`.
    - `mode` (`"vestra" | "customer" | "none"`, may be absent): `vestra`, `customer` or `none`. Absent: the company's `memory_mode`.
  - `notebooks` (`string[]`): What Bash reads before it answers: notebooks, by their `nb_` ids. Default: `[]`.
  - `skills` (`string[]`): Skills Bash may use: company skills by their `skl_` ids, or skills Bash already has, by name. Default: `[]`.
  - `tools` (`object[]`): Tools your own code answers: Bash's call reaches you as an interaction of type `tool_call`, after the person's yes unless the tool is marked `read_only` or `run_without_asking`. Default: `[]`.
    - `name` (`string`): The tool's name: lowercase letters, digits and underscores, starting with a letter, up to 64. Example: `get_booking`.
    - `description` (`string`): What the tool does, so Bash knows when to call it, up to 1024 characters. Limits: 1 to 1024 characters. Example: `A booking by its PNR`.
    - `input_schema` (`map of any`): Its arguments, as JSON Schema with `type: object`. Bash's call fills them in. Example: `{"type":"object","properties":{"pnr":{"type":"string"}},"required":["pnr"]}`.
    - `timeout_s` (`integer`): Seconds Bash waits for your answer, 5 to 600. Default 120; after it, the call expires. Default: `120`. Limits: 5 to 600.
    - `read_only` (`boolean`, may be absent): true: the tool changes nothing (a lookup), so Bash calls it without waiting for the person's yes. Absent: false.
    - `run_without_asking` (`boolean`, may be absent): true: the tool changes something that needs no yes, so Bash calls it without waiting for the person's yes. Absent: false.
  - `environment` (`object`, may be absent): Bash's computer, ready before the first task: a setup script, environment variables, repositories and files. Absent: a plain computer.
    - `setup_script` (`string`, may be absent): A shell script run once on each computer before its first task, and again when the environment changes, up to 20,000 characters. Absent: none. Limits: at most 20000 characters.
    - `env` (`map of string`): Environment variables on the computer, by name. A credential is `${secret:NAME}`, filled in from your secrets, never a raw value. Default: `{}`. Example: `{"REGION":"ap-south-1"}`.
    - `repos` (`object[]`): Repositories cloned onto the computer. Default: `[]`.
    - `files` (`object[]`): Files placed on the computer. Default: `[]`.
  - `browser` (`object`, may be absent): The sites a browser fetch may open, and the pages the browser never opens or acts on. Absent: a fetch opens nothing, and no page is closed.
    - `allowed_domains` (`string[]`, may be absent): The sites a browser fetch may open, as bare host names such as `ixigo.com`, which also allows `www.ixigo.com`. Up to 20. Absent: a browser fetch opens nothing. Limits: 1 to 20 items. Example: `["ixigo.com"]`.
    - `blocked_paths` (`string[]`, may be absent): Pages the browser never opens or acts on, as a site and the start of a path, such as `ixigo.com/payments/`. Enforced in code for a browser fetch (403 page_blocked) and Bash's own browser tools. Up to 20. Limits: 1 to 20 items. Example: `["ixigo.com/payments/"]`.
  - `routines` (`object[]`): Not used: a version holds no routines. Write them in vestra.yaml, or make them with POST /v1/routines. Default: `[]`.
    - `name` (`string`): The routine's name.
    - `schedule` (`string`): When it runs.
    - `message` (`string`): What it asks.
  - `limits` (`object`): What one piece of work may spend. There is no time limit. Default: `{"spend_per_task_usd":2}`.
    - `spend_per_task_usd` (`number`): The most one piece of work may spend, in US dollars, above 0 and at most 100. Default 2. Default: `2`. Limits: above 0, at most 100.
- `manifest_sha256` (`string`): A hash of the settings: two versions with the same hash run the same.
- `skill_pins` (`object[]`): The company skills this version runs, each at the version of the skill current when it was saved.
  - `skill` (`string`): The company skill, by its `skl_` id.
  - `slug` (`string`): The skill's name.
  - `pressing_sha256` (`string | null`): The content hash of the skill's version when this version was saved: the one it runs. Null when none was recorded.
- `note` (`string | null`): The note it was saved with; null when none.
- `created_by` (`string`): Who saved it: a key's id (`key_...`), `console` (a person in the console), `client` (a session token from your app) or `interview` (the draft the interview wrote).
- `created_at` (`string`): When it was saved, as an ISO 8601 date and time in UTC.
- `is_draft` (`boolean`): True when test keys run this version.
- `is_live` (`boolean`): True when live keys run it.
- `test_verdict` (`"passed" | "failed" | "none"`, may be absent): Its latest test run's verdict: `passed`, `failed`, or `none` (never tested). Only when you read one version (`GET .../versions/{version}`); absent in the list.
- `interview` (`object`, may be absent): The interview's closing words, on the version the interview saved; absent on every other version.
  - `summary` (`string`): What the draft sets Bash up to do, in the interview's words.
  - `tools` (`object[]`): The tools the file declares that your own code answers.
    - `name` (`string`): The tool's name.
    - `description` (`string`): What it does.
  - `per_task_usd` (`number`): The spending cap per piece of work, in US dollars.
  - `per_task_usd_is_default` (`boolean`): True when that cap is the default, 2, because the file left it.
  - `tests` (`integer`): How many starter tests the file holds.
  - `next_steps` (`string[]`): What to do next, one plain line each, in order.

## Code

**HTTP**

```bash
curl -X POST "$VESTRA_BASE_URL/v1/deployments/{deployment_id}/versions" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "manifest": {}
  }'
```

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