API reference
Build a client for your own VYLK instance. Read the client protocol guide for authentication, offline sync, and encryption.
Create a session
POST /api/login
Read bootstrap first. In legacy/preparing mode, send the configured server password. In encrypted/cleaning mode, omit password and send a derived proof or recovery_proof. Recovery proof takes precedence if both proofs are supplied. Limit: 16 KiB JSON. Preserve both response cookies in your cookie jar.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Content-Type | string | Yes | application/json |
Path parameters
None.
Query parameters
None.
Request body
Content-Type: application/json
Schema: Login
| Field | Type | Required | Details |
|---|---|---|---|
password | string | No | Server password, legacy/preparing only. |
proof | Proof | No | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
recovery_proof | Proof | No | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
Unknown fields are rejected.
{
"password": "your-server-password"
}Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
| Header | Status | Details |
|---|---|---|
Set-Cookie | 200 | May contain multiple Set-Cookie lines. session is HttpOnly, Path=/, SameSite=Lax, 180-day lifetime; vylk-device groups browser-profile sessions and lasts 365 days. Secure is set for HTTPS. Authenticated activity periodically renews session. Never combine Set-Cookie lines with commas. |
Retry-After | 429 | Delay in seconds. |
Responses
200 Signed in. Sets session and vylk-device cookies.
Content-Type: application/json
Schema: LoginResult
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
429 login_rate_limited. Wait retry_after seconds; do not hammer credentials.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Sign out this browser profile
POST /api/logout
Revokes all sessions grouped under a valid session's device, not just this tab. Missing, expired, or unrecognized sessions still clear the cookie successfully. Keep encrypted pending edits locally; logout does not upload them.
Request headers
None.
Path parameters
None.
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
| Header | Status | Details |
|---|---|---|
Set-Cookie | 204 | May contain multiple Set-Cookie lines. session is HttpOnly, Path=/, SameSite=Lax, 180-day lifetime; vylk-device groups browser-profile sessions and lasts 365 days. Secure is set for HTTPS. Authenticated activity periodically renews session. Never combine Set-Cookie lines with commas. |
Responses
204 Signed out; empty body. Clears session cookie.
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Check session and server identity
GET /api/check
Compare instance_id, vault_mode, and vault_epoch against persisted local state before resuming synchronization. revision is the application asset revision, not a note revision.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Path parameters
None.
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
List signed-in devices
GET /api/devices
Tabs sharing a vylk-device cookie appear as one device. last_seen_at is approximate; activity updates are throttled. Entries are ordered by recent activity.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Path parameters
None.
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Active browser-profile devices.
Content-Type: application/json
Schema: object
| Field | Type | Required | Details |
|---|---|---|---|
devices | array of Device | Yes |
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Revoke a device's sessions
DELETE /api/devices/{id}
Idempotent for an unknown but valid device ID. Revocation cannot erase plaintext or keys already held by an offline device. Disabled by VYLK_DISABLE_VAULT_CHANGES.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Path parameters
| Parameter | Type | Required | Details |
|---|---|---|---|
id | string | Yes | Device ID, not a sync queue device_id. pattern: "^[a-f0-9]{32}$" |
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
204 Sessions revoked; empty body.
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
403 vault_changes_disabled: server policy rejects vault/device mutations. Ordinary notes and preferences remain usable.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
List note summaries
GET /api/notes
With limit, returns a page; without limit, returns an array and ignores cursor. Newest updated_at first. Plaintext tag filtering is exact; encrypted mode rejects tag with 409 client_search_required. Encrypted responses contain envelopes instead of title/tags/filename. The page response's nextCursor may be absent or empty at the end.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
X-Vylk-Vault-Protocol | string | No | Required value 1 for this route in encrypted mode; otherwise 426. Not required for authentication, preferences, keys, migration, or SSE. const: "1" |
Path parameters
None.
Query parameters
| Parameter | Type | Required | Details |
|---|---|---|---|
limit | integer | No | Enable pagination. An empty limit is invalid in encrypted mode. minimum: 1 maximum: 100 |
cursor | string | No | Opaque nextCursor from the preceding page; do not parse or invent it. |
tag | string | No | Exact tag; plaintext mode only. |
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Summaries, never note bodies.
Content-Type: application/json
Schema: array of NoteSummary or NotesPage
Variants:
- array of NoteSummary
- NotesPage
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
409 client_search_required: decrypt summaries and search/filter/index tags locally.
Content-Type: application/json
Schema: Error
423 vault_migration_in_progress (writes during preparation), vault_maintenance_in_progress (cleaning), or vault_unavailable. Pause writes; do not discard pending changes.
Content-Type: application/json
Schema: Error
426 encrypted_client_required: set X-Vylk-Vault-Protocol: 1 and implement the encrypted contract; never retry a plaintext body as encrypted.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Create or replace a complete note
POST /api/notes
Not a patch or text delta. Plaintext limit: 4 MiB JSON; encrypted limit: 8 MiB. Plaintext id may be omitted to generate an ID; supplying an ID requires base_revision (0 for a new note). Encrypted mode requires a client-generated ID, base_revision, epoch, summary, and body. Responses omit content/body. For offline clients prefer ordered sync/push, which supports replay acknowledgements and an instance fence.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
X-Vylk-Vault-Protocol | string | No | Required value 1 for this route in encrypted mode; otherwise 426. Not required for authentication, preferences, keys, migration, or SSE. const: "1" |
Content-Type | string | Yes | application/json |
Path parameters
None.
Query parameters
None.
Request body
Content-Type: application/json
Schema: SaveNote or SaveEncryptedNote
Variants:
{
"id": "my-note",
"base_revision": 0,
"title": "First note",
"tags": "personal,ideas",
"content": "# First note\n\nHello."
}Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
409 note_revision_conflict or stale_vault_epoch. Plaintext revision conflicts include note_id and current_revision; encrypted direct conflicts may contain only error/code.
Content-Type: application/json
Schema: Error
423 vault_migration_in_progress (writes during preparation), vault_maintenance_in_progress (cleaning), or vault_unavailable. Pause writes; do not discard pending changes.
Content-Type: application/json
Schema: Error
426 encrypted_client_required: set X-Vylk-Vault-Protocol: 1 and implement the encrypted contract; never retry a plaintext body as encrypted.
Content-Type: application/json
Schema: Error
503 Pending file recovery prevents writes. Retain operations and retry with backoff after recovery; code depends on route (note_file_recovery_blocked, note_recovery_pending, note_recovery_failed).
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Read a complete note
GET /api/notes/{id}
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
X-Vylk-Vault-Protocol | string | No | Required value 1 for this route in encrypted mode; otherwise 426. Not required for authentication, preferences, keys, migration, or SSE. const: "1" |
Path parameters
| Parameter | Type | Required | Details |
|---|---|---|---|
id | ID | Yes | Note identifier. pattern: "^[A-Za-z0-9_-]{1,64}$" |
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Metadata and Markdown content, or summary/body envelopes in encrypted mode.
Content-Type: application/json
Schema: CompleteNote
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
423 vault_migration_in_progress (writes during preparation), vault_maintenance_in_progress (cleaning), or vault_unavailable. Pause writes; do not discard pending changes.
Content-Type: application/json
Schema: Error
426 encrypted_client_required: set X-Vylk-Vault-Protocol: 1 and implement the encrypted contract; never retry a plaintext body as encrypted.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Delete a note at a known revision
DELETE /api/notes/{id}
Revision-checked deletion; not an unconditional idempotent delete. A repeat request may return conflict or not-found. Offline clients should use sync/push.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
X-Vylk-Vault-Protocol | string | No | Required value 1 for this route in encrypted mode; otherwise 426. Not required for authentication, preferences, keys, migration, or SSE. const: "1" |
Path parameters
| Parameter | Type | Required | Details |
|---|---|---|---|
id | ID | Yes | Note identifier. pattern: "^[A-Za-z0-9_-]{1,64}$" |
Query parameters
| Parameter | Type | Required | Details |
|---|---|---|---|
base_revision | integer | Yes | Last known note revision. format: "int64" minimum: 1 |
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
204 Deleted; empty body.
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
423 vault_migration_in_progress (writes during preparation), vault_maintenance_in_progress (cleaning), or vault_unavailable. Pause writes; do not discard pending changes.
Content-Type: application/json
Schema: Error
426 encrypted_client_required: set X-Vylk-Vault-Protocol: 1 and implement the encrypted contract; never retry a plaintext body as encrypted.
Content-Type: application/json
Schema: Error
503 Pending file recovery prevents writes. Retain operations and retry with backoff after recovery; code depends on route (note_file_recovery_blocked, note_recovery_pending, note_recovery_failed).
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Search plaintext titles and tags
GET /api/search
Title/tag prefix search, not body search. Uses at most the first eight whitespace-separated terms, combined with AND; returns up to 50 ranked summaries. An empty query returns []; no matches for a nonempty query can return null. Encrypted mode returns 409 client_search_required; decrypt and search locally.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
X-Vylk-Vault-Protocol | string | No | Required value 1 for this route in encrypted mode; otherwise 426. Not required for authentication, preferences, keys, migration, or SSE. const: "1" |
Path parameters
None.
Query parameters
| Parameter | Type | Required | Details |
|---|---|---|---|
q | string | No | Trimmed search text, at most 256 UTF-8 bytes. |
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Ranked plaintext note summaries or null.
Content-Type: application/json
Schema: array / null
Type: array / null
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
409 client_search_required: decrypt summaries and search/filter/index tags locally.
Content-Type: application/json
Schema: Error
423 vault_migration_in_progress (writes during preparation), vault_maintenance_in_progress (cleaning), or vault_unavailable. Pause writes; do not discard pending changes.
Content-Type: application/json
Schema: Error
426 encrypted_client_required: set X-Vylk-Vault-Protocol: 1 and implement the encrypted contract; never retry a plaintext body as encrypted.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
List plaintext tags
GET /api/tags
Encrypted mode returns 409 client_search_required. Build its tag index from decrypted summaries locally.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
X-Vylk-Vault-Protocol | string | No | Required value 1 for this route in encrypted mode; otherwise 426. Not required for authentication, preferences, keys, migration, or SSE. const: "1" |
Path parameters
None.
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Distinct tags.
Content-Type: application/json
Schema: array of string
Type: array of string
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
409 client_search_required: decrypt summaries and search/filter/index tags locally.
Content-Type: application/json
Schema: Error
423 vault_migration_in_progress (writes during preparation), vault_maintenance_in_progress (cleaning), or vault_unavailable. Pause writes; do not discard pending changes.
Content-Type: application/json
Schema: Error
426 encrypted_client_required: set X-Vylk-Vault-Protocol: 1 and implement the encrypted contract; never retry a plaintext body as encrypted.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Read the durable note change feed
GET /api/sync
since is exclusive. Store nextSequence only after committing the fetched changes locally. Follow hasMore; when false, stop fetching until a new hint/poll. resetRequired means the retained feed no longer covers the cursor: rebuild a snapshot without deleting pending edits. instance_id distinguishes replacement databases; do not infer identity from a cursor. See the protocol guide for snapshot races and reset handling.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
X-Vylk-Vault-Protocol | string | No | Required value 1 for this route in encrypted mode; otherwise 426. Not required for authentication, preferences, keys, migration, or SSE. const: "1" |
Path parameters
None.
Query parameters
| Parameter | Type | Required | Details |
|---|---|---|---|
since | integer | No | default: 0 format: "int64" minimum: 0 |
limit | integer | No | default: 100 minimum: 1 maximum: 500 |
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Ordered changes, tombstones, cursor and server identity.
Content-Type: application/json
Schema: ChangesPage
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
423 vault_migration_in_progress (writes during preparation), vault_maintenance_in_progress (cleaning), or vault_unavailable. Pause writes; do not discard pending changes.
Content-Type: application/json
Schema: Error
426 encrypted_client_required: set X-Vylk-Vault-Protocol: 1 and implement the encrypted contract; never retry a plaintext body as encrypted.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Fetch complete notes in a small batch
GET /api/sync/notes
Up to 25 comma-separated IDs, counted before deduplication. Empty ids returns empty notes/missing arrays. A fetched revision may be newer than the change-feed entry; do not overwrite pending local edits.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
X-Vylk-Vault-Protocol | string | No | Required value 1 for this route in encrypted mode; otherwise 426. Not required for authentication, preferences, keys, migration, or SSE. const: "1" |
Path parameters
None.
Query parameters
| Parameter | Type | Required | Details |
|---|---|---|---|
ids | string | No | Comma-separated note IDs; no spaces. |
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Existing complete notes and missing IDs.
Content-Type: application/json
Schema: object
| Field | Type | Required | Details |
|---|---|---|---|
notes | array of CompleteNote | Yes | |
missing | array of ID | Yes |
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
423 vault_migration_in_progress (writes during preparation), vault_maintenance_in_progress (cleaning), or vault_unavailable. Pause writes; do not discard pending changes.
Content-Type: application/json
Schema: Error
426 encrypted_client_required: set X-Vylk-Vault-Protocol: 1 and implement the encrypted contract; never retry a plaintext body as encrypted.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Apply ordered offline operations
POST /api/sync/push
At most 100 operations; 4 MiB plaintext or 8 MiB encrypted JSON. Empty operations is allowed to read expected_sequence. Full-note saves, not text deltas. Sequence starts at 1 and is contiguous per device_id; preserve it and stable op_id across retries. Conflicts consume sequence numbers. A 409 sequence gap can acknowledge an already-committed prefix. Timeouts/5xx can also follow partial commits: retry the identical operations. Do not interpret every HTTP 200 as every operation applied. See the protocol guide.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
X-Vylk-Vault-Protocol | string | No | Required value 1 for this route in encrypted mode; otherwise 426. Not required for authentication, preferences, keys, migration, or SSE. const: "1" |
X-Vylk-Instance-ID | string | No | Strongly recommended: persisted server instance_id. A mismatch returns 409 before any writes. Omission is accepted for compatibility, not safe for offline replacement-database recovery. |
Content-Type | string | Yes | application/json |
Path parameters
None.
Query parameters
None.
Request body
Content-Type: application/json
Schema: PushRequest or EncryptedPushRequest
Variants:
{
"device_id": "native-client-queue",
"operations": [
{
"client_sequence": 1,
"op_id": "save-first-note",
"type": "note.save",
"note_id": "my-note",
"base_revision": 0,
"title": "First note",
"tags": "ideas",
"content": "Hello"
}
]
}Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
400 invalid_sync_request, invalid_sync_operation or invalid_encrypted_operation. Plaintext invalid_sync_operation includes permanent=true, operation_index, client_sequence and op_id.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
409 Sequence gap (PushResult), invalid_replayed_operation, server_instance_changed, or stale_vault_epoch (Error, possibly epoch). A changed server requires a user decision; never silently renumber/re-upload pending edits.
Content-Type: application/json
Schema: PushResult or Error
Variants:
423 vault_migration_in_progress (writes during preparation), vault_maintenance_in_progress (cleaning), or vault_unavailable. Pause writes; do not discard pending changes.
Content-Type: application/json
Schema: Error
426 encrypted_client_required: set X-Vylk-Vault-Protocol: 1 and implement the encrypted contract; never retry a plaintext body as encrypted.
Content-Type: application/json
Schema: Error
503 Pending file recovery prevents writes. Retain operations and retry with backoff after recovery; code depends on route (note_file_recovery_blocked, note_recovery_pending, note_recovery_failed).
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Subscribe to change and session hints
GET /api/events
Server-Sent Events, not a durable feed. Starts with retry: 3000, server and heartbeat events; heartbeats every 25 seconds. change events hint notes/preferences; session-expired closes the connection. No event IDs or Last-Event-ID replay. Reconnect with cookies and perform a durable pull after hints/reconnect. Plain-text 500 is possible before stream setup.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Path parameters
None.
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
| Header | Status | Details |
|---|---|---|
Cache-Control | 200 | const: "no-cache" |
X-Accel-Buffering | 200 | const: "no" |
Responses
200 A long-lived UTF-8 SSE stream.
Content-Type: text/event-stream
Schema: string
Type: string
retry: 3000
event: server
data: {"version":"3.0.4","revision":"asset-revision"}
event: change
data: {"type":"notes","sequence":1}
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
500 Stream setup failed.
Content-Type: text/plain
Schema: string
Type: string
Read account preferences
GET /api/prefs
Preferences are not encrypted. Read revision from JSON; no ETag is emitted.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Path parameters
None.
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Replace account preferences
PATCH /api/prefs
Despite PATCH, this is full replacement, not a partial merge. Read, modify, and send the complete object with If-Match. Missing fields can reset settings. Limit: 16 KiB JSON. _sync_patch/_sync_base only implement partial merging in sync prefs.save; this endpoint ignores those helpers.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
If-Match | string | No | Positive revision, plain or quoted. Missing or * permits unconditional replacement; avoid this in multi-device clients. |
Content-Type | string | Yes | application/json |
Path parameters
None.
Query parameters
None.
Request body
Content-Type: application/json
Schema: Preferences
Account-wide unencrypted UI settings. For sync partial updates, supply _sync_patch (desired fields) and _sync_base (previous values of those fields) with the operation base_revision; stale changes merge only when each current field equals the base or desired value. Defaults documented here apply to a fresh account, not every omitted field in PATCH.
| Field | Type | Required | Details |
|---|---|---|---|
revision | integer | No | format: "int64" |
autoSave | boolean | No | default: true |
reduceMotion | string | No | System follows the OS accessibility setting; Always disables interface animations; Never explicitly enables them regardless of the OS setting. enum: ["system", "always", "never"] default: "system" |
startView | string | No | enum: ["editor", "preview", "split", "zen"] default: "editor" |
hideToolbar | boolean | No | default: false |
hideSaveButton | boolean | No | default: true |
saveButtonLocation | string | No | enum: ["", "panel", "header"] default: "panel" |
collapseDetails | boolean | No | |
hideCursorHighlight | boolean | No | |
interactivePreview | boolean | No | default: true |
statusDisplay | string | No | |
contentWidth | ContentWidth | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty width. enum: ["", "compact", "standard", "wide", "full"] |
zenPageWidth | ContentWidth | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty width. enum: ["", "compact", "standard", "wide", "full"] |
theme | string | No | Built-in theme name or client-defined palette string; server stores it without interpreting the palette. |
accentColor | string | No | |
fontFamily | string | No | |
fontFamilyGoogle | boolean | No | |
fontSize | FontSize | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty size. enum: ["", "0.8rem", "0.9rem", "1rem", "1.1rem", "1.25rem", "1.5rem"] |
editorFontFamily | string | No | |
editorFontFamilyGoogle | boolean | No | |
editorFontSize | FontSize | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty size. enum: ["", "0.8rem", "0.9rem", "1rem", "1.1rem", "1.25rem", "1.5rem"] |
previewFontFamily | string | No | |
previewFontFamilyGoogle | boolean | No | |
previewFontSize | FontSize | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty size. enum: ["", "0.8rem", "0.9rem", "1rem", "1.1rem", "1.25rem", "1.5rem"] |
zenFontFamily | string | No | default: "Inter" |
zenFontFamilyGoogle | boolean | No | default: true |
zenFontSize | FontSize | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty size. enum: ["", "0.8rem", "0.9rem", "1rem", "1.1rem", "1.25rem", "1.5rem"] default: "1.25rem" |
zenWordCount | boolean | No | default: true |
zenShowTitle | boolean | No | default: true |
zenShowControls | boolean | No | default: true |
zenInteractivePreview | boolean | No | |
shortcutPrefix | ShortcutBinding | No | Exactly one step; default Mod+E (Ctrl+E on Windows/Linux, Command+E on macOS). |
keyboardShortcuts | object | No | maxProperties: 64 |
shortcutConfirmationSkips | object | No | maxProperties: 64 |
_sync_patch | object | No | |
_sync_base | object | No |
Unknown fields are rejected.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
409 preferences_revision_conflict, with current_revision.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Discover vault mode and derivation settings
GET /api/vault/bootstrap
Public, no key wrappers. Compare instance_id with the local cache identity before reading notes or restoring remembered keys. In legacy mode vault_id, epoch, and kdf are absent. reset_available does not override VYLK_DISABLE_VAULT_CHANGES; the reset API can still reject the operation.
Request headers
None.
Path parameters
None.
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
| Header | Status | Details |
|---|---|---|
Cache-Control | 200 | const: "no-store" |
Responses
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Read encrypted vault-key wrappers
GET /api/vault/keys
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Path parameters
None.
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Decrypt locally using the appropriate derived wrapping key.
Content-Type: application/json
Schema: object
| Field | Type | Required | Details |
|---|---|---|---|
vault_id | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
epoch | integer | Yes | format: "int64" |
wrapped_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
wrapped_recovery_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Change passphrase or recovery credentials
POST /api/vault/credentials
Limit: 32 KiB. Rewrap the SAME vault key; do not replace it. master requires fresh KDF parameters and updates the login proof; recovery updates the recovery proof/wrapper. current_recovery chooses which old proof authorizes the action, independent of kind. Always revokes the current session; sign_out_other_devices defaults true for either kind. Success requires a new login. Does not rotate the note key, vault ID, or epoch.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Content-Type | string | Yes | application/json |
Path parameters
None.
Query parameters
None.
Request body
Content-Type: application/json
Schema: CredentialChange
| Field | Type | Required | Details |
|---|---|---|---|
kind | string | Yes | enum: ["master", "recovery"] |
current_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
current_recovery | boolean | No | default: false |
new_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
wrapped_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
kdf_salt | VaultID | No | Required for master changes. minLength: 22 maxLength: 22 |
kdf_memory_kib | integer | No | Required for master changes. minimum: 19456 maximum: 262144 |
kdf_iterations | integer | No | Required for master changes. minimum: 2 maximum: 10 |
sign_out_other_devices | boolean | No | default: true |
Unknown fields are rejected.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
| Header | Status | Details |
|---|---|---|
Set-Cookie | 200 | May contain multiple Set-Cookie lines. session is HttpOnly, Path=/, SameSite=Lax, 180-day lifetime; vylk-device groups browser-profile sessions and lasts 365 days. Secure is set for HTTPS. Authenticated activity periodically renews session. Never combine Set-Cookie lines with commas. |
Responses
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
403 vault_changes_disabled: server policy rejects vault/device mutations. Ordinary notes and preferences remain usable.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Start a new empty vault (destructive)
POST /api/vault/reset
Optional recovery-from-total-key-loss operation, NOT passphrase recovery or decryption. Requires configured VYLK_PASSWORD in the body, not a session/proof. Limit: 32 KiB. Archives the old database/ciphertext for 30 days, then replaces credentials, clears notes/feed/queue counters and revokes all sessions. Returns a new session. The old data still needs its old key. instance_id remains unchanged; vault_id changes and epoch increments. Confirm data-loss consequences before calling. Do not retry an ambiguous reset blindly.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Content-Type | string | Yes | application/json |
Path parameters
None.
Query parameters
None.
Request body
Content-Type: application/json
Schema: VaultReset
| Field | Type | Required | Details |
|---|---|---|---|
vault_id | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
kdf_salt | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
kdf_memory_kib | integer | Yes | minimum: 19456 maximum: 262144 |
kdf_iterations | integer | Yes | minimum: 2 maximum: 10 |
auth_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
recovery_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
wrapped_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
wrapped_recovery_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
password | string | Yes | Configured VYLK_PASSWORD, not vault passphrase. |
Unknown fields are rejected.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
| Header | Status | Details |
|---|---|---|
Set-Cookie | 200 | May contain multiple Set-Cookie lines. session is HttpOnly, Path=/, SameSite=Lax, 180-day lifetime; vylk-device groups browser-profile sessions and lasts 365 days. Secure is set for HTTPS. Authenticated activity periodically renews session. Never combine Set-Cookie lines with commas. |
Retry-After | 429 | Delay in seconds. |
Responses
200 New empty vault and new session.
Content-Type: application/json
Schema: object
| Field | Type | Required | Details |
|---|---|---|---|
ok | boolean | Yes | const: true |
vault_id | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
epoch | integer | Yes | format: "int64" |
archive_expires_at | string | Yes | format: "date-time" |
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
403 vault_changes_disabled or vault_reset_unavailable (no server password).
Content-Type: application/json
Schema: Error
409 vault_reset_unavailable (not an active encrypted vault).
Content-Type: application/json
Schema: Error
429 login_rate_limited. Wait retry_after seconds; do not hammer credentials.
Content-Type: application/json
Schema: Error
503 Pending file recovery prevents writes. Retain operations and retry with backoff after recovery; code depends on route (note_file_recovery_blocked, note_recovery_pending, note_recovery_failed).
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Prepare plaintext-to-encrypted migration
POST /api/vault/migration/start
Limit: 32 KiB. Requires old_password even with a session. Generate a random vault key/ID and both credential wrappers locally. Creates a consistent backup and pauses plaintext writes. Repeating the same vault_id resumes setup; it does not replace previously stored credentials. Preserve the original setup secrets until commit. No abort or disable-encryption API exists.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Content-Type | string | Yes | application/json |
Path parameters
None.
Query parameters
None.
Request body
Content-Type: application/json
Schema: MigrationStart
| Field | Type | Required | Details |
|---|---|---|---|
vault_id | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
kdf_salt | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
kdf_memory_kib | integer | Yes | minimum: 19456 maximum: 262144 |
kdf_iterations | integer | Yes | minimum: 2 maximum: 10 |
auth_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
recovery_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
wrapped_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
wrapped_recovery_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
old_password | string | Yes |
Unknown fields are rejected.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Migration prepared.
Content-Type: application/json
Schema: object
| Field | Type | Required | Details |
|---|---|---|---|
ok | boolean | Yes | const: true |
epoch | integer | Yes | const: 1 |
400 invalid_vault_setup, including incorrect old_password.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
403 vault_changes_disabled: server policy rejects vault/device mutations. Ordinary notes and preferences remain usable.
Content-Type: application/json
Schema: Error
503 Pending file recovery prevents writes. Retain operations and retry with backoff after recovery; code depends on route (note_file_recovery_blocked, note_recovery_pending, note_recovery_failed).
Content-Type: application/json
Schema: Error
507 migration_backup_failed: free storage and retry same setup.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Read encryption migration progress
GET /api/vault/migration/status
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Path parameters
None.
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Progress counts and mode.
Content-Type: application/json
Schema: object
| Field | Type | Required | Details |
|---|---|---|---|
mode | VaultMode | Yes | enum: ["legacy", "preparing", "cleaning", "encrypted"] |
backup_ready | boolean | Yes | |
source_notes | integer | Yes | |
staged_notes | integer | Yes | |
verified_notes | integer | Yes |
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
409 Migration state/source mismatch: vault_migration_not_started, vault_migration_not_ready, vault_note_changed, vault_verify_mismatch, vault_migration_incomplete, or unexpected_note_files. Inspect state before retrying.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
List notes still needing verification
GET /api/vault/migration/next
Up to 100 notes in lexicographic ID order. Empty array means no remaining unverified notes. A source revision change invalidates its previous verification.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Path parameters
None.
Query parameters
| Parameter | Type | Required | Details |
|---|---|---|---|
after | string | No | Last ID from the preceding page, exclusive. This is an ID, not a notes-page cursor. |
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Source IDs and revisions.
Content-Type: application/json
Schema: array of object
Type: array of object
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
409 Migration state/source mismatch: vault_migration_not_started, vault_migration_not_ready, vault_note_changed, vault_verify_mismatch, vault_migration_incomplete, or unexpected_note_files. Inspect state before retrying.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Stage an encrypted copy of a source note
POST /api/vault/migration/stage
Limit: 8 MiB. Source revision must match. Restaging clears its verification. Fetch and decrypt the staged copy before verifying it.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Content-Type | string | Yes | application/json |
Path parameters
None.
Query parameters
None.
Request body
Content-Type: application/json
Schema: StagedRequest
| Field | Type | Required | Details |
|---|---|---|---|
id | ID | Yes | pattern: "^[A-Za-z0-9_-]{1,64}$" |
revision | integer | Yes | format: "int64" minimum: 1 |
summary | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
body | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
Unknown fields are rejected.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
403 vault_changes_disabled: server policy rejects vault/device mutations. Ordinary notes and preferences remain usable.
Content-Type: application/json
Schema: Error
409 Migration state/source mismatch: vault_migration_not_started, vault_migration_not_ready, vault_note_changed, vault_verify_mismatch, vault_migration_incomplete, or unexpected_note_files. Inspect state before retrying.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Read a staged encrypted note
GET /api/vault/migration/staged/{id}
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Path parameters
| Parameter | Type | Required | Details |
|---|---|---|---|
id | ID | Yes | Note identifier. pattern: "^[A-Za-z0-9_-]{1,64}$" |
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Staged copy. body_hash is SHA-256 of exact stored envelope JSON bytes, not decrypted Markdown or reserialized JSON.
Content-Type: application/json
Schema: StagedNote
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Acknowledge client verification of a staged note
POST /api/vault/migration/verify
Limit: 4 KiB. First decrypt the downloaded staged summary/body and compare them with the source. The server only checks the stored hash and source revision; it cannot verify decryption. Send the body_hash returned by staged/{id}.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Content-Type | string | Yes | application/json |
Path parameters
None.
Query parameters
None.
Request body
Content-Type: application/json
Schema: object
| Field | Type | Required | Details |
|---|---|---|---|
id | ID | Yes | pattern: "^[A-Za-z0-9_-]{1,64}$" |
body_hash | string | Yes | SHA-256, lowercase hexadecimal. minLength: 64 maxLength: 64 |
Unknown fields are rejected.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
400 Malformed, oversized, unknown-field, or invalid request (invalid_request or a route-specific code). JSON body limits return 400, not 413.
Content-Type: application/json
Schema: Error
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
403 vault_changes_disabled: server policy rejects vault/device mutations. Ordinary notes and preferences remain usable.
Content-Type: application/json
Schema: Error
409 Migration state/source mismatch: vault_migration_not_started, vault_migration_not_ready, vault_note_changed, vault_verify_mismatch, vault_migration_incomplete, or unexpected_note_files. Inspect state before retrying.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Commit verified encrypted notes and remove plaintext
POST /api/vault/migration/commit
No body required. Requires backup and all source notes verified. Revokes all sessions; log in with the derived proof afterward. Note queue counters survive migration; do not restart sequence at 1. If vault_cleanup_pending returns 500, encrypted cutover already succeeded; cleanup resumes on server restart. Do not revert to plaintext or start a new setup.
Request headers
| Header | Type | Required | Details |
|---|---|---|---|
Cookie | string | Yes | session=<token>; use a persistent cookie jar. |
Path parameters
None.
Query parameters
None.
Request body
None.
Response headers
Content-Type is shown with each response below; 204 has no body. Any authenticated response may renew the session cookie. Infrastructure may add transport/security headers.
No additional documented response headers.
Responses
200 Encrypted cutover completed.
Content-Type: application/json
Schema: object
| Field | Type | Required | Details |
|---|---|---|---|
ok | boolean | Yes | const: true |
mode | string | Yes | const: "encrypted" |
401 authentication_required for a missing, expired, or revoked session; credential endpoints can return invalid_credentials.
Content-Type: application/json
Schema: Error
403 vault_changes_disabled: server policy rejects vault/device mutations. Ordinary notes and preferences remain usable.
Content-Type: application/json
Schema: Error
409 Migration state/source mismatch: vault_migration_not_started, vault_migration_not_ready, vault_note_changed, vault_verify_mismatch, vault_migration_incomplete, or unexpected_note_files. Inspect state before retrying.
Content-Type: application/json
Schema: Error
default Storage/internal failure with a route-specific code. Some writes may already have committed; reconcile rather than assuming nothing changed.
Content-Type: application/json
Schema: Error
Wire models
ID
pattern: "^[A-Za-z0-9_-]{1,64}$"
Type: string
QueueID
pattern: "^[A-Za-z0-9_-]{1,128}$"
Type: string
VaultID
16 random bytes, unpadded strict base64url (22 characters).
16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22
Type: string
Proof
32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead.
32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43
Type: string
Envelope
AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes.
| Field | Type | Required | Details |
|---|---|---|---|
v | integer | Yes | const: 1 |
nonce | string | Yes | minLength: 16 maxLength: 16 |
ciphertext | string | Yes | minLength: 22 |
Error
| Field | Type | Required | Details |
|---|---|---|---|
error | string | Yes | Human-readable message; branch on code instead. |
code | string | Yes | |
note_id | string | No | |
current_revision | integer | No | format: "int64" |
epoch | integer | No | format: "int64" |
retry_after | integer | No | |
permanent | boolean | No | |
operation_index | integer | No | minimum: 0 |
client_sequence | integer | No | format: "int64" |
op_id | string | No |
OK
| Field | Type | Required | Details |
|---|---|---|---|
ok | boolean | Yes | const: true |
Login
| Field | Type | Required | Details |
|---|---|---|---|
password | string | No | Server password, legacy/preparing only. |
proof | Proof | No | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
recovery_proof | Proof | No | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
Unknown fields are rejected.
LoginResult
| Field | Type | Required | Details |
|---|---|---|---|
ok | boolean | Yes | const: true |
version | string | Yes | |
revision | string | Yes | Application asset revision. |
instance_id | string | Yes | Persistent database identity. |
CheckResult
| Field | Type | Required | Details |
|---|---|---|---|
ok | boolean | Yes | const: true |
version | string | Yes | |
revision | string | Yes | Application asset revision. |
instance_id | string | Yes | Persistent database identity. |
vault_mode | VaultMode | Yes | enum: ["legacy", "preparing", "cleaning", "encrypted"] |
vault_epoch | integer | Yes | format: "int64" |
Device
| Field | Type | Required | Details |
|---|---|---|---|
id | string | Yes | |
name | string | Yes | |
last_seen_at | string | Yes | format: "date-time" |
current | boolean | Yes |
PlainNote
| Field | Type | Required | Details |
|---|---|---|---|
id | ID | Yes | pattern: "^[A-Za-z0-9_-]{1,64}$" |
title | string | Yes | At most 512 UTF-8 bytes on save. |
filename | string | Yes | Storage name; not an API path. |
tags | string | Yes | Comma-separated, trimmed and deduplicated, case-sensitive; at most 4096 UTF-8 bytes in a save request. |
pinned | boolean | Yes | |
pin_order | integer | No | May be absent when zero. format: "int64" |
created_at | string | Yes | format: "date-time" |
updated_at | string | Yes | format: "date-time" |
revision | integer | Yes | format: "int64" |
EncryptedNote
| Field | Type | Required | Details |
|---|---|---|---|
id | ID | Yes | pattern: "^[A-Za-z0-9_-]{1,64}$" |
summary | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
pinned | boolean | Yes | |
pin_order | integer | Yes | format: "int64" |
created_at | string | Yes | format: "date-time" |
updated_at | string | Yes | format: "date-time" |
revision | integer | Yes | format: "int64" |
epoch | integer | Yes | format: "int64" |
NotesPage
| Field | Type | Required | Details |
|---|---|---|---|
notes | array of NoteSummary | Yes | |
nextCursor | string | No | Opaque cursor; absent/empty at end. |
SaveNote
| Field | Type | Required | Details |
|---|---|---|---|
id | ID | No | pattern: "^[A-Za-z0-9_-]{1,64}$" |
title | string | No | |
content | string | No | |
tags | string | No | |
base_revision | integer | No | Required when id is supplied; 0 creates a new client-generated ID. format: "int64" |
Unknown fields are rejected.
SaveEncryptedNote
| Field | Type | Required | Details |
|---|---|---|---|
id | ID | Yes | pattern: "^[A-Za-z0-9_-]{1,64}$" |
summary | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
body | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
epoch | integer | Yes | format: "int64" minimum: 1 |
base_revision | integer | Yes | format: "int64" |
Unknown fields are rejected.
Change
| Field | Type | Required | Details |
|---|---|---|---|
sequence | integer | Yes | format: "int64" |
note_id | ID | Yes | pattern: "^[A-Za-z0-9_-]{1,64}$" |
revision | integer | Yes | format: "int64" |
deleted | boolean | Yes | |
changed_at | string | Yes | format: "date-time" |
ChangesPage
| Field | Type | Required | Details |
|---|---|---|---|
changes | array of Change | Yes | |
nextSequence | integer | Yes | format: "int64" |
hasMore | boolean | Yes | |
resetRequired | boolean | No | |
instance_id | string | Yes |
Operation
Conditional fields: save needs note_id/base_revision and complete title/tags/content; delete needs note_id/base_revision >= 1; pin needs note_id/base_revision >= 0/pinned; prefs.save needs prefs and preferably base_revision; noop only needs sequence/op_id/type. base_content is accepted but does not cause server-side text merging. Existing note pin state changes through note.pin.
| Field | Type | Required | Details |
|---|---|---|---|
client_sequence | integer | Yes | format: "int64" minimum: 1 |
op_id | QueueID | Yes | pattern: "^[A-Za-z0-9_-]{1,128}$" |
type | string | Yes | enum: ["note.save", "note.delete", "note.pin", "prefs.save", "noop"] |
note_id | ID | No | pattern: "^[A-Za-z0-9_-]{1,64}$" |
base_revision | integer | No | format: "int64" |
title | string | No | |
tags | string | No | |
content | string | No | |
base_content | string | No | |
pinned | boolean | No | |
prefs | Preferences | No | Account-wide unencrypted UI settings. For sync partial updates, supply _sync_patch (desired fields) and _sync_base (previous values of those fields) with the operation base_revision; stale changes merge only when each current field equals the base or desired value. Defaults documented here apply to a fresh account, not every omitted field in PATCH. |
Unknown fields are rejected.
EncryptedOperation
save requires note_id/base_revision/summary/body/epoch; delete and pin require note_id/base_revision/epoch (and pinned for pin). prefs.save and noop do not require epoch. Plaintext title/tags/content/base_content fields are rejected. Envelope AAD binds epoch; do not change it without re-encrypting.
| Field | Type | Required | Details |
|---|---|---|---|
client_sequence | integer | Yes | format: "int64" minimum: 1 |
op_id | QueueID | Yes | pattern: "^[A-Za-z0-9_-]{1,128}$" |
type | string | Yes | enum: ["note.save", "note.delete", "note.pin", "prefs.save", "noop"] |
note_id | ID | No | pattern: "^[A-Za-z0-9_-]{1,64}$" |
base_revision | integer | No | format: "int64" |
summary | Envelope | No | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
body | Envelope | No | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
epoch | integer | No | format: "int64" minimum: 1 |
pinned | boolean | No | |
prefs | Preferences | No | Account-wide unencrypted UI settings. For sync partial updates, supply _sync_patch (desired fields) and _sync_base (previous values of those fields) with the operation base_revision; stale changes merge only when each current field equals the base or desired value. Defaults documented here apply to a fresh account, not every omitted field in PATCH. |
Unknown fields are rejected.
PushRequest
| Field | Type | Required | Details |
|---|---|---|---|
device_id | QueueID | Yes | pattern: "^[A-Za-z0-9_-]{1,128}$" |
operations | array of Operation | Yes | maxItems: 100 |
Unknown fields are rejected.
EncryptedPushRequest
| Field | Type | Required | Details |
|---|---|---|---|
device_id | QueueID | Yes | pattern: "^[A-Za-z0-9_-]{1,128}$" |
operations | array of EncryptedOperation | Yes | maxItems: 100 |
Unknown fields are rejected.
Acknowledgement
| Field | Type | Required | Details |
|---|---|---|---|
client_sequence | integer | Yes | format: "int64" |
op_id | string | Yes | |
status | string | Yes | All consume sequence. compacted acknowledges an old consumed operation whose detailed result was pruned, not proof that remote content matches. enum: ["applied", "conflict", "compacted"] |
revision | integer | No | format: "int64" |
current_revision | integer | No | format: "int64" |
pin_order | integer | No | format: "int64" |
PushResult
| Field | Type | Required | Details |
|---|---|---|---|
acknowledged | array of Acknowledgement | Yes | |
expected_sequence | integer | Yes | format: "int64" minimum: 1 |
ShortcutStep
First step modifiers are [Mod] or [Mod, Shift]; second step modifiers must be empty.
| Field | Type | Required | Details |
|---|---|---|---|
key | string | Yes | pattern: "^[A-Za-z0-9/;]$" |
modifiers | array of string | Yes |
Unknown fields are rejected.
ShortcutBinding
| Field | Type | Required | Details |
|---|---|---|---|
steps | array of ShortcutStep | Yes | minItems: 1 maxItems: 2 |
Unknown fields are rejected.
FontSize
Empty is accepted in full replacement; a sync patch must use a listed nonempty size.
Empty is accepted in full replacement; a sync patch must use a listed nonempty size. enum: ["", "0.8rem", "0.9rem", "1rem", "1.1rem", "1.25rem", "1.5rem"]
Type: string
ContentWidth
Empty is accepted in full replacement; a sync patch must use a listed nonempty width.
Empty is accepted in full replacement; a sync patch must use a listed nonempty width. enum: ["", "compact", "standard", "wide", "full"]
Type: string
Preferences
Account-wide unencrypted UI settings. For sync partial updates, supply _sync_patch (desired fields) and _sync_base (previous values of those fields) with the operation base_revision; stale changes merge only when each current field equals the base or desired value. Defaults documented here apply to a fresh account, not every omitted field in PATCH.
| Field | Type | Required | Details |
|---|---|---|---|
revision | integer | No | format: "int64" |
autoSave | boolean | No | default: true |
reduceMotion | string | No | System follows the OS accessibility setting; Always disables interface animations; Never explicitly enables them regardless of the OS setting. enum: ["system", "always", "never"] default: "system" |
startView | string | No | enum: ["editor", "preview", "split", "zen"] default: "editor" |
hideToolbar | boolean | No | default: false |
hideSaveButton | boolean | No | default: true |
saveButtonLocation | string | No | enum: ["", "panel", "header"] default: "panel" |
collapseDetails | boolean | No | |
hideCursorHighlight | boolean | No | |
interactivePreview | boolean | No | default: true |
statusDisplay | string | No | |
contentWidth | ContentWidth | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty width. enum: ["", "compact", "standard", "wide", "full"] |
zenPageWidth | ContentWidth | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty width. enum: ["", "compact", "standard", "wide", "full"] |
theme | string | No | Built-in theme name or client-defined palette string; server stores it without interpreting the palette. |
accentColor | string | No | |
fontFamily | string | No | |
fontFamilyGoogle | boolean | No | |
fontSize | FontSize | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty size. enum: ["", "0.8rem", "0.9rem", "1rem", "1.1rem", "1.25rem", "1.5rem"] |
editorFontFamily | string | No | |
editorFontFamilyGoogle | boolean | No | |
editorFontSize | FontSize | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty size. enum: ["", "0.8rem", "0.9rem", "1rem", "1.1rem", "1.25rem", "1.5rem"] |
previewFontFamily | string | No | |
previewFontFamilyGoogle | boolean | No | |
previewFontSize | FontSize | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty size. enum: ["", "0.8rem", "0.9rem", "1rem", "1.1rem", "1.25rem", "1.5rem"] |
zenFontFamily | string | No | default: "Inter" |
zenFontFamilyGoogle | boolean | No | default: true |
zenFontSize | FontSize | No | Empty is accepted in full replacement; a sync patch must use a listed nonempty size. enum: ["", "0.8rem", "0.9rem", "1rem", "1.1rem", "1.25rem", "1.5rem"] default: "1.25rem" |
zenWordCount | boolean | No | default: true |
zenShowTitle | boolean | No | default: true |
zenShowControls | boolean | No | default: true |
zenInteractivePreview | boolean | No | |
shortcutPrefix | ShortcutBinding | No | Exactly one step; default Mod+E (Ctrl+E on Windows/Linux, Command+E on macOS). |
keyboardShortcuts | object | No | maxProperties: 64 |
shortcutConfirmationSkips | object | No | maxProperties: 64 |
_sync_patch | object | No | |
_sync_base | object | No |
Unknown fields are rejected.
VaultMode
enum: ["legacy", "preparing", "cleaning", "encrypted"]
Type: string
Bootstrap
| Field | Type | Required | Details |
|---|---|---|---|
mode | VaultMode | Yes | enum: ["legacy", "preparing", "cleaning", "encrypted"] |
reset_available | boolean | Yes | |
instance_id | string | Yes | Persistent database identity. Compare before reading cached notes or resuming sync; a restart with the same database preserves it. |
require_strong_passwords | boolean | Yes | |
version | string | Yes | |
revision | string | Yes | |
vault_id | VaultID | No | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
epoch | integer | No | format: "int64" |
kdf | object | No |
VaultSetup
| Field | Type | Required | Details |
|---|---|---|---|
vault_id | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
kdf_salt | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
kdf_memory_kib | integer | Yes | minimum: 19456 maximum: 262144 |
kdf_iterations | integer | Yes | minimum: 2 maximum: 10 |
auth_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
recovery_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
wrapped_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
wrapped_recovery_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
MigrationStart
| Field | Type | Required | Details |
|---|---|---|---|
vault_id | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
kdf_salt | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
kdf_memory_kib | integer | Yes | minimum: 19456 maximum: 262144 |
kdf_iterations | integer | Yes | minimum: 2 maximum: 10 |
auth_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
recovery_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
wrapped_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
wrapped_recovery_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
old_password | string | Yes |
Unknown fields are rejected.
VaultReset
| Field | Type | Required | Details |
|---|---|---|---|
vault_id | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
kdf_salt | VaultID | Yes | 16 random bytes, unpadded strict base64url (22 characters). minLength: 22 maxLength: 22 |
kdf_memory_kib | integer | Yes | minimum: 19456 maximum: 262144 |
kdf_iterations | integer | Yes | minimum: 2 maximum: 10 |
auth_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
recovery_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
wrapped_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
wrapped_recovery_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
password | string | Yes | Configured VYLK_PASSWORD, not vault passphrase. |
Unknown fields are rejected.
CredentialChange
| Field | Type | Required | Details |
|---|---|---|---|
kind | string | Yes | enum: ["master", "recovery"] |
current_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
current_recovery | boolean | No | default: false |
new_proof | Proof | Yes | 32 derived bytes, unpadded strict base64url (43 characters). Reusable authentication secret; never log it or send SHA-256(proof) instead. minLength: 43 maxLength: 43 |
wrapped_key | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
kdf_salt | VaultID | No | Required for master changes. minLength: 22 maxLength: 22 |
kdf_memory_kib | integer | No | Required for master changes. minimum: 19456 maximum: 262144 |
kdf_iterations | integer | No | Required for master changes. minimum: 2 maximum: 10 |
sign_out_other_devices | boolean | No | default: true |
Unknown fields are rejected.
StagedRequest
| Field | Type | Required | Details |
|---|---|---|---|
id | ID | Yes | pattern: "^[A-Za-z0-9_-]{1,64}$" |
revision | integer | Yes | format: "int64" minimum: 1 |
summary | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
body | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
Unknown fields are rejected.
StagedNote
| Field | Type | Required | Details |
|---|---|---|---|
id | ID | Yes | pattern: "^[A-Za-z0-9_-]{1,64}$" |
revision | integer | Yes | format: "int64" |
summary | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
body | Envelope | Yes | AES-256-GCM envelope, an object rather than a JSON-encoded string. Nonce: 12 bytes. Ciphertext includes the final 16-byte authentication tag. Both are strict unpadded base64url. Summary envelope JSON is limited to 37,888 bytes. |
epoch | integer | Yes | format: "int64" |
body_hash | string | Yes |