# Concepts (/en/docs/developers/vault/concepts)



A vault is a set of secrets; a secret is one credential made of a name, a value and allowed hosts. Every other feature is a way to create or update secrets.

## Vaults and secrets [#vaults-and-secrets]

Your API Key decides which vaults you can reach. A vault has a display name and up to 16 metadata pairs.

A secret's name is unique within its vault and cannot change after creation. It contains 1–128 uppercase letters, digits or underscores, may start with an underscore, and cannot start with a digit, for example `GITHUB_TOKEN`. Address a secret by its name or its `vsec_` ID. Lookup by an invalid name also returns 404 `SECRET_NOT_FOUND`.

Values are write-only: after you create or replace one, no endpoint returns it. Each replacement increments `version`.

## Allowed hosts [#allowed-hosts]

Every secret declares 1–16 `allowed_hosts`; an empty array returns 400 `INVALID_PARAMS`. Only requests to those hosts can use the secret.

| Entry            | Matches                                                  |
| ---------------- | -------------------------------------------------------- |
| `api.github.com` | That host only                                           |
| `*.example.com`  | Any subdomain of `example.com`, not `example.com` itself |
| `192.0.2.1`      | That IPv4 address only                                   |

Hosts are stored lowercase, without a trailing dot, with internationalized names in punycode. Duplicates are removed. There is no "any host" option. IPv6, single-label hosts (including `localhost`), `*.internal`, platform domains, and loopback, link-local, private or CGNAT IPv4 addresses return 400 `INVALID_PARAMS`.

## Two kinds of secret [#two-kinds-of-secret]

| kind     | Where the value comes from                                    | Status                                   |
| -------- | ------------------------------------------------------------- | ---------------------------------------- |
| `static` | The value you supply                                          | `active`                                 |
| `oauth`  | An access token from authorization, refreshed by the platform | `active`, `refreshing`, `refresh_failed` |

Either kind becomes `archived` when archived.

## Templates [#templates]

Templates prefill the name, allowed hosts and kind for common services, so you only supply the value. `GET /vault/v1/templates` lists them, with static templates `github`, `openai`, `anthropic`, `stripe`, and OAuth templates `linear`, `atlassian`. GitHub prefills `GITHUB_TOKEN`, `api.github.com` and `uploads.github.com`. When adding a static secret, `template_id` records its source as `metadata.template`.

`metadata.source` and `metadata.template` are system-owned keys. Callers cannot write them, and they do not count toward the 16-label limit. Vault and secret metadata updates merge by key: strings upsert, `null` deletes, and omitted keys remain. For secrets, `description: null` clears the description; omission preserves it.

## Import a .env file [#import-a-env-file]

`POST /vault/v1/vaults/{vault_id}/secrets:import` creates several static secrets at once. Send `dotenv` (multi-line `KEY=VALUE` text) or a `lines` array, not both; For new secrets, default `allowed_hosts` applies to lines without their own hosts. Replacing an existing name changes only its value. Hosts change only from that line's own `allowed_hosts`, never from top-level defaults. The result lists created, replaced and failed lines; failed lines carry a line number and error code, and the other lines are still written.

## One-click authorization [#one-click-authorization]

Linear (CIMD: Client ID Metadata Document) and Atlassian (DCR: Dynamic Client Registration) support one-click authorization without registering an app first. Add credentials manually for services requiring app registration: GitHub, HubSpot, Asana and Vercel. Asana is not a one-click authorization template.

1. Call `POST /vault/v1/authorizations` with an authorization-capable `template_id`, or the `resource_url` to authorize.
2. Give the returned `connect_url` to the end user. The link is valid for 30 minutes and works once.
3. The user sees who is asking, the target service and the requested permissions, approves, and finishes on the service's own page.
4. The vault then holds an `oauth` secret named `secret_name`. `GET /vault/v1/authorizations/{id}` reports the status: `pending`, `authorizing`, `completed`, `failed` or `expired`.

If the vault already has a secret with that name, authorization updates it in place: the ID stays the same and `version` increments.

To send the user back to your app afterwards, pass `return_url`. It must be on your return URL allowlist, set with `PUT /vault/v1/settings/return_urls`: at most 10 https URLs, matched exactly. The return carries `authorization_id` and `status`.

## Refresh and validation [#refresh-and-validation]

The platform refreshes an `oauth` secret's access token before it expires. If the service refuses the refresh, the secret becomes `refresh_failed` and `oauth.last_error` explains why; authorizing again or replacing the value makes it `active` again. If the service is temporarily unavailable, the secret stays `active` and the refresh is retried later.

`POST …/secrets/{secret}/validate` probes the service once with the current token and returns `valid`, `invalid` or `unknown`; it applies to `oauth` secrets only. A caller that receives a 401 from the service can call `POST …/secrets/{secret}/refresh` to refresh immediately; this also requires an `oauth` secret. Static validation returns 400 `VALIDATE_UNSUPPORTED`; static refresh returns 400 `SECRET_KIND_MISMATCH`.

## Audit events [#audit-events]

Events record creation, updates, archival, deletion, use, refresh and authorization outcomes, never secret values. `GET /vault/v1/vaults/{vault_id}/events` lists newest first. Filter by `secret` name or ID; unknown secrets return an empty list. Update events list changed fields, such as `value` and `allowed_hosts`, in `detail.changed`.

Events have no time-based expiry. Archiving preserves events; deleting a vault or secret removes its history and leaves a deletion record. `limit` defaults to 50 and caps at 200; invalid limits or cursors return 400 `INVALID_PARAMS`. See the [event reference](/en/docs/api/native/vault/vaults) for all event types.

## Archive and delete [#archive-and-delete]

Archiving a vault archives every secret in it and clears their values; nothing more can be written to it. Deleting a vault removes every secret and cannot be undone. Individual secrets can also be archived or deleted.

## Claude Managed Agents vaults compatibility [#claude-managed-agents-vaults-compatibility]

The same vaults are reachable with the [Claude Managed Agents SDK compatibility API](/en/docs/api/compatible/anthropic/managed-agents) (`/anthropic/v1/vaults`). Every request needs an API Key, `anthropic-version: 2023-06-01`, and `anthropic-beta` containing `managed-agents-2026-04-01`. The beta header may include other comma-separated betas. Lists paginate with `page`, not `cursor`; credentials are addressed only by `vsec_` ID. Setting a vault's `display_name` to `null` returns 400.

Errors have shape `{type: "error", error: {type, message}, request_id}`; HTTP 409 maps to `conflict_error`. Non-retryable errors, including 409, carry `x-should-retry: false` to prevent SDK retries.

| Credential type        | Matching secret                                                                                          |
| ---------------------- | -------------------------------------------------------------------------------------------------------- |
| `environment_variable` | Named `secret_name`, allowed hosts from `networking.allowed_hosts`; `networking` supports `limited` only |
| `static_bearer`        | Allowed host is the host of `mcp_server_url`; the name is generated as `MCP_<HOST>`                      |
| `mcp_oauth`            | Creation is not available yet and returns 501; use one-click authorization instead                       |

Secrets created through `/vault/v1` appear there as `environment_variable`, with `injection_location` substituting in request headers only.
