API reference
Passwords vault
The Authenticator app's encrypted vault: the account and its sealed keys, vaults, items, devices and resets. The server stores ciphertext; every change is signed by the vault's own key.
See Passwords. These routes are at /v1/me/vault, reachable only with the Authenticator app's own sign-in (a mobile token issued to the authenticator app): organization keys, Caity and the other apps get 403. Item contents and vault names are encrypted on the phone before they're sent, so the server holds ciphertext, padded sizes and a little metadata (when things changed, which devices synced).
Signed writes. Reads need only the sign-in. Every request that changes something carries X-Vault-Signature: t=<epoch milliseconds>,d=<device id>,s=<signature>: an Ed25519 signature (base64url) by the vault's signing key over these lines:
cactive-vault/v1/request
<METHOD>
<path>
<t>
<device id>
<base64url SHA-256 of the exact body, or of "" without one>t must be within 5 minutes of the server's clock (otherwise 403 with code: "clock" and serverTime). Making a vault is signed with the new vault's own key; a device that was removed gets 403 with code: "device_removed".
Revisions. Every write names the revision it replaces; a stale one gets 409, and the app merges field by field and tries again.
Resets. An unsigned DELETE asks for a reset: after 7 days, unless a device that can open the vault cancels it, the vault can be deleted and made again. The account's email address is told when a reset is asked for. A signed DELETE deletes the vault at once.
GET /v1/me/vault
Your Passwords vault as stored: account (the keyset sealed under your master password and Secret Key, the Argon2id settings and salt, the public keys) or null, vaults with their wrapped keys and counters, devices, a pending reset and limits. Everything that says what's in the vault is encrypted on your devices. From the Authenticator app's sign-in only.
Auth: user access token or platform agent key
Response 200
{
account: null | {
createdAt: number
updatedAt: number
accountId: string
kdfSalt: string
keyset: string
keysetRevision: number
encryptionPublicKey: string
signingPublicKey: string
kdf: {
alg: "argon2id"
m: number
t: number
p: number
}
}
vaults: {
vaultId: string
kind: "personal"
role: "owner"
envelope: string
attrs: string
attrsRevision: number
revision: number
itemCount: number
createdAt: number
updatedAt: number
}[]
devices: {
deviceId: string
name: string
platform: string
createdAt: number
lastSeenAt: number
}[]
reset: null | {
requestedAt: number
at: number
}
limits: {
maxItems: number
maxDevices: 20
}
}Errors
| Status | Message |
|---|---|
403 | Passwords opens only in the Authenticator app. |
POST /v1/me/vault
Makes your vault: the account, a personal vault and the first device, all sealed on the device and signed with the new vault's own key (X-Vault-Signature). 409 (exists) when you have one, unless its reset is due, when the old vault is deleted first.
Auth: user access token or platform agent key
Request body (up to 16 KB)
| Field | Type | Required | Notes |
|---|---|---|---|
accountId | string | Yes | |
kdf | object | Yes | |
kdf.alg | "argon2id" | Yes | |
kdf.m | integer | Yes | |
kdf.t | integer | Yes | |
kdf.p | integer | Yes | |
kdfSalt | string | Yes | matches ^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$ |
keyset | any JSON | Yes | |
encryptionPublicKey | string | Yes | matches ^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$ |
signingPublicKey | string | Yes | matches ^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$ |
vault | object | Yes | |
vault.vaultId | string | Yes | matches ^[A-Za-z0-9_-]{22}$ |
vault.envelope | any JSON | Yes | |
vault.attrs | any JSON | Yes | |
device | object | Yes | |
device.deviceId | string | Yes | matches ^[A-Za-z0-9_-]{22}$ |
device.name | string | Yes | 1–80 characters; trimmed |
device.platform | "ios" | "android" | "web" | Yes |
Also checked: Unknown fields are rejected.
Response 201
{
account: null | {
createdAt: number
updatedAt: number
accountId: string
kdfSalt: string
keyset: string
keysetRevision: number
encryptionPublicKey: string
signingPublicKey: string
kdf: {
alg: "argon2id"
m: number
t: number
p: number
}
}
vaults: {
vaultId: string
kind: "personal"
role: "owner"
envelope: string
attrs: string
attrsRevision: number
revision: number
itemCount: number
createdAt: number
updatedAt: number
}[]
devices: {
deviceId: string
name: string
platform: string
createdAt: number
lastSeenAt: number
}[]
reset: null | {
requestedAt: number
at: number
}
limits: {
maxItems: number
maxDevices: 20
}
}Errors
| Status | Message |
|---|---|
403 | Passwords opens only in the Authenticator app. |
409 | You already have a vault. |
413 | The request is too large. |
PUT /v1/me/vault/keyset
Replaces the sealed keyset after a new master password or Secret Key, if keysetRevision is still current (409 otherwise). Signed by the vault. Emails you.
Auth: user access token or platform agent key
Request body (up to 8 KB)
| Field | Type | Required | Notes |
|---|---|---|---|
kdf | object | Yes | |
kdf.alg | "argon2id" | Yes | |
kdf.m | integer | Yes | |
kdf.t | integer | Yes | |
kdf.p | integer | Yes | |
kdfSalt | string | Yes | matches ^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$ |
keyset | any JSON | Yes | |
keysetRevision | integer | Yes | ≥ 1 |
Also checked: Unknown fields are rejected.
Response 200
{
account: {
createdAt: number
updatedAt: number
accountId: string
kdfSalt: string
keyset: string
keysetRevision: number
encryptionPublicKey: string
signingPublicKey: string
kdf: {
alg: "argon2id"
m: number
t: number
p: number
}
}
}Errors
| Status | Message |
|---|---|
403 | Passwords opens only in the Authenticator app. |
409 | The vault changed on another device. |
413 | The request is too large. |
DELETE /v1/me/vault
Signed by the vault: deletes it now (items, vaults, devices and keys; the log stays). Without a signature (you can't open it any more): asks for a reset (202, reset), which may happen 7 days later unless a device that can open the vault cancels it. Emails you either way.
Auth: user access token or platform agent key
Response 200
{
deleted: true
}Response 202
{
reset: {
requestedAt: number
at: number
}
}Errors
| Status | Message |
|---|---|
403 | Passwords opens only in the Authenticator app. |
404 | There's no vault for this account. |
POST /v1/me/vault/reset/cancel
Cancels a pending reset. Signed by the vault.
Auth: user access token or platform agent key
Response 200
{
reset: null
}Errors
| Status | Message |
|---|---|
403 | Passwords opens only in the Authenticator app. |
GET /v1/me/vault/vaults/:vaultId/index
The vault's counter (revision) and every item's itemId, revision and whether it was deleted, without ciphertext: devices compare it with their copy.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:vaultId | A Passwords vault's id: 22 base64url characters, made on the device. |
Response 200
{
revision: number
items: {
itemId: string
revision: number
deleted: boolean
}[]
}Errors
| Status | Message |
|---|---|
403 | Passwords opens only in the Authenticator app. |
404 | That vault isn't yours. |
GET /v1/me/vault/vaults/:vaultId/items
Up to 100 items by id (ids, comma-separated): each item's sealed key, overview and details (absent for deleted ones), revision and times.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:vaultId | A Passwords vault's id: 22 base64url characters, made on the device. |
| Query parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
ids | string | No | "" |
Response 200
{
items: {
itemId: string
revision: number
deleted: boolean
key?: string
overview?: string
details?: string
createdAt: number
updatedAt: number
}[]
}Errors
| Status | Message |
|---|---|
400 | Ask for up to 100 item ids. |
403 | Passwords opens only in the Authenticator app. |
404 | That vault isn't yours. |
POST /v1/me/vault/vaults/:vaultId/items
Writes up to 50 changes (a sealed key, overview and details, or deleted: true), each only if the item is still at its baseRevision (0: new; deletes go first). Per change: ok with the new revision, conflict with the item as it is now, or rejected (limit: the plan's item limit). Signed by the vault.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:vaultId | A Passwords vault's id: 22 base64url characters, made on the device. |
Request body (up to 4 MB)
| Field | Type | Required | Notes |
|---|---|---|---|
changes | object[] | Yes | 1–50 items |
changes[].itemId | string | Yes | matches ^[A-Za-z0-9_-]{22}$ |
changes[].baseRevision | integer | Yes | ≥ 0 |
changes[].deleted | true | No | |
changes[].key | any JSON | No | |
changes[].overview | any JSON | No | |
changes[].details | any JSON | No |
Also checked: Unknown fields are rejected. Each item can change once per request.
Response 200
{
results: {
itemId: string
status: "ok"
revision: number
deleted: boolean
createdAt: number
updatedAt: number
} | {
itemId: string
status: "conflict"
current: null | {
itemId: string
revision: number
deleted: boolean
key?: string
overview?: string
details?: string
createdAt: number
updatedAt: number
}
} | {
itemId: string
status: "rejected"
code: "limit" | "invalid"
error: string
}[]
vault: {
revision: number
itemCount: number
}
}Errors
| Status | Message |
|---|---|
403 | Passwords opens only in the Authenticator app. |
404 | That vault isn't yours. |
413 | Send fewer changes at once. |
PUT /v1/me/vault/devices/:deviceId
Registers this device (name, platform: ios, android or web) or renames it; signed by the vault with the device's own id. Up to 20 devices; a removed id can't come back. Emails you about a new device.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:deviceId | Device id: an agent's (dev_…); under /v1/me/notifications a phone's install id; under /v1/me/sign-in-approvals a phone that approves sign-ins (apd_…); under /v1/me/vault the id a device made for its vault (22 base64url characters). |
Request body (up to 4 KB)
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | 1–80 characters; trimmed |
platform | "ios" | "android" | "web" | Yes |
Also checked: Unknown fields are rejected.
Response 200
{
device: {
deviceId: string
name: string
platform: string
createdAt: number
lastSeenAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Passwords opens only in the Authenticator app. |
403 | This device was removed from your vault. |
409 | Your vault is on 20 devices already. Remove one first. |
413 | The request is too large. |
DELETE /v1/me/vault/devices/:deviceId
Removes a device; it stops syncing and wipes its copy when it next connects. Signed by the vault.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:deviceId | Device id: an agent's (dev_…); under /v1/me/notifications a phone's install id; under /v1/me/sign-in-approvals a phone that approves sign-ins (apd_…); under /v1/me/vault the id a device made for its vault (22 base64url characters). |
Response 200
{
removed: boolean
}Errors
| Status | Message |
|---|---|
400 | Not a device id. |
403 | Passwords opens only in the Authenticator app. |
GET /v1/me/vault/activity
Your vault's log, newest first, kept a year: made, devices added and removed, master password or Secret Key changes, resets, deletion, and counts of changed items. Never contents.
Auth: user access token or platform agent key
Response 200
{
events: {
at: number
counts?: {
[key: string]: number
}
deviceName?: string
deviceId?: string
activityId: string
action: string
}[]
}Errors
| Status | Message |
|---|---|
403 | Passwords opens only in the Authenticator app. |