> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentsentinel.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# AgentVault Credentials

> Credentials for governed tools: write-only secrets, envelope encryption, key rotation and tool bindings.

## Overview

AgentVault holds the credentials your governed tools need (an upstream API key, a bearer token) so that agents never hold them. An operator stores a credential once and binds it to a governed tool. Agents can never create, read, rotate or bind a credential.

A credential is a **single opaque string secret**. It is **write-only**: no response, ledger row or log line ever contains it, or its base64.

## Credential references and the local store

A credential is a **reference**: `provider` names where the secret lives and `ref` locates it there. The only provider available today is `local`. With `local`, the secret is kept in the Agent Sentinel database as an envelope-encrypted payload:

* Each secret is encrypted with its own random AES-256-GCM **data key**.
* The data key is wrapped by a versioned **master key**, identified by `key_id`.
* The payload is bound to its organization and credential id, so a ciphertext copied into another row fails to open.

If the credential store is unavailable, every route returns `503 agentvault_local_store_disabled` or `503 agentvault_key_unavailable`, and nothing is stored or changed.

> [!NOTE]
>
> **Vault connectors are not available yet.** External secret managers (HashiCorp Vault, cloud secret managers) are planned as additional providers. Until then, `local` is the only provider.

## Who can call these routes

* **User sessions only.** Every route refuses any API key, bound to an agent or not, with `403 user_session_required`.
* **Reads** (`GET`) require membership of your active organization.
* **Writes** require the `OWNER` or `ADMIN` role. A `MEMBER` gets `403 org_admin_required`.
* A credential or governed tool of another organization is a `404`. A credential can only be bound to a tool of its own organization.

## Endpoints

All routes are under `/api/v1/agentvault/credentials`.

| Method | Path | Body | Result |
| - | - | - | - |
| `POST` | `/` | `{name, secret, scopes}` | `201` with the credential |
| `GET` | `/` | | `{data, count}` for your organization |
| `GET` | `/{id}` | | The credential |
| `POST` | `/{id}/revoke` | | The revoked credential |
| `POST` | `/rotate` | `{to_key_id}` | `{rewrapped: n}` |
| `PUT` | `/{id}/bindings/{tool_id}` | `{credential_injection}` | The tool's binding |
| `DELETE` | `/{id}/bindings/{tool_id}` | | The cleared binding |

### Create a credential

```bash theme={null}
POST /api/v1/agentvault/credentials
```

```json theme={null}
{
  "name": "stripe-live",
  "secret": "sk_live_...",
  "scopes": ["charges:write"]
}
```

* `name` matches `^[A-Za-z0-9_.-]{1,128}$` and is unique in the organization (`409 credential_name_exists`).
* `secret` is a non-empty string of at most 8 KiB, UTF-8 encoded. A validation error never echoes it.
* `scopes` is a list of at most 32 strings of at most 64 characters each.

The response carries `id`, `organization_id`, `name`, `provider`, `ref`, `scopes`, `key_id`, `created_by_user_id`, `created_at`, `rotated_at` and `revoked_at`. It never carries the secret or any envelope field (`wrapped_data_key`, `wrap_nonce`, `data_nonce`, `ciphertext`).

### Revoke a credential

```bash theme={null}
POST /api/v1/agentvault/credentials/{id}/revoke
```

Revocation is permanent and idempotent. A revoked credential cannot be bound (`409 credential_revoked`) and is never re-wrapped.

### Bind a credential to a governed tool

```bash theme={null}
PUT /api/v1/agentvault/credentials/{id}/bindings/{tool_id}
```

```json theme={null}
{
  "credential_injection": {
    "type": "header",
    "header": "Authorization",
    "prefix": "Bearer "
  }
}
```

The secret is injected into exactly one header:

* `type` must be `"header"`.
* `header` matches `^[A-Za-z0-9-]{1,64}$` and is not, in any letter case, `Host`, `Content-Length`, `Transfer-Encoding`, `Cookie`, `Connection` or `X-Sentinel-Decision-Token`.
* `prefix` is optional: at most 32 printable ASCII characters, so it has no CR or LF. An explicit `null` is rejected; omit the key for no prefix.
* Any other key is rejected with `422`.

A tool has at most one credential. Binding a tool that is already bound replaces its binding.

`DELETE /{id}/bindings/{tool_id}` clears the binding, but only if the tool is bound to that credential. Otherwise it returns `404 credential_binding_not_found`.

## Key rotation

Each credential records the `key_id` of the master key that wraps its data key. Agent Sentinel manages the master keys. When a new master key is available, an owner or admin moves the organization's credentials onto it:

```http theme={null}
POST /api/v1/agentvault/credentials/rotate
Content-Type: application/json

{ "to_key_id": "<new key_id>" }
```

Rotation re-wraps only the data key. The ciphertext is unchanged and the secret itself is never decrypted. All of your organization's non-revoked credentials move in one transaction: if any fails, none move. An unknown or malformed `to_key_id` returns `503 agentvault_key_unavailable` before anything changes. A `to_key_id` that is empty, longer than 64 characters or contains control characters returns `422`.

## Audit trail

Every write appends exactly one chained, signed ledger row, in the same transaction as the change. Its `outputs.type` is `credential_created`, `credential_revoked`, `credential_rotated`, `credential_bound` or `credential_unbound`. `outputs` carries the credential id(s), the tool id for bindings, and the count for a rotation. The row's `actor` is the user who made the change. Each operator action is recorded under its own completed run.

If a signing key is configured and the row cannot be signed, the route returns `500 audit_signing_failed` and the change is rolled back. A credential change is never stored without its audit row.

## Leases and injection

Binding a credential does not hand it to anyone. A credential is leased and its secret injected into the bound header **only after the gateway returns an ALLOW decision** for that tool call. The secret is added to the outbound request by the platform and is never returned to the agent. Lease minting and injection ship in a later release; this release covers storage, rotation and bindings.

## Error codes

| Status | `detail` | Meaning |
| - | - | - |
| `403` | `user_session_required` | Called with an API key |
| `403` | `org_admin_required` | Write by a non-admin member |
| `404` | `credential_not_found`, `governed_tool_not_found` | Missing, or belongs to another organization |
| `404` | `credential_binding_not_found` | The tool is not bound to this credential |
| `409` | `credential_name_exists` | Duplicate name in the organization |
| `409` | `credential_revoked` | Binding a revoked credential |
| `422` | | Body failed validation; nothing was written |
| `500` | `audit_signing_failed` | The audit row could not be signed; nothing was written |
| `503` | `agentvault_local_store_disabled` | The local store is disabled |
| `503` | `agentvault_key_unavailable` | A master key or the active key is missing or invalid |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.