Skip to content
Hoody.com

The KV store is a per-database key-value table. It runs in either SQLite mode (one .db file) or directory mode (a folder of files), and supports hierarchical keys, optional TTL (SQLite mode), JSON-path access into nested values, compare-and-swap, and time-travel reads that reconstruct the value at a given moment using the key’s recorded history (and fall back to the current value when no recorded write establishes the value at that moment). This page covers the everyday read and write operations: get, set, delete, exists, the two atomic counter operations (incr and decr), reading a key’s full entry in one call, and TTL management.

GET /api/v1/sqlite/kv/{key}

Retrieve a value from the KV store with optional JSON path extraction and time-travel support.

NameInTypeRequiredDescription
keypathstringYesKey name (supports / for hierarchical keys)
dbquerystringYesDatabase file path or directory
tablequerystringNoCustom table name (default: kv_store)
pathquerystringNoJSON path for nested value extraction. The extracted value is returned as JSON with an X-JSON-Path header; a path that does not exist in the value answers 404. Ignored with at_timestamp.
at_timestampqueryintegerNoUnix timestamp. Returns the value the key held at that moment in a JSON envelope, instead of the current value. 0 or omitted reads the current value; a time in the future is rejected with 400. A directory (directory mode) is rejected with 400 in this mode. A write made with history=false that leaves the value at that moment unknown answers 409 TIME_TRAVEL_CHAIN_GAP.
rebuildquerybooleanNoDirectory mode only: clear the directory’s cached file information (this directory only, not its subdirectories), then read the key. Ignored with at_timestamp.
If-None-MatchheaderstringNoAnswers 304 while the key’s current ETag matches.
timeoutqueryintegerNoServer-side deadline in seconds, clamped to [1, 300]. Defaults to 30.

The body is the stored value itself, returned with its stored Content-Type. The Content-Type is application/json when the value was stored as JSON, or application/octet-stream when it was stored as raw bytes. With a non-zero at_timestamp, the body is always a JSON envelope carrying the value as it was at that time.

{
"user": "alice",
"score": 42,
"active": true
}
HeaderDescription
ETagThe key’s ETag; it changes on every write of the key, TTL changes included. Not sent with at_timestamp, in directory mode, or when the server could not verify the key’s version.
X-Created-AtUnix timestamp when created; in directory mode, the file modification time. Not sent with at_timestamp.
X-Updated-AtUnix timestamp when last updated; in directory mode, the file modification time. Not sent with at_timestamp.
X-Expire-AtUnix timestamp when the key expires; sent only when a TTL is set. Not sent with at_timestamp.
X-JSON-PathThe path that was extracted, sent when path is set.
X-Directory-ModeSet to true when db is a directory (directory mode).
X-File-HashSHA-256 of the value file, hex-encoded. Directory mode only, and not sent when path is set.
X-KV-Nesting-DepthNumber of /-separated segments in the key. Directory mode only.
X-KV-ReferenceSet to true if value is a KV store reference.
X-Reference-PathThe reference target, sent with X-KV-Reference.
Terminal window
curl -X GET "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/users/alice?db=app.db" \
-H "Authorization: Bearer $HOODY_TOKEN"

PUT /api/v1/sqlite/kv/{key}

Store or update a value in the KV store with optional TTL, JSON path, and compare-and-swap.

NameInTypeRequiredDescription
keypathstringYesKey name
dbquerystringYesDatabase file path or directory
tablequerystringNoCustom table name (default: kv_store)
pathquerystringNoJSON path for nested value update
ttlqueryintegerNoTime-to-live in seconds. SQLite mode only; a non-zero ttl against a directory-mode store is rejected with 400.
if_matchquerystringNoCurrent value for compare-and-swap. SQLite mode only; rejected with 400 against a directory-mode store.
historyquerybooleanNoStore this write’s value in history; false records only that the write happened, not what it wrote. (default: true)
create_db_if_missingquerybooleanNoCreate database file if it is missing. The legacy alias auto_create is still accepted and means the same thing. (default: false)
If-MatchheaderstringNoApplies the write only if the key’s current ETag matches.
If-None-MatchheaderstringNoSet to * to write only if the key does not exist yet.
Idempotency-KeyheaderstringNoReplays the stored response for a repeat of the same key (24 h).
timeoutqueryintegerNoServer-side deadline in seconds, clamped to [1, 300]. Defaults to 30.

The body is the value to store. With Content-Type: application/json the body must be one valid JSON document: an object, array, number, boolean, null, or a quoted string. To store raw text or bytes, send Content-Type: text/plain or application/octet-stream; those are stored verbatim. The Content-Type is recorded with the value and returned by a subsequent GET.

{
"name": "alice",
"role": "admin",
"score": 42
}
{
"key": "users/alice",
"size": 47,
"success": true,
"etag": "\"a1b2c3d4e5f6\""
}
FieldTypeRequiredDescription
keystringYesThe key written.
sizeintegerYesSize of the stored value in bytes. With path, the size of the whole updated value.
successbooleanYesAlways true on a 200.
etagstringNoThe key’s ETag after the write (also sent as the ETag header). SQLite mode only.
directory_modebooleanNoAlways true when present. Directory mode only.
file_pathstringNoAbsolute path of the file that holds the value. Directory mode only.
pathstringNoThe path that was set. Present only when path was sent.
Terminal window
curl -X PUT "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/users/alice?db=app.db" \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"alice","role":"admin","score":42}'

DELETE /api/v1/sqlite/kv/{key}

Remove a key-value pair from the store.

NameInTypeRequiredDescription
keypathstringYesKey name
dbquerystringYesDatabase file path or directory
tablequerystringNoCustom table name (default: kv_store)
historyquerybooleanNoStore this write’s value in history; false records only that the write happened, not what it wrote. (default: true)
create_db_if_missingquerybooleanNoCreate database file if it is missing. The legacy alias auto_create is still accepted and means the same thing. (default: false)
timeoutqueryintegerNoServer-side deadline in seconds, clamped to [1, 300]. Defaults to 30.
If-MatchheaderstringNoApplies the write only if the key’s current ETag matches.
Idempotency-KeyheaderstringNoReplays the stored response for a repeat of the same key (24 h).
{
"success": true,
"deleted": true
}
FieldTypeRequiredDescription
successbooleanYesAlways true on a 200.
deletedbooleanYesAlways true on a 200 (a missing key is a 404).
directory_modebooleanNoAlways true when present. Directory mode only.
Terminal window
curl -X DELETE "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/users/alice?db=app.db" \
-H "Authorization: Bearer $HOODY_TOKEN"

HEAD /api/v1/sqlite/kv/{key}

Check whether a key exists in the KV store without retrieving its value. The method returns only the status code and a small set of headers, so it is cheaper than a full GET for existence checks.

NameInTypeRequiredDescription
keypathstringYesKey name
dbquerystringYesDatabase file path or directory
tablequerystringNoCustom table name (default: kv_store)
If-None-MatchheaderstringNoAnswers 304 while the key’s current ETag matches.
timeoutqueryintegerNoServer-side deadline in seconds, clamped to [1, 300]. Defaults to 30.

The key exists.

Terminal window
curl -I "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/users/alice?db=app.db" \
-H "Authorization: Bearer $HOODY_TOKEN"

POST /api/v1/sqlite/kv/{key}/incr

Atomically increment a numeric value. Supports JSON paths for nested values.

NameInTypeRequiredDescription
keypathstringYesKey name
dbquerystringYesDatabase file path
tablequerystringNoCustom table name (default: kv_store)
deltaqueryintegerNoAmount to increment (default: 1)
pathquerystringNoJSON path to nested numeric value
historyquerybooleanNoStore this write’s value in history; false records only that the write happened, not what it wrote. (default: true)
create_db_if_missingquerybooleanNoCreate database file if it is missing. The legacy alias auto_create is still accepted and means the same thing. (default: false)
timeoutqueryintegerNoServer-side deadline in seconds, clamped to [1, 300]. Defaults to 30.
If-MatchheaderstringNoApplies the write only if the key’s current ETag matches.
Idempotency-KeyheaderstringNoReplays the stored response for a repeat of the same key (24 h).
{
"key": "visits/today",
"value": 6,
"etag": "\"a1b2c3d4e5f6\"",
"success": true
}
FieldTypeRequiredDescription
keystringYesThe key changed.
valueintegerYesThe value after the change; with path, the nested value.
etagstringYesThe key’s ETag after the write (also sent as the ETag header).
successbooleanYesAlways true on a 200.
pathstringNoThe path that was changed. Present only when path was sent.
Terminal window
curl -X POST "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/visits/today/incr?db=app.db&delta=1" \
-H "Authorization: Bearer $HOODY_TOKEN"

POST /api/v1/sqlite/kv/{key}/decr

Atomically decrement a numeric value. Supports JSON paths for nested values.

NameInTypeRequiredDescription
keypathstringYesKey name
dbquerystringYesDatabase file path
tablequerystringNoCustom table name (default: kv_store)
deltaqueryintegerNoAmount to decrement (default: 1)
pathquerystringNoJSON path to nested numeric value
historyquerybooleanNoStore this write’s value in history; false records only that the write happened, not what it wrote. (default: true)
create_db_if_missingquerybooleanNoCreate database file if it is missing. The legacy alias auto_create is still accepted and means the same thing. (default: false)
timeoutqueryintegerNoServer-side deadline in seconds, clamped to [1, 300]. Defaults to 30.
If-MatchheaderstringNoApplies the write only if the key’s current ETag matches.
Idempotency-KeyheaderstringNoReplays the stored response for a repeat of the same key (24 h).
{
"key": "seats/remaining",
"value": 4,
"etag": "\"a1b2c3d4e5f6\"",
"success": true
}
FieldTypeRequiredDescription
keystringYesThe key changed.
valueintegerYesThe value after the change; with path, the nested value.
etagstringYesThe key’s ETag after the write (also sent as the ETag header).
successbooleanYesAlways true on a 200.
pathstringNoThe path that was changed. Present only when path was sent.
Terminal window
curl -X POST "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/seats/remaining/decr?db=app.db&delta=1" \
-H "Authorization: Bearer $HOODY_TOKEN"

GET /api/v1/sqlite/kv/{key}/entry

Read a key’s value together with its metadata and ETag in one JSON body: the value (parsed when its content type is JSON, otherwise base64), content type, ETag, and created/updated/expiry times. Send If-None-Match with a previously received ETag to get 304 Not Modified while the key is unchanged (weak comparison; any write, TTL change included, changes the ETag).

NameInTypeRequiredDescription
keypathstringYesKey name (supports / for hierarchical keys)
dbquerystringYesDatabase file path
tablequerystringNoCustom table name (default: kv_store)
If-None-MatchheaderstringNoAnswers 304 while the key’s current ETag matches.
timeoutqueryintegerNoServer-side deadline in seconds, clamped to [1, 300]. Defaults to 30.
{
"key": "users/alice",
"content_type": "application/json",
"etag": "\"a1b2c3d4e5f6\"",
"created_at": 1700000000,
"updated_at": 1700000060,
"expire_at": null,
"value": {
"name": "alice",
"role": "admin",
"score": 42
}
}
FieldTypeRequiredDescription
keystringYesThe key.
content_typestringYesThe content type stored with the value.
etagstringYesThe key’s ETag (also sent as the ETag header). null when the server could not verify the key’s version (database busy); retry for a tag.
created_atintegerYesWhen the key was created, Unix seconds.
updated_atintegerYesWhen the key was last written, Unix seconds.
expire_atintegerYesWhen the key expires, Unix seconds; null when it has no TTL.
valueany JSON valueNoThe value, parsed, when its content type is JSON and it parses; null for a stored JSON null. Absent otherwise.
value_base64stringNoThe value as base64, when its content type is not JSON or it does not parse as JSON; an empty string for an empty value. Absent otherwise.
Terminal window
curl -X GET "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/users/alice/entry?db=app.db" \
-H "Authorization: Bearer $HOODY_TOKEN"

POST /api/v1/sqlite/kv/{key}/expire

Make an existing key expire ttl seconds from now, replacing any TTL it had. The value, its content type, and updated_at are not changed; the ETag is. Setting the same expiry again is still a write (new ETag, a set event in the change feed). History records the change (rollback and time travel restore the TTL).

NameInTypeRequiredDescription
keypathstringYesKey name (supports / for hierarchical keys)
dbquerystringYesDatabase file path
tablequerystringNoCustom table name (default: kv_store)
ttlqueryintegerYesSeconds from now until the key expires; at least 1.
historyquerybooleanNoStore this write’s value in history; false records only that the write happened, not what it wrote. (default: true)
If-MatchheaderstringNoApplies the write only if the key’s current ETag matches.
Idempotency-KeyheaderstringNoReplays the stored response for a repeat of the same key (24 h).
create_db_if_missingquerybooleanNoCreate database file if it is missing. The legacy alias auto_create is still accepted and means the same thing. (default: false)
timeoutqueryintegerNoServer-side deadline in seconds, clamped to [1, 300]. Defaults to 30.
{
"key": "users/alice",
"etag": "\"a1b2c3d4e5f6\"",
"expire_at": 1700003600,
"success": true
}
FieldTypeRequiredDescription
keystringYesThe key.
etagstringYesThe key’s ETag after the change (also sent as the ETag header).
expire_atintegerYesWhen the key now expires, Unix seconds; null when it has no TTL.
successbooleanYesAlways true on a 200.
HeaderDescription
ETagThe key’s new ETag.
Idempotent-Replayedtrue when this is the stored response of an earlier request with the same Idempotency-Key.
Terminal window
curl -X POST "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/users/alice/expire?db=app.db&ttl=3600" \
-H "Authorization: Bearer $HOODY_TOKEN"

POST /api/v1/sqlite/kv/{key}/persist

Make an existing key permanent: remove its expiry. The value, its content type, and updated_at are not changed; the ETag is. A key without a TTL is answered 200 with nothing changed (same ETag, no history entry, no change-feed event). History records the change (rollback and time travel restore the TTL).

NameInTypeRequiredDescription
keypathstringYesKey name (supports / for hierarchical keys)
dbquerystringYesDatabase file path
tablequerystringNoCustom table name (default: kv_store)
historyquerybooleanNoStore this write’s value in history; false records only that the write happened, not what it wrote. (default: true)
If-MatchheaderstringNoApplies the write only if the key’s current ETag matches.
Idempotency-KeyheaderstringNoReplays the stored response for a repeat of the same key (24 h).
create_db_if_missingquerybooleanNoCreate database file if it is missing. The legacy alias auto_create is still accepted and means the same thing. (default: false)
timeoutqueryintegerNoServer-side deadline in seconds, clamped to [1, 300]. Defaults to 30.
{
"key": "users/alice",
"etag": "\"a1b2c3d4e5f6\"",
"expire_at": null,
"success": true
}
FieldTypeRequiredDescription
keystringYesThe key.
etagstringYesThe key’s ETag after the change (also sent as the ETag header).
expire_atintegerYesWhen the key now expires, Unix seconds; null when it has no TTL.
successbooleanYesAlways true on a 200.
HeaderDescription
ETagThe key’s ETag.
Idempotent-Replayedtrue when this is the stored response of an earlier request with the same Idempotency-Key.
Terminal window
curl -X POST "https://{projectId}-{containerId}-sqlite-1.{server}.containers.hoody.com/api/v1/sqlite/kv/users/alice/persist?db=app.db" \
-H "Authorization: Bearer $HOODY_TOKEN"