Developers
API reference
zcrypt exposes a JSON REST API. Encryption happens on the client, so the API only ever moves opaque ciphertext, metadata, and chunks, never plaintext or keys.
Basics
- •Base URL. Your backend origin, with every route under
/api. On a self-hosted instance this is whatever you set asBACKEND_URL. - •Content type. Request and response bodies are JSON, except chunk transfers, which send and receive raw bytes. JSON request bodies are capped at 1 MB.
- •Errors. Non-2xx responses return
{ "error": "message" }with a matching HTTP status. - •Rate limits. Requests are rate-limited per IP. Auth and public (share / send / pad) routes have their own tighter limits; the SSE and WebSocket endpoints are exempt.
Stability
This documents the routes the web, desktop, and Android apps use today. zcrypt is open source and pre-1.0. Treat the surface as evolving, and readRegisterRoutes in app/backend/cmd/server.go as the authoritative route table.Authentication
Authenticate by sending a JSON Web Token in the Authorization header. Logging in returns an access token (and a refresh token); send the access token on every protected request.
Authorization: Bearer <access-token>When an account has two-factor enabled, login returns a 2FA challenge instead of tokens; complete it with POST /api/auth/2fa/verify. Access tokens are short-lived. Use POST /api/auth/refresh to mint a new one.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/auth/register | Create an account. |
| POST | /api/auth/login | Sign in; returns tokens or a 2FA challenge. |
| POST | /api/auth/refresh | Exchange a refresh token for a new access token. |
| POST | /api/auth/logout | Invalidate the current session. |
| POST | /api/auth/2fa/verify | Complete a TOTP challenge during login. |
| POST | /api/auth/2fa/setup | Generate a TOTP secret (authenticated). |
| POST | /api/auth/2fa/enable | Enable 2FA after verifying a code. |
| POST | /api/auth/forgot-password | Send a password-reset email. |
| POST | /api/auth/reset-password | Reset a password with a token. |
| POST | /api/auth/verify-email | Verify an email address. |
| POST | /api/auth/magic-link | Request a passwordless sign-in link. |
| GET | /api/auth/me | Return the current user. |
| GET | /api/auth/oauth/{provider} | Begin a Google/GitHub OAuth flow. |
Files & folders
Files and folders carry only encrypted metadata. Folders can be nested, renamed, moved, and given their own password; deletes are soft (Trash) until purged. File names and folder names are ciphertext on the server.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/files | List files (filterable by folder). |
| GET | /api/files/trash | List soft-deleted files. |
| DELETE | /api/files/{id} | Move a file to Trash. |
| POST | /api/files/bulk-delete | Move many files to Trash. |
| PATCH | /api/files/{id}/move | Move a file into a folder. |
| POST | /api/files/{id}/restore | Restore from Trash. |
| DELETE | /api/files/{id}/purge | Permanently delete a file. |
| GET | /api/folders | List folders. |
| POST | /api/folders | Create a folder. |
| PATCH | /api/folders/{id} | Rename a folder. |
| PATCH | /api/folders/{id}/move | Move a folder. |
| POST | /api/folders/{id}/password | Set a folder password. |
| DELETE | /api/folders/{id} | Delete a folder. |
Upload (chunked)
Uploads are client-side encrypted and chunked. Initialise a session, push each encrypted chunk (or upload it directly to the platform via a presigned URL where supported), then complete the session. Sessions are resumable: query status and re-send only the chunks that are missing.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/upload/init | Start an upload session; returns a session id. The body must carry encrypted_name, or the server answers 426 with code client_update_required. Files over 10 GiB get 413. |
| PUT | /api/upload/{sid}/chunk/{idx} | Upload one encrypted chunk through the server. |
| POST | /api/upload/{sid}/presign/{idx} | Get a presigned URL to upload a chunk directly. |
| POST | /api/upload/{sid}/confirm/{idx} | Confirm a directly-uploaded chunk. |
| POST | /api/upload/{sid}/complete | Finalise the file once all chunks are in. |
| GET | /api/upload/{sid}/status | Report which chunks are received (resume). |
| DELETE | /api/upload/{sid} | Cancel an in-flight upload. |
Chunk integrity headers
Chunk uploads carry anX-Chunk-SHA256 hash and an X-Chunk-Compressed flag so the server can verify each chunk and record how it was packed, without ever decrypting it.Download (chunked)
Downloads fetch metadata, then pull chunks by index. The client verifies each chunk, reassembles, decrypts with your passphrase-derived key, and decompresses, all locally.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/files/{id}/meta | File metadata: size, chunk count, salt/IV. |
| POST | /api/files/meta | Batch metadata for up to 200 file ids in one request. |
| GET | /api/files/{id}/chunks/{idx} | Fetch one encrypted chunk by index. |
Shared vaults (spaces)
A shared vault (a “space”) lets several accounts share files under role-based access. The space has its own random key that the client seals to each member’s public key (see User keys below), and every shared file’s content key is re-wrapped under the space key. The server only ever stores these opaque grants, it can add, remove, and rotate them but never open them. Owners and admins manage members; only the owner can rotate the key or delete the space. Adding or removing files is limited to editors and admins.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/shared-vaults | List spaces you belong to. |
| POST | /api/shared-vaults | Create a space. |
| GET | /api/shared-vaults/{id} | Space detail with members and files. |
| DELETE | /api/shared-vaults/{id} | Delete a space (owner). |
| PATCH | /api/shared-vaults/{id} | Edit the space name, description, or size limit (owner or admin). |
| POST | /api/shared-vaults/{id}/members | Add a member with a sealed grant (owner or admin). |
| PATCH | /api/shared-vaults/{id}/members/{uid} | Change a member's role (owner or admin; only the owner can grant or change admin). |
| DELETE | /api/shared-vaults/{id}/members/{uid} | Remove a member (owner or admin; only the owner can remove an admin). Members can remove themselves. |
| POST | /api/shared-vaults/{id}/files | Share a file into the space (editor/admin). |
| POST | /api/shared-vaults/{id}/files/batch | Share many files into the space in one transaction (editor/admin). The free plan caps a batch at max_space_batch_files (default 200). |
| DELETE | /api/shared-vaults/{id}/files/{fid} | Unshare a file (editor/admin). |
| POST | /api/shared-vaults/{id}/rotate | Rotate the space key after a change (owner). |
Rotation is what makes removal a revocation
Rotating re-seals a fresh space key to the remaining members and re-wraps every file’s key under it, so a removed member’s old grant, and any copy of the old key, can no longer open the space’s files.User keys
To seal a space key to another account, every user gets an X25519 keypair generated on the client. The private key is wrapped under your passphrase-derived key and stored only as ciphertext the server can’t read; the public key and a short fingerprint are stored in the clear so others can seal grants to you. These routes never return anyone else’s private key.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/keys/me | Your key record (wrapped private key, wrapped master key if set, and the master_key_writes flag), or null. |
| POST | /api/keys | Publish or rotate your keypair. |
| PUT | /api/keys/master | Store your wrapped account master key (mk1). Set once: a second write returns 409. |
| GET | /api/keys/lookup | Resolve a user's public key by email or username (identifier query). |
| GET | /api/keys/user/{id} | Fetch another user's public key by id. |
| GET | /api/users/{id}/avatar | A user's avatar image. Visible to that user and to people who share a space with them; anyone else gets 404. |
Send & Pad
Send lets anyone upload an encrypted file without an account and hand over a token; Pad stores a one-time encrypted note. Both are unauthenticated and rate-limited by IP.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/send/init | Start an anonymous send. |
| PUT | /api/send/{sid}/chunk/{idx} | Upload an encrypted chunk. |
| POST | /api/send/{sid}/complete | Finalise the send; returns a token. |
| GET | /api/send/{token} | Fetch send info / metadata / chunks. |
| POST | /api/pad | Create an encrypted one-time pad. |
| GET | /api/pad/{token} | Read pad info, then its content. |
Timed vaults
A timed vault groups files under a name and a countdown. The expiry is a label, not a delete trigger, deleting a vault never deletes the files it references. The expiry must be at least an hour out.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/vaults | List your timed vaults. |
| POST | /api/vaults | Create one (name, expires_at, file ids). |
| GET | /api/vaults/{id} | Get a timed vault. |
| DELETE | /api/vaults/{id} | Delete the vault (not its files). |
Snapshots & integrity
Snapshots capture a point-in-time manifest of your file list; the integrity monitor records each file’s SHA-256 and size so a later check can flag a hash that changed. Both are metadata-only, no file contents are stored or restored. See Snapshots & integrity.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/snapshots | List vault snapshots. |
| POST | /api/snapshots | Capture a new snapshot. |
| GET | /api/snapshots/{id} | Get one snapshot. |
| DELETE | /api/snapshots/{id} | Delete a snapshot. |
| GET | /api/integrity | List integrity references. |
| POST | /api/integrity | Record a file's hash + size reference. |
| POST | /api/integrity/check | Check a file against its latest reference. |
| GET | /api/integrity/changes | List files whose hash no longer matches. |
Offline & clipboard
Offline pins mark files to keep available on a device; the encrypted clipboard syncs small snippets between your own devices. Clipboard items are encrypted on the client (the server stores only ciphertext, capped at 512 KB per item) and a push notifies your other devices over the event stream.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/offline | List offline pins (optional device_id). |
| POST | /api/offline | Pin a file for offline access. |
| DELETE | /api/offline/{fileId} | Unpin a file. |
| POST | /api/clipboard | Push an encrypted clipboard item (text / image / link). |
| GET | /api/clipboard | List recent clipboard items. |
| GET | /api/clipboard/{id} | Fetch an item's encrypted bytes. |
| DELETE | /api/clipboard/{id} | Delete a clipboard item. |
Sync folders
Sync folders are a per-device registry of local folders the desktop client keeps in sync. The server stores only the configuration and the stats a client reports back, the actual file syncing happens on your machine.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/sync/folders | List sync-folder configs. |
| POST | /api/sync/folders | Register a folder for sync. |
| PUT | /api/sync/folders/{id} | Update a folder (enable/label). |
| PUT | /api/sync/folders/{id}/stats | Report sync results (file count, total size). |
| DELETE | /api/sync/folders/{id} | Remove a folder config. |
Events (SSE)
Real-time progress (uploads, syncs) streams over Server-Sent Events. Because EventSource cannot set headers, first trade your access token for a single-use ticket, then open the stream with it. The ticket is valid for 30 seconds, so the token itself never appears in a URL or a proxy log.
POST /api/sse/ticket -> {"ticket": "..."}
GET /api/events?ticket=<ticket>The stream carries upload and sync progress plus a clipboard event, emitted to your other connections when a new clipboard item is pushed. It closes when the access token it was opened with expires, or as soon as your sessions are revoked, so reconnect with a fresh ticket. Each account can hold a limited number of open streams. A separate WebSocket endpoint, GET /api/transfer/ws, backs live device-to-device transfer. Both long-lived endpoints bypass the per-request rate limiter.
Quota & config
These routes report account limits, connected storage, and instance settings. Most are read-only for normal users; writing config requires admin.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/quota | Storage quota and max concurrent uploads. |
| GET | /api/config | Effective instance configuration. |
| GET | /api/platforms/status | Connection status per storage platform. |
| POST | /api/platforms/connect | Connect a platform token. |
| DELETE | /api/platforms/disconnect | Disconnect a platform. |
| GET | /api/repos | List repositories in your storage pool. |
| GET | /api/plans | Public plan configs (no auth). |
| GET | /api/health | Health check (no auth). |
| GET | /api/geo/country | The visitor's two-letter country code taken from edge headers, or an empty string (no auth). |
Admin
Admin routes require an account with the admin role. They cover user management, quotas and plans, global tokens, and audit data.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/admin/users | List all users. |
| GET | /api/admin/stats | System statistics. |
| PUT | /api/admin/users/{id}/role | Set a user's role. Send the acting admin's password, and a 2FA code if enabled. |
| PUT | /api/admin/users/{id}/plan | Set a user's plan. |
| PUT | /api/admin/users/{id}/quota | Set a user's quota. |
| DELETE | /api/admin/users/{id} | Delete a user. Needs the same re-authentication as a role change. |
| GET | /api/admin/tokens | List global storage tokens. |
| GET | /api/admin/audit | Read the audit log. |
| PUT | /api/admin/settings/master-key-writes | Turn master-key (mk1) writes on or off. Off by default; GET on the same path reads the current value. |
Looking to run your own instance? See Self-hosting and Architecture.