Proxy API Reference

Proxy API Reference

Health

GET /health

Returns:

1
{ "status": "ok" }

Key APIs

POST /v1/keys/generate

Required headers:

  • X-Api-Key
  • X-User-Secret

Generates an ECC keypair and a Kyber keypair, encrypts the private keys with a KEK derived from X-User-Secret, stores the encrypted private keys in Backend, and returns the public keys.

Response:

1
2
3
4
{
  "publicKeyB64": "<ecc-public-key>",
  "publicKeyKyberB64": "<kyber-public-key>"
}

GET /v1/keys/status

Required headers:

  • X-Api-Key

Returns:

1
2
3
4
{
  "hasEcc": true,
  "hasKyber": true
}

POST /v1/keys/rekey

Required headers:

  • X-Api-Key
  • X-User-Secret
  • X-New-User-Secret

Re-encrypts the stored private keys with a KEK derived from the new user secret.

Errors:

  • 401 if the old user secret cannot decrypt the stored private keys
  • 400 if the user has not generated key material yet

KV APIs

PUT /v1/kv/{key}

Required headers:

  • X-Api-Key

Request:

1
{ "valueB64": "base64-plaintext" }

Behavior:

  • Proxy generates a random AES-256 key for the value
  • Proxy encrypts the plaintext with AES-GCM
  • Proxy wraps the AES key with ECC, then wraps the ECC payload with Kyber
  • Backend receives only ciphertext and wrapped keys

GET /v1/kv/{key}

Required headers:

  • X-Api-Key
  • X-User-Secret

Response:

1
{ "valueB64": "base64-plaintext" }

Errors:

  • 401 for a wrong user secret
  • 400 for tampered wrapped keys, key mismatch, or invalid ciphertext
  • 404 if the key does not exist

DELETE /v1/kv/{key}

Required headers:

  • X-Api-Key

Deletes the stored key.

GET /v1/kv

Required headers:

  • X-Api-Key

Returns an array of active key names.

KV Sharing APIs

Shares are same-org and currently read-only. The only accepted permission value is "read".

POST /v1/kv/{key}/shares

Required headers:

  • X-Api-Key
  • X-User-Secret

Request:

1
2
3
4
5
6
{
  "recipientUserId": "uuid",
  "permission": "read",
  "alias": "database password",
  "expiresUtc": "2026-03-24T12:00:00Z"
}

Behavior:

  • Proxy unwraps the stored AES key with the caller’s private keys.
  • Proxy re-wraps that AES key to the recipient’s ECC and Kyber public keys.
  • alias is optional and is encrypted with the same AES key before it is stored.
  • expiresUtc is optional but, if supplied, must be in the future.

Success response:

1
2
3
4
5
6
7
{
  "shareId": "uuid",
  "recipientUserId": "uuid",
  "permission": "read",
  "createdUtc": "2026-03-17T12:34:56Z",
  "expiresUtc": "2026-03-24T12:00:00Z"
}

Errors:

  • 400 for missing owner private keys, missing recipient public keys, invalid wrapped keys, permission other than read, or an expiresUtc in the past
  • 401 for a wrong X-User-Secret
  • 404 if the key or recipient user does not exist in the caller’s org
  • 409 if an active share already exists for that recipient and key

GET /v1/kv/{key}/shares

Required headers:

  • X-Api-Key

Returns active owner-side grants for one key:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[
  {
    "shareId": "uuid",
    "recipientUserId": "uuid",
    "recipientName": "alice",
    "permission": "read",
    "createdUtc": "2026-03-17T12:34:56Z",
    "updatedUtc": "2026-03-17T12:34:56Z",
    "expiresUtc": "2026-03-24T12:00:00Z",
    "firstAccessUtc": null,
    "lastAccessUtc": null
  }
]

DELETE /v1/kv/{key}/shares/{recipientUserId}

Required headers:

  • X-Api-Key

Revokes the active share for that recipient.

Response:

1
2
3
4
5
{
  "revoked": true,
  "shareId": "uuid",
  "revokedUtc": "2026-03-17T12:34:56Z"
}

GET /v1/kv/shared

Required headers:

  • X-Api-Key
  • X-User-Secret

Returns active shares that the current user can still unwrap:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[
  {
    "shareId": "uuid",
    "ownerUserId": "uuid",
    "ownerName": "owner",
    "permission": "read",
    "alias": "database password",
    "updatedUtc": "2026-03-17T12:34:56Z",
    "expiresUtc": "2026-03-24T12:00:00Z"
  }
]

Expired or no-longer-decryptable shares are omitted from this list.

GET /v1/kv/shared/{shareId}

Required headers:

  • X-Api-Key
  • X-User-Secret

Response:

1
2
3
4
5
6
{
  "shareId": "uuid",
  "ownerUserId": "uuid",
  "valueB64": "base64-plaintext",
  "expiresUtc": "2026-03-24T12:00:00Z"
}

Behavior:

  • Proxy decrypts the recipient envelope locally and returns plaintext only to the recipient.
  • Proxy records a share-access event after a successful read.

Errors:

  • 401 for a wrong X-User-Secret
  • 403 if the share exists but can no longer be decrypted with the user’s current keys
  • 404 if the share does not exist, is expired, or does not belong to the caller

DELETE /v1/kv/shared/{shareId}

Required headers:

  • X-Api-Key

Allows the recipient to remove their own access.

Response:

1
2
3
4
5
{
  "removed": true,
  "shareId": "uuid",
  "revokedUtc": "2026-03-17T12:34:56Z"
}

File APIs

POST /v1/files

Required headers:

  • X-Api-Key
  • X-User-Secret

Request:

1
2
3
4
5
{
  "fileName": "snapshot.json",
  "contentB64": "base64-file-bytes",
  "contentType": "application/json"
}

Behavior:

  • file name and file content are encrypted with a random AES key
  • the AES key is wrapped with ECC and Kyber
  • Backend stores encrypted metadata
  • Backend writes ciphertext to the org’s configured object store

Validation:

  • fileName is required
  • contentB64 must be valid base64
  • the user’s public keys must already exist
  • the org must have storage configured and an eligible plan

GET /v1/files

Required headers:

  • X-Api-Key
  • X-User-Secret

Returns decrypted file metadata for files the current user can unwrap:

1
2
3
4
5
6
7
8
9
[
  {
    "id": "uuid",
    "fileName": "snapshot.json",
    "sizeBytes": 123,
    "contentType": "application/json",
    "updatedUtc": "2026-03-17T12:34:56Z"
  }
]

GET /v1/files/{id}

Required headers:

  • X-Api-Key
  • X-User-Secret

Response:

1
2
3
4
5
{
  "fileName": "snapshot.json",
  "contentB64": "base64-file-bytes",
  "contentType": "application/json"
}

DELETE /v1/files/{id}

Required headers:

  • X-Api-Key

Deletes the file record and object.

File Sharing APIs

File shares are also same-org and currently read-only.

POST /v1/files/{id}/shares

Required headers:

  • X-Api-Key
  • X-User-Secret

Request:

1
2
3
4
5
{
  "recipientUserId": "uuid",
  "permission": "read",
  "expiresUtc": "2026-03-24T12:00:00Z"
}

Behavior:

  • Proxy unwraps the file AES key with the caller’s private keys.
  • Proxy re-wraps that AES key for the recipient’s public keys.
  • expiresUtc is optional but must be in the future when provided.

Success response:

1
2
3
4
5
6
7
{
  "shareId": "uuid",
  "recipientUserId": "uuid",
  "permission": "read",
  "createdUtc": "2026-03-17T12:34:56Z",
  "expiresUtc": "2026-03-24T12:00:00Z"
}

Errors:

  • 400 for missing owner private keys, missing recipient public keys, invalid wrapped keys, permission other than read, or an expiresUtc in the past
  • 401 for a wrong X-User-Secret
  • 404 if the file or recipient user does not exist in the caller’s org
  • 409 if an active share already exists for that recipient and file

GET /v1/files/{id}/shares

Required headers:

  • X-Api-Key

Returns active owner-side grants for one file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[
  {
    "shareId": "uuid",
    "recipientUserId": "uuid",
    "recipientName": "alice",
    "permission": "read",
    "createdUtc": "2026-03-17T12:34:56Z",
    "updatedUtc": "2026-03-17T12:34:56Z",
    "expiresUtc": "2026-03-24T12:00:00Z",
    "firstAccessUtc": null,
    "lastAccessUtc": null
  }
]

DELETE /v1/files/{id}/shares/{recipientUserId}

Required headers:

  • X-Api-Key

Revokes the active file share for that recipient.

Response:

1
2
3
4
5
{
  "revoked": true,
  "shareId": "uuid",
  "revokedUtc": "2026-03-17T12:34:56Z"
}

GET /v1/files/shared

Required headers:

  • X-Api-Key
  • X-User-Secret

Returns active shared files that the current user can still unwrap:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[
  {
    "shareId": "uuid",
    "fileId": "uuid",
    "ownerUserId": "uuid",
    "ownerName": "owner",
    "fileName": "snapshot.json",
    "contentType": "application/json",
    "sizeBytes": 123,
    "updatedUtc": "2026-03-17T12:34:56Z",
    "expiresUtc": "2026-03-24T12:00:00Z"
  }
]

GET /v1/files/shared/{shareId}

Required headers:

  • X-Api-Key
  • X-User-Secret

Response:

1
2
3
4
5
6
7
8
9
{
  "shareId": "uuid",
  "fileId": "uuid",
  "ownerUserId": "uuid",
  "fileName": "snapshot.json",
  "contentType": "application/json",
  "contentB64": "base64-file-bytes",
  "expiresUtc": "2026-03-24T12:00:00Z"
}

Behavior:

  • Proxy decrypts the file name and ciphertext locally for the recipient.
  • Proxy records a share-access event after a successful download.

Errors:

  • 401 for a wrong X-User-Secret
  • 403 if the share exists but can no longer be decrypted with the user’s current keys
  • 404 if the share does not exist, is expired, or does not belong to the caller

DELETE /v1/files/shared/{shareId}

Required headers:

  • X-Api-Key

Allows the recipient to remove their own file access.

Response:

1
2
3
4
5
{
  "removed": true,
  "shareId": "uuid",
  "revokedUtc": "2026-03-17T12:34:56Z"
}

User Discovery APIs

GET /v1/users/me

Required headers:

  • X-Api-Key

Returns the Backend users/me payload through Proxy.

GET /v1/users/org

Required headers:

  • X-Api-Key

Returns org-scoped public profiles for the current user’s organization.

GET /v1/users/org/{userId}

Required headers:

  • X-Api-Key

Returns one org-scoped public profile or 404 for a different org.

Recovery APIs

PUT /v1/recovery/trustees/{trusteeUserId}

Required headers:

  • X-Api-Key

Request:

1
2
3
4
5
6
{
  "encryptedPrivateKeyEccShareB64": "base64-share-ecc",
  "encryptedPrivateKeyKyberShareB64": "base64-share-kyber",
  "xorApplied": true,
  "xorSaltHint": "dog-name"
}

Creates or updates a trusted-user recovery share.

GET /v1/recovery/trustees

Required headers:

  • X-Api-Key

Lists trustees configured by the current user.

DELETE /v1/recovery/trustees/{trusteeUserId}

Required headers:

  • X-Api-Key

Deletes a trusted-user entry.

GET /v1/recovery/shares

Required headers:

  • X-Api-Key

Lists owners who have shared recovery material with the current user.

GET /v1/recovery/shares/{ownerUserId}

Required headers:

  • X-Api-Key

Returns the raw encrypted recovery share for an owner where the current user is the trustee.

POST /v1/recovery/restore/{ownerUserId}

Required headers:

  • X-Api-Key

Request:

1
2
3
4
5
6
{
  "encryptedPrivateKeyEccB64": "base64-restored-ecc",
  "encryptedPrivateKeyKyberB64": "base64-restored-kyber",
  "usedXor": true,
  "xorSaltHint": "dog-name"
}

Response:

1
{ "requiresRekey": true }

The expected next step is for the recovered user to rekey with a fresh X-User-Secret.

Admin Passthrough APIs

The following routes are forwarded to Backend using the caller’s X-Api-Key:

  • POST /admin/orgs
  • GET /admin/orgs
  • POST /admin/orgs/{orgId}/users
  • GET /admin/orgs/{orgId}/users
  • POST /admin/orgs/{orgId}/users/{userId}/apikey/rotate

These are not versioned under /v1; they preserve the Backend route shape.

Common Error Cases

  • 401: missing API key, missing required user secret, or wrong user secret
  • 400: missing user keys, invalid base64, tampered wrapped keys, invalid ciphertext, invalid share payloads, or Backend validation failure
  • 403: share envelope exists but cannot be decrypted with the caller’s current keys
  • 404: missing key, file, user, or recovery share
  • 409: an active share already exists for that recipient and resource