Database records
Read, query, create, and update records in a Snoze database.
Records are the rows of a database item. Every value is typed by the database's field schema, and values are keyed by the field's key — a stable slug that survives renaming the field's label, so display-name changes never break your integration. Fetch a database item to see its schema: each field carries id, key, label, kind, required, and (for selects) options.
The record object
{
"id": "1d8a44e0-…",
"workspaceId": "4b81aa0c-…",
"databaseId": "9f2c1e7a-…",
"title": "Ship the desktop app",
"data": {
"status": "in_progress",
"due": "2026-08-01",
"owner": "7a1b04c2-…"
},
"relations": {},
"computed": {},
"system": {},
"archived": false,
"createdAt": "2026-07-10T08:00:00.000Z",
"updatedAt": "2026-07-14T11:26:02.000Z"
}Value shapes by field kind:
| Kind | Value |
|---|---|
text, long_text, email, phone | string |
url | URL string, or { "url": "…", "label": "…" } for a labelled link |
number | number |
checkbox | boolean |
date | ISO date string, or { "start": "…", "end": "…" } for a range |
select, status | option key (string) |
multi_select | array of option keys |
user | user UUID — or an array when the field allows multiple |
file | file UUID (from uploads) — or an array |
relation | related record UUID — or an array for to-many relations |
formula, rollup and other computed kinds | read-only, surfaced under computed |
A url value only takes the object form when the link has a label: a link without one is always stored and returned as a plain string, so a client that only ever sends and reads strings keeps working. Labels are trimmed and may be at most 200 characters; an empty or null label collapses the value back to the bare URL string. Links with a javascript:, data:, vbscript:, blob:, or file: scheme are rejected, as is an object with a blank url, a non-string label, or any key other than url and label.
List and query records
GET /v1/databases/{databaseId}/records pages with a cursor and can search or apply a saved view:
curl "https://api.snoze.dev/v1/databases/9f2c1e7a-…/records?limit=100" \
-H "Authorization: Bearer $SNOZE_API_KEY"{
"data": {
"records": [{ "id": "1d8a44e0-…", "…": "…" }],
"cursor": "k2|100|1d8a44e0-…",
"hasMore": true
}
}cursor— pass it back as?cursor=for the next page;nullmeans the end.search— full-text match across the database (returns a single unpaginated batch).viewId— apply a saved view's filters and sorts. Views are where structured querying lives: build the filter once in the app (or via the views endpoints), then read through it here.
Create a record
Send only the fields you're setting — everything else takes the database's defaults:
curl -X POST "https://api.snoze.dev/v1/databases/9f2c1e7a-…/records" \
-H "Authorization: Bearer $SNOZE_API_KEY" \
-H "Idempotency-Key: 4c9a1c1e-launch-task" \
-H "Content-Type: application/json" \
-d '{
"title": "Write release notes",
"data": { "status": "todo" }
}'Update a record
PATCH /v1/records/{recordId} addresses a record directly — no databaseId needed — and takes the same partial data map. Fields you omit are untouched; set a field to null to clear it (required fields fall back to their default or fail validation):
{
"data": {
"status": "done",
"due": null
}
}GET /v1/records/{recordId} and DELETE /v1/records/{recordId} complete the flat surface.
Validation runs against the field schema: an unknown key or a wrong shape for
a field kind fails the whole request with a bad_request error naming the
offending field — see Errors.