Loading article…

DocsLoading article…
The branch base URL is:
/v1/projects/{projectId}/branches/{branchId}Use the primary alias or an explicit branch ID. Include X-QB-Key with a publishable key. Include an application bearer token when authenticated.
{"columns":["id","title"],"filters":[{"column":"completed","op":"eq","value":0}],"order":[{"column":"id","direction":"asc"}],"limit":50,"count":true}POST this body to /data/tasks/query. The default page is 50 rows; maximum is 500. Filters never accept raw SQL. Counts include only authorized rows.
Follow the response's meta.cursor with the same projection, filters, ordering, identity and schema version. Cursors are signed and bind that query scope. Changing scope rejects the cursor. Pagination does not provide a database snapshot spanning multiple requests.
Group and summarize authorized rows with groupBy, aggregates and having. Aggregates are a deliberately degraded query mode: cursor pagination and relations are disabled, and meta.count returns the number of groups rather than rows.
{
"groupBy": ["owner_id"],
"aggregates": [
{ "fn": "count", "alias": "task_count" },
{ "fn": "sum", "column": "completed", "alias": "completed_sum" }
],
"having": [{ "alias": "task_count", "op": "gt", "value": 2 }],
"limit": 50
}Supported aggregate functions are count, countDistinct, sum, avg, min and max. sum and avg require a numeric column. Aliases must be unique and identifier-safe. Up to five group-by columns, eight aggregates and ten having clauses are allowed; the page limit is 1–500 rows (groups).
await client
.from('tasks')
.groupBy(['owner_id'])
.aggregate('sum', 'completed', 'completed_sum')
.having('completed_sum', 'gte', 1)
.execute();Policy filters are applied before grouping, so a row invisible to the caller never contributes to a sum or count.
POST inserts one row or an atomic batch of up to 100 rows. PATCH updates a row by key; DELETE removes it. Supply Idempotency-Key to safely reconcile a retried mutation. Reusing the same key with different content returns a conflict.
Tables with a server-managed revision column support conditional PATCH and DELETE through If-Match. The server increments revisions on updates and rejects stale values with CONFLICT (HTTP 409). Existing calls without a revision remain compatible; do not treat a stale write as an automatic retry.
await client.from('tasks').update(task.id, { title }, { revision: task.revision });
await client.from('tasks').delete(task.id, { revision: task.revision });These are alternative mutations against a freshly read row, not a sequence using the same revision: after an update, refetch the current revision before deleting.
Success returns data and meta, including a request ID and relevant schema metadata. Errors provide a stable code, human-readable message and retryability. SDK methods throw QBError on failed responses.
An invisible row is returned as unavailable without revealing whether it exists. Public responses do not contain SQL, filesystem paths or credentials.