Vaults
Create, list, update, archive, and delete credential vaults and their credentials. Vaults are workspace-scoped; all endpoints authenticate with the team's API key.
For the concept — how credentials are resolved at session time, injection shapes, OAuth refresh, and multi-tenant patterns — see Credential vaults.
Vault object
Fields returned on vault endpoints.
| Field | Type | Description |
|---|---|---|
| id | string (uuid) | Stable vault identifier. |
| name | string | 1-200 chars. Display name. |
| description | string | null | Up to 500 chars. |
| status | "active" | "archived" | Archived vaults have their encrypted secrets purged but the row is retained for audit. |
| isDefault | boolean | Exactly one active vault per team can be the default. |
| metadata | Record<string, string> | Max 16 pairs; keys ≤ 64 chars, values ≤ 512 chars. Returned in clear text — do not store secrets here. |
| credentials | Credential[] | Present on GET /v1/mcp/vaults/:id. |
| createdAt | string (ISO 8601) | Timestamp. |
| updatedAt | string (ISO 8601) | Timestamp. |
| archivedAt | string | null | Set when archived. |
Credential object
| Field | Type | Description |
|---|---|---|
| id | string (uuid) | Credential identifier. |
| vaultId | string (uuid) | Parent vault. |
| name | string | null | Display label. |
| serverUrl | string (url) | The MCP server or upstream API URL this credential authenticates. |
| serverUrlNormalized | string | Lowercased and trailing-slash-stripped form used for matching. |
| hostPattern | string | null | Derived from serverUrl. Used by the proxy to match outbound requests. |
| authType | "bearer" | "oauth" | Secret shape. See the Auth shapes section below. |
| status | "active" | "archived" | Archived credentials have their encrypted auth purged. |
| metadata | Record<string, string> | Same caps as vault metadata. |
| lastResolvedAt | string | null | Set on successful proxy resolve. |
| lastError | string | null | Set on upstream auth failure (e.g. 401). Cleared on next success. |
token, accessToken, refreshToken, clientSecret) are write-only. They are never returned in API responses.Inject rules
The inject field on credential create / update tells the proxy where to put the secret in the outbound request. Defaults to { kind: "header", header: "Authorization", prefix: "Bearer " }, which matches MCP and most modern APIs.
Header — custom name, optional prefix
{
"kind": "header",
"header": "X-Subscription-Token",
"prefix": ""
}Rendered as X-Subscription-Token: <token>. Use this for Brave, SendGrid, and other APIs with custom header auth.
Query parameter
{
"kind": "query",
"param": "key"
}Rendered as ?key=<token>. Use for Google Maps and other query-auth APIs.
Basic auth
{
"kind": "basic",
"username": "api"
}Rendered as Authorization: Basic base64(username:<token>). The token is used as the password.
Auth shapes
The auth field on credential create / update is a discriminated union.
Bearer
{
"type": "bearer",
"token": "lin_api_REAL_TOKEN"
}OAuth (with optional refresh)
{
"type": "oauth",
"accessToken": "xoxp-...",
"refreshToken": "xoxe-1-...",
"clientId": "1234567890.0987654321",
"clientSecret": "abc123def456",
"tokenEndpoint": "https://slack.com/api/oauth.v2.access",
"tokenEndpointAuth": "client_secret_post",
"tokenType": "bearer",
"expiresAt": "2026-04-24T13:00:00Z",
"scope": "channels:read chat:write",
"resource": null
}All OAuth fields are optional on input, but at least accessToken is required for immediate use. Supply the full refresh block if you want the relay to refresh tokens on your behalf when they near expiry.
Supported tokenEndpointAuth modes: none, client_secret_basic, client_secret_post.
Vault endpoints
List Vaults
Returns all vaults in the workspace with their nested active credentials.
Response
curl https://relay.an.dev/v1/mcp/vaults \
-H "Authorization: Bearer YOUR_API_KEY"Create Vault
Creates a new vault in the workspace.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Display name, 1-200 chars. |
| description | string | null | Optional | Up to 500 chars. |
| metadata | Record<string, string> | Optional | Max 16 pairs; keys ≤ 64 chars, values ≤ 512 chars. |
{
"name": "Alice",
"description": "Per-user credentials",
"metadata": {
"external_user_id": "usr_abc123"
}
}Response
{
"vault": {
"id": "a1b2c3d4-5678-90ab-cdef-000000000002",
"name": "Alice",
"description": "Per-user credentials",
"status": "active",
"isDefault": false,
"metadata": { "external_user_id": "usr_abc123" },
"createdAt": "2026-04-24T12:00:00Z",
"updatedAt": "2026-04-24T12:00:00Z",
"archivedAt": null
}
}curl -X POST https://relay.an.dev/v1/mcp/vaults \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Alice", "metadata": { "external_user_id": "usr_abc123" } }'Returns 201 Created with the new vault.
Get Vault
Returns a single vault with its active credentials and coverage summary.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| vaultId | string (uuid) | Required | Vault ID. |
Response
The coverage field lists which declared hosts in any active deployment are covered vs. missing for this vault.
Update Vault
Updates vault metadata. Supports partial updates: any omitted field is left unchanged.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| vaultId | string (uuid) | Required | Vault ID. |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Optional | 1-200 chars. |
| description | string | null | Optional | Up to 500 chars. Pass null to clear. |
| metadata | Record<string, string> | Optional | Replaces the full metadata object. |
{
"name": "Alice (archived users)",
"metadata": { "archived_user": "true" }
}Archive or Delete Vault
By default, soft-archives the vault: the record stays for audit but encrypted secrets are purged. Pass ?force=true to hard-delete (requires the vault to already be archived or have no active credentials).
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| vaultId | string (uuid) | Required | Vault ID. |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| force | boolean | Optional | If true, hard-delete the row. Requires prior archive. |
Response
{
"success": true
}Set Default Vault
Marks this vault as the workspace default. Any previously-default vault is un-set in the same transaction. If a thread.run is made without explicit vaultIds, externalUserId, or agent-pinned vaultIds, the default vault is used.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| vaultId | string (uuid) | Required | Vault ID. |
Response
{
"success": true
}Credential endpoints
Create Credential
Creates a credential in the vault. A vault can hold up to 20 active credentials. Creating a second credential for the same serverUrl returns 409.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| vaultId | string (uuid) | Required | Parent vault. |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | null | Optional | Display label, up to 200 chars. |
| serverUrl | string (url) | Required | MCP server or upstream API URL. |
| auth | BearerAuth | OAuthAuth | Required | See Auth shapes above. |
| inject | InjectRule | Optional | How to attach the secret. Defaults to { kind: "header", header: "Authorization", prefix: "Bearer " }. See Inject rules below. |
| metadata | Record<string, string> | Optional | Same caps as vault metadata. |
{
"name": "Linear",
"serverUrl": "https://mcp.linear.app/mcp",
"auth": {
"type": "bearer",
"token": "lin_api_REAL_TOKEN"
},
"metadata": {}
}{
"name": "Slack",
"serverUrl": "https://mcp.slack.com/mcp",
"auth": {
"type": "oauth",
"accessToken": "xoxp-...",
"refreshToken": "xoxe-1-...",
"clientId": "1234567890.0987654321",
"clientSecret": "abc123def456",
"tokenEndpoint": "https://slack.com/api/oauth.v2.access",
"tokenEndpointAuth": "client_secret_post",
"tokenType": "bearer",
"expiresAt": "2026-04-24T13:00:00Z",
"scope": "channels:read chat:write",
"resource": null
}
}Response
{
"credential": {
"id": "c9d8e7f6-5678-90ab-cdef-000000000011",
"vaultId": "a1b2c3d4-5678-90ab-cdef-000000000002",
"name": "Linear",
"serverUrl": "https://mcp.linear.app/mcp",
"serverUrlNormalized": "https://mcp.linear.app/mcp",
"hostPattern": "mcp.linear.app",
"authType": "bearer",
"status": "active",
"metadata": {},
"createdAt": "2026-04-24T12:00:00Z",
"updatedAt": "2026-04-24T12:00:00Z",
"archivedAt": null,
"lastResolvedAt": null,
"lastError": null
}
}curl -X POST https://relay.an.dev/v1/mcp/vaults/VAULT_ID/credentials \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Linear",
"serverUrl": "https://mcp.linear.app/mcp",
"auth": { "type": "bearer", "token": "lin_api_REAL_TOKEN" }
}'Update Credential
Replaces the credential's auth payload and/or metadata. Pass the same shape as create.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| vaultId | string (uuid) | Required | Parent vault. |
| credentialId | string (uuid) | Required | Credential ID. |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| serverUrl | string (url) | Required | Same host as the existing credential. |
| auth | BearerAuth | OAuthAuth | Required | New secret material. |
| name | string | null | Optional | Display label. |
| inject | InjectRule | Optional | Replaces the injection rule (header / query / basic). |
| metadata | Record<string, string> | Optional | Replaces the full metadata object. |
Archive or Delete Credential
By default, soft-archives the credential: the row stays, encrypted secret is purged. Pass ?force=true to hard-delete.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| vaultId | string (uuid) | Required | Parent vault. |
| credentialId | string (uuid) | Required | Credential ID. |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| force | boolean | Optional | If true, hard-delete the row. Requires prior archive. |
Response
{
"success": true
}Errors
These endpoints share the standard error format documented on the Errors page. Vault-specific codes:
| Status | Code | When |
|---|---|---|
| 400 | validation_error | Body fails schema (missing/bad fields, metadata caps exceeded). |
| 404 | not_found | Vault or credential doesn't exist in your workspace. |
| 409 | conflict | Duplicate credential for the same host, or hard-delete attempted on an active resource. |
| 422 | credential_cap_exceeded | Attempted to create a 21st active credential in a vault. |
More resources
- Credential vaults — concept page with the 3-slot model, worked examples, and resolution order.
- Tools and MCPs — how MCP servers are declared on the agent.
- Chat — passing
vaultIdsandexternalUserIdat request time.