API Docs
Vaults

Vaults API

All vault endpoints require a JWT access token in the Authorization header.

List Vaults

Retrieve all vaults the authenticated user has access to, including their encrypted vault keys.

GET /vaults

Response — 200 OK

{
  "vaults": [
    {
      "vaultId": "660e8400-e29b-41d4-a716-446655440000",
      "vaultName": "Personal",
      "vaultType": "personal",
      "encryptedVaultKey": "base64-encoded-wrapped-key",
      "senderId": "550e8400-e29b-41d4-a716-446655440000",
      "wrapSignature": "base64-encoded-signature",
      "role": "owner",
      "createdAt": "2026-04-01T10:00:00Z",
      "updatedAt": "2026-04-01T10:00:00Z"
    }
  ]
}
FieldDescription
vaultIdUUID of the vault
vaultNamePlaintext vault name
vaultTypepersonal or shared
encryptedVaultKeyBase64-encoded vault key, wrapped for this user
senderIdUUID of the user who wrapped the key
wrapSignatureEd25519 signature over the wrapped key (for MITM prevention)
roleUser's role in this vault: owner, admin, or member

Example

curl -H "Authorization: Bearer <access_token>" \
     https://vault.example.com/api/v1/vaults

Create Vault

Create a new vault. The client generates a 256-bit vault key, wraps it, and signs the wrap.

POST /vaults

Request

{
  "name": "Work Credentials",
  "type": "personal",
  "encryptedVaultKey": "base64-encoded-wrapped-key",
  "wrapSignature": "base64-encoded-signature"
}
FieldTypeDescription
namestringVault display name
typestringpersonal or shared
encryptedVaultKeystringBase64-encoded vault key wrapped with the user's RSA public key (shared) or AES Key Wrap (personal)
wrapSignaturestringBase64-encoded Ed25519 signature over the wrapped key bytes

Response — 201 Created

{
  "vaultId": "770e8400-e29b-41d4-a716-446655440000",
  "vaultName": "Work Credentials",
  "vaultType": "personal",
  "encryptedVaultKey": "base64-encoded-wrapped-key",
  "senderId": "550e8400-e29b-41d4-a716-446655440000",
  "wrapSignature": "base64-encoded-signature",
  "role": "owner",
  "createdAt": "2026-04-06T12:00:00Z",
  "updatedAt": "2026-04-06T12:00:00Z"
}

Example

curl -X POST https://vault.example.com/api/v1/vaults \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Work Credentials",
    "type": "personal",
    "encryptedVaultKey": "d3JhcHBlZC12YXVsdC1rZXk=",
    "wrapSignature": "c2lnbmF0dXJlLW92ZXItd3JhcA=="
  }'

The vault key is a random 256-bit AES key generated by the client. For personal vaults it is wrapped with AES-256-KW using the user's stretched key. For shared vaults it is wrapped with RSA-OAEP-SHA256 using each member's public key.


Share Vault (Add Member)

Add a new member to a shared vault. The client wraps the vault key with the recipient's RSA public key and signs the wrap.

POST /vaults/:vaultId/members

Request

{
  "recipientUserId": "user-uuid",
  "encryptedVaultKey": "base64-encoded-wrapped-key",
  "wrapSignature": "base64-encoded-signature",
  "role": "member"
}
FieldTypeRequiredDescription
recipientUserIdstringyesUUID of the user to invite
encryptedVaultKeystringyesVault key wrapped with recipient's RSA public key
wrapSignaturestringyesEd25519 signature over the wrapped key bytes
rolestringyesRole to assign: admin or member

Response — 204 No Content

No response body.

Errors

  • 400 INVALID -- Validation failed
  • 404 NOT_FOUND -- Vault or recipient not found

Example

curl -X POST https://vault.example.com/api/v1/vaults/660e8400-.../members \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "recipientUserId": "550e8400-e29b-41d4-a716-446655440001",
    "encryptedVaultKey": "d3JhcHBlZC1mb3ItcmVjaXBpZW50",
    "wrapSignature": "c2lnbmF0dXJlLW92ZXItd3JhcA==",
    "role": "member"
  }'

Remove Member

Remove a member from a shared vault. Returns whether a rekey is required and the remaining members with their wrapped keys.

DELETE /vaults/:vaultId/members/:userId

Response — 200 OK

{
  "rekeyRequired": true,
  "remainingMembers": [
    {
      "vaultId": "660e8400-...",
      "vaultName": "Shared Vault",
      "vaultType": "shared",
      "encryptedVaultKey": "base64-wrapped-key",
      "senderId": "550e8400-...",
      "wrapSignature": "base64-signature",
      "role": "owner"
    }
  ]
}

Errors

  • 400 INVALID -- Cannot remove the last owner
  • 404 NOT_FOUND -- Vault or member not found

Example

curl -X DELETE -H "Authorization: Bearer <access_token>" \
     https://vault.example.com/api/v1/vaults/660e8400-.../members/550e8400-...
⚠️

After removing a member, the client must rekey the vault if rekeyRequired is true. This involves generating a new vault key, re-encrypting all items, and re-wrapping the key for each remaining member.


Rekey Vault

Atomically re-encrypt all items with a new vault key and re-wrap the key for all remaining members. Typically called after removing a member.

POST /vaults/:vaultId/rekey

Request

{
  "newKeys": [
    {
      "userId": "user-uuid",
      "encryptedVaultKey": "base64-new-wrapped-key",
      "wrapSignature": "base64-signature"
    }
  ],
  "items": [
    {
      "itemId": "item-uuid",
      "encryptedData": "base64-re-encrypted-blob",
      "encryptedName": "base64-re-encrypted-name"
    }
  ]
}
FieldTypeDescription
newKeysarrayOne entry per remaining member with the new wrapped vault key
newKeys[].userIdstringMember's user ID
newKeys[].encryptedVaultKeystringNew vault key wrapped for this member
newKeys[].wrapSignaturestringEd25519 signature over the new wrapped key
itemsarrayAll items re-encrypted with the new vault key
items[].itemIdstringItem ID
items[].encryptedDatastringItem data re-encrypted with new vault key
items[].encryptedNamestringItem name re-encrypted with new vault key

Response — 204 No Content

No response body.

Errors

  • 400 INVALID -- Validation failed (missing members or items)
  • 404 NOT_FOUND -- Vault not found

Example

curl -X POST https://vault.example.com/api/v1/vaults/660e8400-.../rekey \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "newKeys": [
      {
        "userId": "550e8400-...",
        "encryptedVaultKey": "bmV3LXdyYXBwZWQta2V5",
        "wrapSignature": "bmV3LXNpZ25hdHVyZQ=="
      }
    ],
    "items": [
      {
        "itemId": "880e8400-...",
        "encryptedData": "cmUtZW5jcnlwdGVkLWRhdGE=",
        "encryptedName": "cmUtZW5jcnlwdGVkLW5hbWU="
      }
    ]
  }'