Custodian
ReferenceHTTP API

Vaults

Bootstrap a vault with the deployment credential, and read the vault a credential belongs to.

A vault is one tenant's encrypted store. Bringing one into existence is the one thing you do before there is any principal to authenticate as, so it uses a different credential from every other route.

POST /v1/vaults — bootstrap a vault

This route creates a vault and its first operator principal in a single transaction, and returns that operator's token. It is the only route authenticated by the deployment bootstrap credential rather than a principal token, because the vault — and so its principals — does not exist yet. The credential is set on the server as CUSTODIAN_BOOTSTRAP_CREDENTIAL (see Deployment).

POST /v1/vaults
Authorization: Bearer <bootstrap credential>
Content-Type: application/json

{
  "vault": {
    "slug": "acme",
    "display_name": "Acme Insurance",
    "region": "af-south-1"
  }
}

slug, display_name and region are all required. A request missing one is refused as a malformed request (400).

region names the AWS region where the vault's key material is held: the master key that wraps everything the server stores for the vault is created in that region and stays there. Supply it as an AWS region name, like af-south-1 above. The value is recorded as given and is not checked against a list on this route, so the only refusal on this field is the 400 for a missing one. Choose it deliberately — it is fixed for the life of the vault, and no route changes it afterward.

On success the response is 201 Created:

{
  "vault": {
    "slug": "acme",
    "display_name": "Acme Insurance",
    "region": "af-south-1",
    "created_at": "2026-08-12T09:00:00Z",
    "keys_destroyed": false,
    "key_material_restorable_days": 30,
    "backup_retention_days": 35
  },
  "operator_token": "<the first operator's token>"
}

Capture operator_token now. It is returned exactly once, here, and no later route ever returns it. Lose it before you have used it to mint another principal or enrol a device, and the only way to administer the vault is gone.

A wrong, missing or unconfigured bootstrap credential is refused with 403, the same way for all three, so the response cannot tell them apart.

A slug already in use is refused with a bare 409 Conflict and no body — there is no error code for it.

GET /v1/vaults/:slug — read your own vault

Authenticated as a principal, this returns the vault that principal belongs to. The :slug must be that vault's own slug; any other slug — another tenant's, or one that does not exist — is refused with the shared not_found response, so the two cannot be told apart and you cannot enumerate other tenants.

GET /v1/vaults/acme
Authorization: Bearer <principal token>
{
  "slug": "acme",
  "display_name": "Acme Insurance",
  "region": "af-south-1",
  "created_at": "2026-08-12T09:00:00Z",
  "keys_destroyed": false,
  "key_material_restorable_days": 30,
  "backup_retention_days": 35
}

keys_destroyed reports whether the vault's keys have been destroyed. The recovery envelope, its salt and its parameters are not returned by any vault route. They are returned only by GET /v1/recovery, to an operator, when the vault has no live device left.

key_material_restorable_days and backup_retention_days are the deployment's two grace-period windows, in days, so you can see them before you confirm a destroy:

  • key_material_restorable_days is how long the AWS key material behind the server-side layer stays restorable after a destroy — the window in which a destroyed vault's tokenised values could still be recovered by anyone with the AWS account.
  • backup_retention_days is how long a database backup is retained — the window in which a backup taken before a destroy still holds the wrapped copies and the recovery envelope.

These are the windows a key destruction reports under surviving_copies. Both are set on the server and are the same for every vault on the deployment; the POST /v1/vaults create response carries them too.

On this page