Skip to content
Hoody.com

The KV Store time-travel API exposes the operation log that backs Hoody’s SQLite-backed key-value store. Use these endpoints to follow a table’s change feed, inspect what changed, reconstruct a key or an entire table as it was at a specific moment, diff two snapshots, and roll back unwanted mutations. Every endpoint operates against a single database file (or directory-mode table) and supports the same db and optional table query parameters used by the rest of the SQLite API.

All examples in this page use the placeholder host https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com. A real host looks like https://67e89abc123def456789abcd-890abcdef12345678901cdef-sqlite-1.node-example-1.containers.hoody.com.

The history endpoints cover the audit trail for SQL and KV operations executed against a database file. The history table is per-database; specifying the db parameter selects which trail to read or prune.

Retrieve the most recent query execution entries for a database. The server keeps the most recent 1000 entries; page through them with limit and offset, and use total to know when to stop. A transaction that failed or was rolled back is recorded too, every item with success=false: the failing item carries its own error, the others say the transaction was rolled back.

NameInTypeRequiredDescription
dbquerystringYesDatabase file path
limitqueryintegerNoMaximum number of entries to return (0 means the default; capped at 1000). Default: 100
offsetqueryintegerNoNumber of newest entries to skip. Default: 0
timeoutqueryintegerNoDeadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Terminal window
curl -X GET "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/history?db=/hoody/databases/orders.db&limit=50" \
-H "Authorization: Bearer $HOODY_TOKEN"

Return aggregated counters over the history table for a single database.

NameInTypeRequiredDescription
dbquerystringYesDatabase file path
timeoutqueryintegerNoDeadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Terminal window
curl -X GET "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/history/stats?db=/hoody/databases/orders.db" \
-H "Authorization: Bearer $HOODY_TOKEN"

Delete every entry in the history table for a single database. Use with care: this is irreversible and removes the audit trail used for time-travel.

NameInTypeRequiredDescription
dbquerystringYesDatabase file path
timeoutqueryintegerNoDeadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Terminal window
curl -X DELETE "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/history?db=/hoody/databases/orders.db" \
-H "Authorization: Bearer $HOODY_TOKEN"

Delete a single query history entry by its numeric ID.

NameInTypeRequiredDescription
indexpathintegerYesHistory entry ID
dbquerystringYesDatabase file path
timeoutqueryintegerNoDeadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Terminal window
curl -X DELETE "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/history/411?db=/hoody/databases/orders.db" \
-H "Authorization: Bearer $HOODY_TOKEN"

Per-key history reads return the chronological log of every recorded operation against a single key. Writes made with history=false still appear as UNRECORDED entries: they record that an untracked write happened and the span of time it covers (timestamp to unrecorded_until), with no value, content type or expiry. They exist so time travel can answer “indeterminate” rather than return a stale value once such a write’s row is gone, and they count towards limit.

Retrieve the operation history for a specific key, showing every change over time.

NameInTypeRequiredDescription
keypathstringYesKey name
dbquerystringYesDatabase file path
tablequerystringNoCustom table name. Default: "kv_store"
limitqueryintegerNoMaximum number of operations to return (0 means the default of 50, clamped to a maximum of 1000). Default: 50
timeoutqueryintegerNoDeadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Terminal window
curl -X GET "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/session:9f2e/history?db=/hoody/databases/orders.db&limit=10" \
-H "Authorization: Bearer $HOODY_TOKEN"

The change feed endpoints deliver the table’s change log to clients. Every physical change of a row is one event, whoever made it (KV API, SQL through POST /db, another process): set (written), ttl (only the expiry changed), delete, expire (an expired row was removed). Events are at-least-once and in order, so a client cache stays correct by applying them in order and re-reading values it needs, or by asking for small values inline with include_values.

Start without since: the response has no events and cursor is the current position. To build a cache, take that cursor, then list the keys (GET /kv), then follow the changes from the cursor. Continue each page from next_cursor. wait (seconds) holds a request that has no events yet until one arrives or the time is up (capped one second before the request deadline; raise it with timeout). prefix only returns keys starting with it; next_cursor still advances past other changes.

Page through the changes made to a KV table, oldest first, or wait for the next ones (long poll).

NameInTypeRequiredDescription
dbquerystringYesDatabase path
tablequerystringNoKV table name. Default: kv_store
sincequerystringNoCursor to continue after (next_cursor of the previous page, or the cursor of an event). Omit to get the current cursor.
prefixquerystringNoOnly keys starting with this prefix
limitqueryintegerNoMaximum events in the page (1-1000, default 100). Default: 100
waitqueryintegerNoSeconds to wait for the first event when there is none yet (0-60, default 0). Default: 0
include_valuesquerybooleanNoAttach the value to set/ttl events when it is still current, not expired and at most 256 KiB (otherwise value_omitted says why)
timeoutqueryintegerNoDeadline for this request, in whole seconds, clamped to [1, 300]. Once it passes, a long operation stops at its next checkpoint rather than being cut off mid-step; a step already running, such as a filesystem scan or a wait for another writer, finishes first. A request that has not finished by then answers 503 REQUEST_TIMEOUT, except that a write which has already committed still returns its success. A value that is not a whole number is ignored and the default applies. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Terminal window
curl -X GET "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/changes?db=/hoody/databases/orders.db&table=kv_store&wait=10" \
-H "Authorization: Bearer $HOODY_TOKEN"

Server-Sent Events stream of the changes made to a KV table (see GET /changes for the events and cursors).

Frames: retry: 1000, then event: ready (id: = the cursor the stream continues from, data {"cursor","oldest_cursor"}); event: change for each matching change (id: = its cursor, data = the event); event: checkpoint (id: = the position scanned, data {"cursor"}) every 15 s and after 1000 skipped changes, so a reconnect skips them; and, before the server closes the stream, event: reset (data {"reason"}: cursor_expired, database_replaced, table_changed, future_cursor, re-read the state and start again) or event: end (data {"reason"}: database_deleted, shutdown, max_lifetime, reconnect with the last id for shutdown and max_lifetime).

Resume with Last-Event-ID (the last id: received; wins over since). Without either, the stream starts at the current position. An invalid cursor at the start is a JSON 410 instead of a stream. A stream lasts at most one hour (server setting); the request timeout does not apply. The number of open streams is capped server-wide: over the cap the answer is 503 with Retry-After. Not available in directory mode.

NameInTypeRequiredDescription
dbquerystringYesDatabase path
tablequerystringNoKV table name. Default: kv_store
sincequerystringNoCursor to continue after; Last-Event-ID wins when both are sent
prefixquerystringNoOnly keys starting with this prefix
include_valuesquerybooleanNoAttach small current values to set/ttl events (see GET /changes)
Last-Event-IDheaderstringNoResumes the stream after this event id.
Terminal window
curl -N "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/changes/stream?db=/hoody/databases/orders.db&table=kv_store" \
-H "Authorization: Bearer $HOODY_TOKEN"

These endpoints reconstruct past state from the operation log. A key snapshot rebuilds a single key’s value at a chosen operation number. A table snapshot rebuilds the whole KV table at a chosen Unix timestamp and supports prefix filtering to narrow large reconstructions. The diff endpoint compares two timestamps and reports created, modified, and deleted keys.

Reconstruct a single key’s value as it was at a specific operation number.

NameInTypeRequiredDescription
keypathstringYesKey name
dbquerystringYesDatabase file path
tablequerystringNoCustom table name. Default: "kv_store"
op_numberqueryintegerYesOperation number to reconstruct from
timeoutqueryintegerNoDeadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Terminal window
curl -X GET "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/session:9f2e/snapshot?db=/hoody/databases/orders.db&op_number=380" \
-H "Authorization: Bearer $HOODY_TOKEN"

Reconstruct the entire KV table as it was at a given Unix timestamp.

NameInTypeRequiredDescription
dbquerystringYesDatabase file path
tablequerystringNoCustom table name. Default: "kv_store"
timestampqueryintegerYesUnix timestamp to reconstruct from
limitqueryintegerNoMaximum number of keys to return. Default: 100
prefixquerystringNoFilter keys by prefix
timeoutqueryintegerNoDeadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Terminal window
curl -X GET "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/snapshot?db=/hoody/databases/orders.db&timestamp=1716900000&prefix=session:&limit=50" \
-H "Authorization: Bearer $HOODY_TOKEN"

Compare the KV table state between two Unix timestamps. The response reports keys that were created, modified, and deleted in the window.

NameInTypeRequiredDescription
dbquerystringYesDatabase file path
tablequerystringNoCustom table name. Default: "kv_store"
fromqueryintegerYesStarting timestamp (Unix)
toqueryintegerYesEnding timestamp (Unix)
keysquerystringNoComma-separated list of keys to compare (optional). When given, exactly these keys are reconstructed (duplicates collapsed).
timeoutqueryintegerNoDeadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Terminal window
curl -X GET "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/diff?db=/hoody/databases/orders.db&from=1716800000&to=1717000123&keys=session:9f2e,session:a31c" \
-H "Authorization: Bearer $HOODY_TOKEN"

The rollback endpoints revert KV state using the same operation log that powers the time-travel reads. A key-level rollback undoes the most recent steps operations for one key. A table-level rollback rewinds the entire table to a target timestamp and supports a dry-run preview.

Undo the last N operations on a single key by replaying the inverse operations from the history log.

NameInTypeRequiredDescription
keypathstringYesKey name
dbquerystringYesDatabase file path
tablequerystringNoCustom table name. Default: "kv_store"
stepsqueryintegerNoNumber of operations to rollback. Default: 1
create_db_if_missingquerybooleanNoCreate database file if it is missing. The legacy alias auto_create is still accepted and means the same thing; the two must agree when both are sent. Default: false
timeoutqueryintegerNoDeadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Idempotency-KeyheaderstringNoReplays the stored response for a repeat of the same key (24 h).
Terminal window
curl -X POST "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/session:9f2e/rollback?db=/hoody/databases/orders.db&steps=1" \
-H "Authorization: Bearer $HOODY_TOKEN"

Roll the entire KV table back to a target Unix timestamp. Use dry_run=true first to preview the set of keys that would change, then re-issue with confirm=yes to apply.

NameInTypeRequiredDescription
dbquerystringYesDatabase file path
tablequerystringNoCustom table name. Default: "kv_store"
to_timestampqueryintegerYesTarget timestamp to rollback to (Unix)
dry_runquerybooleanNoPreview changes without applying. Default: false
confirmquerystringNoMust be 'yes' to execute actual rollback
create_db_if_missingquerybooleanNoCreate database file if it is missing. The legacy alias auto_create is still accepted and means the same thing; the two must agree when both are sent. Default: false
timeoutqueryintegerNoDeadline for this request, in seconds, clamped to [1, 300]. It sets the request context deadline that handlers check between steps, so a long operation stops at the next checkpoint rather than being cut off mid-step; work already in progress, such as a filesystem scan or a wait for another writer, runs to completion. This is a server-side deadline, not a client transport timeout. Omitted, the server default applies (30 seconds unless the deployment overrides it). Default: 30
Idempotency-KeyheaderstringNoReplays the stored response for a repeat of the same key (24 h).

Optional filters applied to the rollback. Omit the body to roll back the whole table.

NameTypeRequiredDescription
keysarray of stringNoKeys to roll back. Omit or leave empty to roll back the whole table. At most 10000 entries; more is rejected with 413.
exclude_keysarray of stringNoKeys to leave untouched. Applied after keys. At most 10000 entries; more is rejected with 413.
Terminal window
curl -X POST "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/rollback?db=/hoody/databases/orders.db&to_timestamp=1716900000&dry_run=true" \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"keys":["session:9f2e","session:a31c"]}'