Conventions¶
Behaviors shared by every endpoint in the Public API v2.
Identifiers¶
All resources are identified by a public_id — a stable, opaque string. Internal numeric IDs are never exposed. When creating relationships between resources (e.g., assigning content to a user), reference them by their public_id.
public_id values are UUIDs in canonical form. Treat them as opaque strings — do not parse or generate them yourself.
Pagination¶
List endpoints return a paginated envelope:
{
"data": [ /* up to page_size items */ ],
"meta": {
"page": 1,
"page_size": 25,
"total_count": 137,
"total_pages": 6
}
}
Control pagination with these query parameters:
| Parameter | Default | Max | Description |
|---|---|---|---|
page |
1 |
— | 1-indexed page number. |
page_size |
25 |
100 |
Items per page. |
Sorting¶
List endpoints support ordering with a comma-separated list of fields. Prefix with - for descending order:
The supported fields per endpoint are listed in the API reference.
Filtering¶
Most list endpoints accept resource-specific filters as query parameters — for example GET /users?email=jane.doe@acme.com or GET /content-items?content_type=video&is_archived=false. The supported filters are listed in the API reference for each endpoint.
Errors¶
All errors follow a consistent envelope:
{
"error": {
"code": "not_found",
"message": "Content item not found.",
"request_id": "req_a1b2c3d4"
}
}
Validation errors include field-level details:
{
"error": {
"code": "validation_error",
"message": "Validation failed.",
"request_id": "req_a1b2c3d4",
"field_errors": {
"email": ["A user with this email already exists."]
},
"non_field_errors": ["Cannot assign archived content."]
}
}
Error codes¶
| HTTP status | code |
Meaning |
|---|---|---|
| 400 | bad_request |
Malformed request. |
| 401 | unauthenticated / invalid_token |
Missing or invalid token. |
| 403 | forbidden |
Authenticated, but the operation is not permitted. |
| 404 | not_found |
Resource does not exist or is not visible to this token. |
| 409 | conflict / duplicate |
The operation conflicts with the current state. |
| 412 | precondition_failed |
If-Match ETag did not match the current resource. |
| 422 | validation_error |
Request body failed validation. |
| 429 | rate_limited |
Too many requests; see Rate limits. |
| 500 | internal_error |
Unexpected server error. Quote the request_id to support. |
Always log request_id with every failure — it's the fastest path to a resolution.
Rate limits¶
All endpoints are rate-limited. Specific per-tier limits are not yet published; the service returns sensible defaults during the preview.
Every response includes rate-limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Your rate limit (requests per window). |
X-RateLimit-Remaining |
Requests remaining in the current window. |
X-RateLimit-Reset |
Seconds until the limit resets. |
When a request is rate-limited, the response is HTTP 429 with the standard error envelope and a Retry-After header.
ETags and concurrency¶
Resources that support optimistic concurrency return an ETag header on GET and require If-Match on PATCH. A mismatched ETag returns HTTP 412 precondition_failed — re-fetch the resource and retry.
Bulk endpoints¶
Bulk endpoints (e.g. POST /content-items/bulk/archive) operate on up to 100 resources per request. Per-item failures are isolated — a partial batch returns HTTP 207 Multi-Status with a per-item result list.