API conventions
Cirrus uses consistent names and response envelopes so that response cardinality and operation-specific data are clear to clients.
Download OpenAPI specificationResponse envelopes
Resource fields describe the cardinality of a successful response. Operation-specific output is kept separate from resource data.
| Field | Meaning |
|---|---|
| item | One resource. |
| items | A collection of zero or more resources. |
| result | Operation-specific data that is not a resource, such as an upload session. |
| context | Request and authentication context returned by public routes. |
| nextToken | An opaque continuation token on paginated collections. |
Collection
{
"items": [
{
"id": "pub_123"
}
],
"nextToken": null,
"context": {
"path": "/v1/publications",
"method": "GET",
"requestId": "req_123",
"authentication": {
"sub": "user_123",
"scopeId": "scope_123"
}
}
}Single resource
{
"item": {
"id": "pub_123"
},
"context": {
"path": "/v1/publications/pub_123",
"method": "GET",
"requestId": "req_123",
"authentication": {
"sub": "user_123",
"scopeId": "scope_123"
}
}
}Resource and result
{
"item": {
"id": "pub_123"
},
"result": {
"objectKey": "publications/pub_123/book.epub",
"uploadUrl": "https://example.com/upload"
},
"context": {
"path": "/v1/publications",
"method": "POST",
"requestId": "req_123",
"authentication": {
"sub": "user_123",
"scopeId": "scope_123"
}
}
} Some lookup routes allow no configured resource and return item: null. A missing resource on an ordinary ID lookup returns a 404. Check each endpoint schema for its exact nullability.
Names and values
JSON fields
Field names use camelCase. Boolean fields should read as predicates, such as isDefault or hasSecret.
Paths
Paths use lowercase plural resource names. Multi-word path segments use kebab-case, for example /v1/reader-instances.
Identifiers
Treat identifiers and continuation tokens as opaque strings. Do not derive meaning from their format or construct them in a client.
Dates and times
ISO timestamp fields use an Iso suffix, such as createdAtIso. Parse values as timestamps instead of displaying the raw value.
Field types
JSON types are significant and clients should not coerce fields based on their names or contents. In particular:
| Field | JSON type | Notes |
|---|---|---|
| Edition.version | number | Numeric edition version. |
| Edition.metadata.fileSize | string | Decimal byte count encoded as a string. |
| Edition.metadata.pageCount | string | Decimal page count encoded as a string. |
| ReaderInstance.configuration | object | Nested reader configuration containing layout regions, features, settings, fonts, and related state. |
| ReaderInstance.designerState | object | Nested state used to restore the reader designer. |
Pagination
The endpoints below use cursor pagination. Send the response's nextToken unchanged as the nextToken query parameter in the next request. A null value means there are no more pages. Clients must not inspect, modify, or persist a token as a permanent cursor.
| Endpoint | Parameters | Default page size |
|---|---|---|
| GET /v1/publications | limit, nextToken | 10 |
| GET /v1/reader-instances | limit, nextToken | 10 |
The limit parameter accepts an integer from 1 to 50. Other list endpoints are not currently paginated and return their complete result set.
GET /v1/publications?limit=10&nextToken=TOKEN_FROM_PREVIOUS_RESPONSEUploads and processing
Publication and asset creation return short-lived presigned S3 upload sessions. Upload each file with an HTTP PUT to uploadUrl, copying every entry in requiredHeaders exactly. The URL expires after the number of seconds in expiresIn.
Publications
POST /v1/publications and edition creation return the publication in item and one upload session in result. A separate optional result.cover session is returned when coverFilename is supplied. Publication uploads enter a quarantine prefix and are scanned before becoming available.
Assets
POST /v1/resources/assets returns one descriptor in result[] for each requested asset. Each descriptor contains its asset id, object key, upload URL, expiry, and required headers.
await fetch(uploadSession.uploadUrl, {
method: 'PUT',
headers: uploadSession.requiredHeaders,
body: file
})Publication upload statuses
| Status | Meaning | Terminal |
|---|---|---|
| upload_pending | Waiting for upload, scanning, or processing. | No |
| uploaded | Upload received; downstream processing may still be running. | No |
| available | Scanning and processing completed successfully. | Yes |
| upload_quarantine | The upload was quarantined after a failed security scan. | Yes |
| upload_failed | Scanning or upload processing failed. | Yes |
Poll the publication or edition resource until uploadStatus reaches a terminal value. The create request must include the correct edition.type: EPUB, PDF, or Audio. LPF audiobook packages use Audio.
Errors
Error responses use error for a machine-readable code and message for a human-readable explanation. The request ID is context.requestId; it is not a top-level field. Use the HTTP status and error code for client control flow, and include the request ID when reporting an error. Do not match message text.
| Field | Type | Description |
|---|---|---|
| error | string | Machine-readable error code. |
| message | string | Human-readable explanation. |
| context.path | string | Path that handled the request. |
| context.method | string | HTTP request method. |
| context.requestId | string | Request identifier used for tracing and support. |
| context.authentication | object | Public authentication context when authentication completed. |
| details | array | Field-level validation or operation details when available. |
| details[].message | string | Description of the issue. |
| details[].path | (string | number)[] | Path to the invalid field. Numbers identify array indexes. |
| details[].code | string | Machine-readable validation issue code. |
details is present on validation error responses, including ZOD_ERROR. The path and code fields on an individual detail are optional. Errors raised before authentication completes can return a context without authentication.
{
"error": "ZOD_ERROR",
"message": "Invalid payload sent to API",
"context": {
"path": "/v1/publications",
"method": "POST",
"requestId": "req_123",
"authentication": {
"sub": "user_123",
"scopeId": "scope_123"
}
},
"details": [
{
"message": "Expected string, received number",
"path": [
"edition",
"title"
],
"code": "invalid_type"
},
{
"message": "Expected string, received null",
"path": [
"collectionIds",
0
],
"code": "invalid_type"
}
]
}