Items API
The Items API is the primary interface for reading and writing data in any collection. It supports full CRUD with filtering, sorting, pagination, field selection, aggregation, and deep relational reads.
GET /api/items/:collection
POST /api/items/:collection
GET /api/items/:collection/:id
PATCH /api/items/:collection/:id
PUT /api/items/:collection/:id
DELETE /api/items/:collection/:id
PATCH /api/items/:collection (batch update)
DELETE /api/items/:collection (batch delete)Replace :collection with the name of any table in your database (e.g., posts, products). Permissions are enforced per collection — a request will 403 if the user’s role doesn’t have the required permission.
List Items
GET /api/items/:collectionQuery Parameters
| Parameter | Description | Example |
|---|---|---|
fields | Comma-separated field names to return | ?fields=id,title,author |
filter | Filter rules (see Filter Rules) | ?filter[status][_eq]=published |
sort | Field to sort by. Prefix with - for descending | ?sort=-created_at |
limit | Number of items to return (default 25, always applied) | ?limit=20 |
page | Page number for pagination (default 1) | ?page=2 |
search | Substring search across string fields (ILIKE) | ?search=hello |
aggregate | Aggregate functions | ?aggregate[count]=* |
groupBy | Group results by one or more fields (comma-separated). Requires aggregate | ?groupBy=status&aggregate[count]=* |
Example
curl
curl "https://your-domain.com/api/items/posts?fields=id,title,status&filter[status][_eq]=published&sort=-created_at&limit=10" \
-H "Authorization: Bearer <token>"Response:
{
"data": [
{ "id": 1, "title": "Hello World", "status": "published" },
{ "id": 2, "title": "Second Post", "status": "published" }
],
"meta": {
"total_count": 42,
"total": 42,
"page": 1,
"limit": 25
}
}Get Single Item
GET /api/items/:collection/:idcurl "https://your-domain.com/api/items/posts/1?fields=*,author.*" \
-H "Authorization: Bearer <token>"The fields=*,author.* pattern includes all top-level fields plus all fields from the related author record (deep read).
Pass ?version=<key> to retrieve the item with a specific content version’s delta merged in (e.g., ?version=draft).
Create Item
POST /api/items/:collectioncurl -X POST "https://your-domain.com/api/items/posts" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"title": "New Post", "status": "draft", "body": "Hello!"}'Returns the created item with its generated id and status 201 Created.
Upsert Item
PUT /api/items/:collection/:idCreates the item if it doesn’t exist, or replaces it entirely if it does. Unlike PATCH (partial update), PUT requires all writable fields in the request body — any omitted fields will be set to null.
curl -X PUT "https://your-domain.com/api/items/posts/1" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"title": "Updated Title", "status": "published", "body": "Full content"}'Update Item (Partial)
PATCH /api/items/:collection/:idOnly the fields included in the request body are updated (partial update).
curl -X PATCH "https://your-domain.com/api/items/posts/1" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"status": "published"}'Delete Item
DELETE /api/items/:collection/:idReturns 200 with { data: { deleted: true, id } } on success.
Batch Operations
The filter parameter is required for batch update and batch delete. Omitting it returns 400 with { "error": "Filter parameter required for batch update" }.
Batch update
Update multiple items matching a filter. The filter query parameter must be JSON-encoded:
curl -X PATCH "https://your-domain.com/api/items/posts?filter=%7B%22status%22%3A%7B%22_eq%22%3A%22draft%22%7D%7D" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"status": "archived"}'Or pass the filter as a JSON string in the URL:
# URL-decoded for readability: ?filter={"status":{"_eq":"draft"}}
curl -X PATCH "https://your-domain.com/api/items/posts" \
-G --data-urlencode 'filter={"status":{"_eq":"draft"}}' \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"status": "archived"}'Batch delete
Delete multiple items matching a filter. The filter query parameter must be JSON-encoded:
curl -X DELETE "https://your-domain.com/api/items/posts" \
-G --data-urlencode 'filter={"status":{"_eq":"archived"}}' \
-H "Authorization: Bearer <token>"Batch update response (200):
{
"data": {
"updated": 3,
"keys": [1, 5, 12]
}
}Batch delete response (200):
{
"data": {
"deleted": 2,
"keys": [8, 15]
}
}The MAX_BATCH_MUTATION environment variable controls the maximum number of items that can be mutated in a single batch request (default: 100).
Aggregates
GET /api/items/posts?aggregate[count]=*&aggregate[avg]=views&groupBy=status{
"data": [
{ "count": { "*": 15 }, "avg": { "views": 243.6 }, "status": "published" },
{ "count": { "*": 8 }, "avg": { "views": 12.1 }, "status": "draft" }
]
}Supported functions: count, countDistinct, countAll, sum, sumDistinct, avg, avgDistinct, min, max.
count/countDistinct: Counts non-null values of a specific field (e.g.,?aggregate[count]=id).countAll: Counts all rows regardless of field values (e.g.,?aggregate[countAll]=*). Use*as the field argument.
Relational Data
Fetch related items inline using dot notation in fields:
?fields=id,title,author.name,author.email,tags.tag_id.nameauthor.name— M2O: the author’s nametags.tag_id.name— M2M junction: the tag name through the junction table
Use the * wildcard to include all fields from a related collection:
?fields=*,author.*This returns all top-level fields plus all fields from the author relation.
Error Responses
Errors return a JSON body with at least an error key, and some errors include a code field for programmatic handling:
{ "error": "Not authenticated" }
{ "error": "Item not found", "code": "NOT_FOUND" }Common HTTP status codes:
| Code | Meaning |
|---|---|
400 | Bad request (invalid parameters, missing required filter) |
401 | Not authenticated |
403 | Forbidden (insufficient permissions) |
404 | Item or collection not found |
500 | Internal server error |
Scope & Multi-tenancy
Every request is scoped to a resource URI via the X-Resource-URI header or a cookie. This header controls which tenant/project the request operates within. Omitted or invalid resource URIs result in authorization errors.
Delegation
Service accounts can act on behalf of other users by setting the X-On-Behalf-Of header to the target user ID. This is useful for automation and background jobs.
Authentication
Requests can be authenticated using either:
- Bearer token:
Authorization: Bearer <token>header - Cookie-based auth: Browser clients with an active session cookie
Field-Level Permissions
Non-admin users receive filtered response objects — fields they don’t have permission to read are stripped from the response. Admins see all fields.
PATCH vs PUT
| Method | Behavior |
|---|---|
PATCH | Partial update — only the fields in the request body are changed |
PUT | Upsert — creates the item if it doesn’t exist, or fully replaces it. Omitted fields are set to null |