API endpoints and fields
Use with your agent
Read https://thirds.ai/docs/api-reference and help me choose the right thirds.ai endpoint and write a request for my app. Check the required fields and response format, and use placeholders for credentials.
Look up the endpoints, request fields, and responses you need for your integration. If you are setting up your first request, start with API keys and requests or create your first PDF. You can also download the OpenAPI file to use with your own tools.
Endpoints
previewImageUrl
POST /v1/image-assets/preview
Preview a URL image
Read a public HTTP or HTTPS PNG, JPEG, or WebP through the checked asset transport. Credentials, private destinations, unsafe DNS answers, and redirects to them are refused. At most five redirects and 15 seconds total. The same upload decoder checks the 10 MiB, dimension, pixel, and memory limits. Returns verified bytes without storage or a render charge. Browser sessions require CSRF.
Authentication: bearer API key. See authentication.
Request body: application/json, {"type":"object","properties":{"url":{"type":"string","maxLength":4096}},"required":["url"],"additionalProperties":false}.
| Status | Meaning | Body |
|---|---|---|
| 200 | The verified image bytes and dimensions after orientation. | image/png: {"type":"string","format":"binary"}; image/jpeg: {"type":"string","format":"binary"}; image/webp: {"type":"string","format":"binary"} |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 422 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 429 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 503 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
uploadImage
POST /v1/image-assets
Upload an image
Store one private, immutable PNG, JPEG, or WebP image. Send the raw bytes with the matching Content-Type. Maximum 10 MiB, 7680 by 4320 stored pixels, 32 million pixels, and 64 MiB decoded memory. Animated images are refused. Images and brand assets share an account limit of 100 files and 100 MiB. Use the returned reference in an HTML body img src, or in template data for an img src. Render creation checks ownership after template expansion and captures the exact bytes. References are not public URLs. PDF header and footer templates do not support image references. No render credit is charged for an upload.
Authentication: bearer API key. See authentication.
Request body: image/png, {"type":"string","format":"binary"}.
Request body: image/jpeg, {"type":"string","format":"binary"}.
Request body: image/webp, {"type":"string","format":"binary"}.
| Status | Meaning | Body |
|---|---|---|
| 201 | The image is stored. | application/json: UploadedImage |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 422 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 429 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 503 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
readImageContent
GET /v1/image-assets/{id}/content
Read an owned image
Return verified image or captured brand logo/font bytes to their owner. Missing and foreign assets return the same 404 error. The response uses private, no-store caching and nosniff.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
id | path | Yes | {"type":"string","pattern":"^asset_[a-f0-9]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The exact image bytes. | font/woff2: {"type":"string","format":"binary"}; image/png: {"type":"string","format":"binary"}; image/jpeg: {"type":"string","format":"binary"}; image/webp: {"type":"string","format":"binary"} |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 422 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 429 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 503 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
getOpenApiDocument
GET /v1/openapi.json
Get the OpenAPI document
Return this API contract as a JSON document.
Authentication: no API key required.
| Status | Meaning | Body |
|---|---|---|
| 200 | The OpenAPI document. | application/json: {"type":"object"} |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
createPdf
POST /v1/pdf
Create a PDF
Render one PDF from raw HTML, an inline stateless template, or one owned saved-template version. An omitted saved version resolves to one exact immutable version before data validation, admission, idempotency, queueing, or billing. Template evaluation happens before storage and only evaluated HTML enters render input. Returns 200 for a terminal job or 202 while the accepted job remains queued or running. A terminal job can be succeeded, failed, or cancelled; check status before downloading. Send wait=false to skip the bounded wait. Every mode uses the same queue, renderer, retention, webhook, and fixed one-credit success price.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
Idempotency-Key | header | No | 1 to 255 printable ASCII characters. A repeated key with the same logical request replays one job; a different logical request conflicts. Stateless fingerprints include original source, sorted data, and effective render options. Saved-template fingerprints include the exact resolved template version, sorted data, and effective options. wait, request IDs, and transport choices do not affect the fingerprint. {"type":"string","minLength":1,"maxLength":255,"pattern":"^[ -~]+$"} |
Request body: application/json, PdfRequest.
| Status | Meaning | Body |
|---|---|---|
| 200 | The job is terminal. Check status for succeeded, failed, or cancelled; HTTP 200 does not mean the render succeeded. | application/json: PdfJob |
| 202 | The job was accepted and is still queued or running. Poll GET /v1/pdf/{id} for the terminal result. | application/json: PdfJob |
| 400 | The request failed validation, saved-template data did not match its schema, bounded template evaluation failed, or the Idempotency-Key header was the wrong shape. | application/json: ErrorEnvelope |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 402 | The account does not hold the one credit required for this render. The code is insufficient_credits. The reservation settles at one credit on success and is fully released on failure. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | This idempotency key was already used for a different logical request, or the selected saved-template version changed before admission completed. | application/json: ErrorEnvelope |
| 413 | The request is larger than this build accepts. | application/json: ErrorEnvelope |
| 415 | The request did not carry a JSON content type. | application/json: ErrorEnvelope |
| 429 | A direct fleet-protection limit was hit. The code is rate_limited, account_concurrency_limited, or key_concurrency_limited. Each key sustains five requests per second with an idle burst of twenty-one. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
| 503 | The service is at capacity. | application/json: ErrorEnvelope |
listPdfHistory
GET /v1/pdf
List render history
List the authenticated account's render jobs, newest first. Failed or cancelled jobs removed through DELETE do not appear. Bounded by keyset: pass the previous page's next_cursor to continue, rather than an offset, so a page already read stays stable while new jobs are created.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
limit | query | No | 1 to 100. Defaults to 20. A value outside this range is clamped rather than refused. {"type":"integer","default":20} |
cursor | query | No | An opaque value from an earlier page's next_cursor. A cursor that cannot be read is refused with 400. {"type":"string","maxLength":128} |
| Status | Meaning | Body |
|---|---|---|
| 200 | One page of the account's render history. | application/json: PdfHistoryPage |
| 400 | The limit parameter was not a whole number, the cursor could not be read, or the query held an unknown parameter. | application/json: ErrorEnvelope |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
createImage
POST /v1/image
Create a static image
Render one PNG, JPEG, or WebP from raw HTML, an inline stateless template, or one owned saved-template version. Returns 200 for a terminal job or 202 while the accepted job remains queued or running. A terminal job can be succeeded, failed, or cancelled; check status before downloading. Send wait=false to skip the bounded wait. A successful render costs exactly one credit; a failed or cancelled render costs zero.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
Idempotency-Key | header | No | {"type":"string","minLength":1,"maxLength":255,"pattern":"^[ -~]+$"} |
Request body: application/json, ImageRequest.
| Status | Meaning | Body |
|---|---|---|
| 200 | The job is terminal. Check status for succeeded, failed, or cancelled; HTTP 200 does not mean the render succeeded. | application/json: ImageJob |
| 202 | The image job is durable and remains queued or running. | application/json: ImageJob |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 402 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 415 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 429 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 503 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
listImageHistory
GET /v1/image
List image history
List the authenticated account's image jobs, newest first. Failed or cancelled jobs removed through DELETE do not appear. Bounded by keyset: pass the previous page's next_cursor to continue, rather than an offset, so a page already read stays stable while new jobs are created.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
limit | query | No | 1 to 100. Defaults to 20. A value outside this range is clamped rather than refused. {"type":"integer","default":20} |
cursor | query | No | An opaque value from an earlier page's next_cursor. A cursor that cannot be read is refused with 400. {"type":"string","maxLength":128} |
| Status | Meaning | Body |
|---|---|---|
| 200 | One page of the account's image history. | application/json: ImageHistoryPage |
| 400 | The limit parameter was not a whole number, the cursor could not be read, or the query held an unknown parameter. | application/json: ErrorEnvelope |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
getImage
GET /v1/image/{id}
Get a static image's status
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
id | path | Yes | {"type":"string","pattern":"^image_[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The image job's current status. | application/json: ImageJob |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
deleteImage
DELETE /v1/image/{id}
Delete an image file or an unsuccessful render
Remove a successful job's file, or remove a failed or cancelled job from history. Repeated requests are safe. The job stays readable by ID, and billing, abuse limits, and idempotency keys stay unchanged. Queued and running jobs return 409.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
id | path | Yes | {"type":"string","pattern":"^image_[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The image job after file or history removal. | application/json: ImageJob |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
createTemplateBuild
POST /v1/template-builds
Start a template build
Start one durable Build a template conversation from words, supported HTML, one source image, or your own working template source. The fixed successful price is 50 credits. A build from source makes no model call and costs 0 credits. A failed build releases the full reservation.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
Idempotency-Key | header | Yes | {"type":"string","minLength":1,"maxLength":255,"pattern":"^[ -~]+$"} |
Request body: application/json, TemplateBuildCreate.
| Status | Meaning | Body |
|---|---|---|
| 200 | An earlier matching request was replayed. | application/json: TemplateBuild |
| 202 | The durable build was accepted. | application/json: TemplateBuild |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 402 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 503 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
getTemplateBuild
GET /v1/template-builds/{build_id}
Get a template build
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
build_id | path | Yes | TemplateBuildId |
| Status | Meaning | Body |
|---|---|---|
| 200 | The current draft and latest message facts. | application/json: TemplateBuild |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
listTemplateMessages
GET /v1/template-builds/{build_id}/messages
List template chat messages
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
build_id | path | Yes | TemplateBuildId |
| Status | Meaning | Body |
|---|---|---|
| 200 | The user-visible messages in chat order. One chat holds at most 100 messages. | application/json: {"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/TemplateMessage"}} |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
editTemplateBuild
POST /v1/template-builds/{build_id}/messages
Edit the working template
Run one later AI edit. A text or HTML edit costs 10 credits. A new source image costs 50 credits. A source message replaces the draft with your own working template, makes no model call, and costs 0 credits. A failed edit keeps the previous valid draft and releases the reservation.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
build_id | path | Yes | TemplateBuildId |
Idempotency-Key | header | Yes | {"type":"string","minLength":1,"maxLength":255,"pattern":"^[ -~]+$"} |
Request body: application/json, TemplateMessageCreate.
| Status | Meaning | Body |
|---|---|---|
| 200 | An earlier matching edit was replayed. | application/json: TemplateBuild |
| 202 | The durable edit was accepted. | application/json: TemplateBuild |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 402 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 503 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
getTemplateBuildDraft
GET /v1/template-builds/{build_id}/draft
Get the working template
Read the current draft as editable source, sample data, schema, and output. The build has no draft until its first message succeeds.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
build_id | path | Yes | TemplateBuildId |
| Status | Meaning | Body |
|---|---|---|
| 200 | The current working template. | application/json: TemplateBuildDraft |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
getTemplateMessageDraft
GET /v1/template-builds/{build_id}/messages/{message_id}/draft
Get a completed message draft
Read the retained final source, sample data, schema, and output from a succeeded message in an owned build. The response excludes the brand snapshot. Reading it changes no state and costs no credits. Pending, failed, and cancelled messages have no final draft.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
build_id | path | Yes | TemplateBuildId |
message_id | path | Yes | TemplateMessageId |
| Status | Meaning | Body |
|---|---|---|
| 200 | The final draft. Cache-Control is private, no-store. | application/json: TemplateBuildDraft |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
cancelTemplateMessage
DELETE /v1/template-builds/{build_id}/messages/{message_id}
Cancel a pending template message
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
build_id | path | Yes | TemplateBuildId |
message_id | path | Yes | TemplateMessageId |
| Status | Meaning | Body |
|---|---|---|
| 200 | The message was cancelled and its reservation was released. | application/json: TemplateMessage |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
publishTemplateBuild
POST /v1/template-builds/{build_id}/publish
Publish the current working template
Explicitly publish the current valid draft as one normal immutable saved-template version. Repeating publication of the same draft returns the same version.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
build_id | path | Yes | TemplateBuildId |
| Status | Meaning | Body |
|---|---|---|
| 200 | This draft was already published. | application/json: TemplatePublication |
| 201 | The immutable template version was published. | application/json: TemplatePublication |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
createTemplate
POST /v1/templates
Create a saved template
Authentication: bearer API key. See authentication.
Request body: application/json, TemplateWriteRequest.
| Status | Meaning | Body |
|---|---|---|
| 201 | The created template, including version 1 source and schema. | application/json: TemplateDetail |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
listTemplates
GET /v1/templates
List saved templates
List the account's active and archived templates newest first. Source and schema are omitted. Pass next_cursor to continue with a stable keyset page.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
limit | query | No | {"type":"integer","default":20} |
cursor | query | No | {"type":"string","maxLength":128} |
| Status | Meaning | Body |
|---|---|---|
| 200 | One page of saved-template summaries. | application/json: TemplatePage |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
previewTemplate
POST /v1/templates/preview
Preview template data as HTML
Expand source and a JSON data object through the bounded template engine. Optional schema validation uses the saved-template rules. This does not save a template, create a render, or spend credits. The response is untrusted customer HTML for an isolated preview. Browser sessions need the current CSRF token.
Authentication: bearer API key. See authentication.
Request body: application/json, TemplatePreviewRequest.
| Status | Meaning | Body |
|---|---|---|
| 200 | The expanded HTML. This response uses Cache-Control: private, no-store. | application/json: TemplatePreview |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 415 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 429 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 503 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
getTemplate
GET /v1/templates/{template_id}
Get a saved template
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
template_id | path | Yes | TemplateId |
| Status | Meaning | Body |
|---|---|---|
| 200 | The template and its latest immutable version. | application/json: TemplateDetail |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
renameTemplate
PATCH /v1/templates/{template_id}
Rename a saved template
Set or clear the name of one owned template. The name is trimmed and can have 1 to 120 characters. A null or blank name clears it.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
template_id | path | Yes | TemplateId |
Request body: application/json, TemplateRename.
| Status | Meaning | Body |
|---|---|---|
| 200 | The template with its new name. | application/json: TemplateSummary |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
archiveTemplate
DELETE /v1/templates/{template_id}
Archive a saved template
Archive one owned template. The operation is idempotent. Archived templates and versions remain readable, but they cannot receive another version or create another render.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
template_id | path | Yes | TemplateId |
| Status | Meaning | Body |
|---|---|---|
| 200 | The template is archived. | application/json: TemplateSummary |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
createTemplateVersion
POST /v1/templates/{template_id}/versions
Create a template version
Edit a template by publishing one complete new immutable source and optional schema. Existing versions never change. Archived templates cannot receive another version.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
template_id | path | Yes | TemplateId |
Request body: application/json, TemplateVersionWriteRequest.
| Status | Meaning | Body |
|---|---|---|
| 201 | The new immutable version, including source and schema. | application/json: TemplateVersionDetail |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
listTemplateVersions
GET /v1/templates/{template_id}/versions
List template versions
List immutable versions newest first. Source and schema are omitted. Archived template versions remain readable.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
template_id | path | Yes | TemplateId |
limit | query | No | {"type":"integer","default":20} |
cursor | query | No | {"type":"string","maxLength":32} |
| Status | Meaning | Body |
|---|---|---|
| 200 | One page of immutable version summaries. | application/json: TemplateVersionPage |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
getTemplateVersion
GET /v1/templates/{template_id}/versions/{version}
Get a template version
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
template_id | path | Yes | TemplateId |
version | path | Yes | {"type":"integer","minimum":1} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The exact immutable template version. | application/json: TemplateVersionDetail |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
createBrandKit
POST /v1/brand-kits
Create a brand kit
Create one mutable account-owned brand kit. Logos and fonts are uploaded through the kit's asset route after creation.
Authentication: bearer API key. See authentication.
Request body: application/json, BrandKitWrite.
| Status | Meaning | Body |
|---|---|---|
| 201 | The brand kit was created. | application/json: BrandKitDetail |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 415 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 422 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
listBrandKits
GET /v1/brand-kits
List brand kits
List the account's active and archived brand kits newest first. Asset metadata is omitted from the page; read one kit for its current assets.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
limit | query | No | {"type":"integer","default":20} |
cursor | query | No | {"type":"string","maxLength":128} |
| Status | Meaning | Body |
|---|---|---|
| 200 | One page of brand-kit summaries. | application/json: BrandKitPage |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
getBrandKit
GET /v1/brand-kits/{brand_kit_id}
Get a brand kit
Return one owned kit and its current logo and font metadata. Replaced assets remain private durable content but are omitted.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
brand_kit_id | path | Yes | BrandKitId |
| Status | Meaning | Body |
|---|---|---|
| 200 | The brand kit and its current assets. | application/json: BrandKitDetail |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
updateBrandKit
PATCH /v1/brand-kits/{brand_kit_id}
Update a brand kit
Replace any supplied mutable brand values. Omitted fields stay unchanged; a null tone_guidance clears it. Archived kits cannot change.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
brand_kit_id | path | Yes | BrandKitId |
Request body: application/json, BrandKitPatch.
| Status | Meaning | Body |
|---|---|---|
| 200 | The updated kit and its current assets. | application/json: BrandKitDetail |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 415 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 422 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
archiveBrandKit
DELETE /v1/brand-kits/{brand_kit_id}
Archive a brand kit
Archive one owned kit. The operation is idempotent. The kit and every current or replaced asset stay durable for already captured references.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
brand_kit_id | path | Yes | BrandKitId |
| Status | Meaning | Body |
|---|---|---|
| 204 | The brand kit is archived. | No body |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
createBrandAsset
POST /v1/brand-kits/{brand_kit_id}/assets
Add a brand asset
Add one logo or WOFF2 font to an active owned kit. The declared Content-Type and the complete bytes must agree. No customer bytes or storage reference appears in the response.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
brand_kit_id | path | Yes | BrandKitId |
Request body: image/png, {"type":"string","format":"binary"}.
Request body: image/jpeg, {"type":"string","format":"binary"}.
Request body: image/webp, {"type":"string","format":"binary"}.
Request body: font/woff2, {"type":"string","format":"binary"}.
| Status | Meaning | Body |
|---|---|---|
| 201 | The immutable asset was accepted and selected by the kit. | application/json: BrandAsset |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 415 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 422 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 429 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 503 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
readBrandAsset
GET /v1/brand-kits/{brand_kit_id}/assets/{asset_id}
Read a brand asset
Read the validated logo or font bytes from an owned kit. Captured assets remain readable after replacement or archive. Responses are private and must not be cached.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
brand_kit_id | path | Yes | BrandKitId |
asset_id | path | Yes | BrandAssetId |
| Status | Meaning | Body |
|---|---|---|
| 200 | The validated image or WOFF2 font bytes. | image/png: {"type":"string","format":"binary"}; image/jpeg: {"type":"string","format":"binary"}; image/webp: {"type":"string","format":"binary"}; font/woff2: {"type":"string","format":"binary"} |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
replaceBrandAsset
PUT /v1/brand-kits/{brand_kit_id}/assets/{asset_id}
Replace a brand asset
Create a new immutable asset of the same kind and make it current. The old asset and bytes stay unchanged for any template or queued operation that already captured its ID.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
brand_kit_id | path | Yes | BrandKitId |
asset_id | path | Yes | BrandAssetId |
Request body: image/png, {"type":"string","format":"binary"}.
Request body: image/jpeg, {"type":"string","format":"binary"}.
Request body: image/webp, {"type":"string","format":"binary"}.
Request body: font/woff2, {"type":"string","format":"binary"}.
| Status | Meaning | Body |
|---|---|---|
| 201 | The new immutable current asset. | application/json: BrandAsset |
| 400 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 401 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 415 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 422 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 429 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 503 | The one error envelope every backend response uses. | application/json: ErrorEnvelope |
getPdf
GET /v1/pdf/{id}
Get a PDF's status
Return one render job's current status, in the same representation POST /v1/pdf answers with. Mints a fresh signed download link on every call while the PDF is still available.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
id | path | Yes | The job's public identifier, such as "pdf*1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^pdf*[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The job's current status. | application/json: PdfJob |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 404 | No job with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, or that is not well formed, so a request can never learn which one is true. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
deletePdf
DELETE /v1/pdf/{id}
Delete a PDF file or an unsuccessful render
Remove a successful job's PDF from storage, or remove a failed or cancelled job from history. The job stays readable by ID, and billing, abuse limits, and idempotency keys stay unchanged. The file is removed before its database marker; a file-removal failure returns 500 and leaves the record live for a safe retry. Repeated requests are safe. At or after the fixed retention cutoff, the removal reason remains expired.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
id | path | Yes | The job's public identifier, such as "pdf*1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^pdf*[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The job's current status after file or history removal. | application/json: PdfJob |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 404 | No job with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, or that is not well formed, so a request can never learn which one is true. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | The job is queued or running, so it cannot be deleted. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
getDownload
GET /v1/downloads/{token}
Download a finished PDF or image
Return the finished PDF, PNG, JPEG, or WebP file a signed download link names. The link carries its own authority, so no other credential is read. The file is opened before 200 is returned: deletion that unlinks first gives 404, while a download that opens first finishes and every later download fails.
Authentication: no API key required.
| Parameter | Location | Required | Rule |
|---|---|---|---|
token | path | Yes | The signed download link. {"type":"string"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The finished PDF or image, bounded by the 25 MiB artifact limit. | application/pdf: {"type":"string","format":"binary"}; image/png: {"type":"string","format":"binary"}; image/jpeg: {"type":"string","format":"binary"}; image/webp: {"type":"string","format":"binary"} |
| 403 | The link is genuine but its time is over. | application/json: ErrorEnvelope |
| 404 | The link is not valid. This is also the answer for a link whose job or file does not exist, so a link can never be used to ask what this product holds. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
createApiKey
POST /v1/keys
Create an API key
Create one API key for the authenticated account. The full secret is returned in this response and never again; only its verifier is kept.
Authentication: bearer API key. See authentication.
Request body: application/json, NewApiKey.
| Status | Meaning | Body |
|---|---|---|
| 201 | The key was created. This is the only response that ever carries the secret. | application/json: ApiKeyWithSecret |
| 400 | The name was empty, longer than 100 characters, or held a character that is not printable ASCII. | application/json: ErrorEnvelope |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | The account already holds ten active keys. Revoke one before creating another. | application/json: ErrorEnvelope |
| 413 | The request body is larger than 16 KiB. | application/json: ErrorEnvelope |
| 415 | The request did not carry a JSON content type. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
listApiKeys
GET /v1/keys
List API keys
List one bounded page of the authenticated account's keys, most recently created first. A revoked key stays in this history; its secret never appears here.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
limit | query | No | The page size. Defaults to 20 and must be from 1 through 100. {"type":"integer","minimum":1,"maximum":100,"default":20} |
cursor | query | No | An opaque value from an earlier page's next_cursor. {"type":"string","maxLength":128} |
| Status | Meaning | Body |
|---|---|---|
| 200 | One page of the account's key history. | application/json: ApiKeyList |
| 400 | The page size or cursor is not valid. | application/json: ErrorEnvelope |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
revokeApiKey
DELETE /v1/keys/{key_id}
Revoke an API key
Revoke one key of the authenticated account. Revoking a key that is already revoked returns the same answer again rather than an error, so a retried request is never rejected.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
key_id | path | Yes | The key's public identifier, such as "key*1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^key*[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The key, now revoked. | application/json: ApiKey |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 404 | No key with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
updateApiKey
PATCH /v1/keys/{key_id}
Set or clear an API key's monthly overage cap
Change how many overage credits this key may take on per calendar month (UTC). A positive number sets the cap; an explicit null, or a body without the field, clears it. The cap bounds only overage: work the prepaid balance fully covers is never refused by it.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
key_id | path | Yes | The key's public identifier, such as "key*1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^key*[0-9a-f]{32}$"} |
Request body: application/json, ApiKeyUpdate.
| Status | Meaning | Body |
|---|---|---|
| 200 | The key with its new cap. | application/json: ApiKey |
| 400 | The cap was zero, negative, not a whole number, or the body held a field this operation does not know. | application/json: ErrorEnvelope |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 404 | No key with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The request body is larger than 16 KiB. | application/json: ErrorEnvelope |
| 415 | The request did not carry a JSON content type. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
createWebhook
POST /v1/webhooks
Register a webhook destination
Register one HTTPS destination for terminal render events. The URL must pass the same egress policy the platform applies to every outbound request — public HTTPS on port 443, no credentials, and a DNS answer set of public addresses only — and delivery rechecks all of it on every connection, so a name that later resolves privately is refused then too. The signing secret is returned in this response and never again. Every delivery carries a Thirds-Signature header of the form "t=<unix-seconds>,v1=<hex>": each v1 value is HMAC-SHA256 over the exact bytes "<t>.<body>" with one signing secret, so verify against the raw body before parsing it, and refuse a timestamp too far from your own clock to bound replays. During a rotation overlap the header carries two v1 values — the current secret's first, then the previous secret's — and a receiver accepts the delivery when any one value matches.
Authentication: bearer API key. See authentication.
Request body: application/json, NewWebhook.
| Status | Meaning | Body |
|---|---|---|
| 201 | The destination was registered and is enabled. This is the only response, with rotation's, that ever carries the signing secret. | application/json: WebhookWithSecret |
| 400 | The URL failed the egress policy — not HTTPS on port 443, malformed, carrying credentials, or resolving to a private, loopback, link-local, metadata, or reserved address — or the events list was empty, had duplicates, had more than three entries, or named an unknown event. The details entry carries the stable policy code that refused it. | application/json: ErrorEnvelope |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | The account already holds ten webhook destinations. Delete one before registering another. | application/json: ErrorEnvelope |
| 413 | The request body is larger than 16 KiB. | application/json: ErrorEnvelope |
| 415 | The request did not carry a JSON content type. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
listWebhooks
GET /v1/webhooks
List webhook destinations
List every webhook destination of the authenticated account, most recently created first. The signing secret never appears here.
Authentication: bearer API key. See authentication.
| Status | Meaning | Body |
|---|---|---|
| 200 | The account's destinations. | application/json: WebhookList |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
getWebhook
GET /v1/webhooks/{webhook_id}
Read a webhook destination
Read one destination of the authenticated account, without its signing secret.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
webhook_id | path | Yes | The destination's public identifier, such as "wh*1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^wh*[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The destination. | application/json: Webhook |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | No destination with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
deleteWebhook
DELETE /v1/webhooks/{webhook_id}
Delete a webhook destination
Delete one destination of the authenticated account. Deliveries stop at once, and events not yet delivered to it are dropped.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
webhook_id | path | Yes | The destination's public identifier, such as "wh*1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^wh*[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 204 | The destination is deleted. There is no body. | No body |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | No destination with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
rotateWebhookSecret
POST /v1/webhooks/{webhook_id}/rotate
Rotate a webhook destination's signing secret
Mint a new signing secret for one destination. The new secret signs immediately and is returned in this response and never again. The previous secret keeps signing alongside it for 24 hours — every delivery in the overlap carries both signatures — so switch the receiver to the new secret inside that window. Rotating again during an overlap replaces the previous secret at once: only the last two secrets ever sign.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
webhook_id | path | Yes | The destination's public identifier, such as "wh*1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^wh*[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The destination with its new secret. This is the only response, with creation's, that ever carries the signing secret. | application/json: WebhookWithSecret |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | No destination with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
disableWebhook
POST /v1/webhooks/{webhook_id}/disable
Disable a webhook destination
Stop deliveries to one destination without deleting it. Disabling a destination that is already disabled returns the same answer again rather than an error, so a retried request is never rejected.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
webhook_id | path | Yes | The destination's public identifier, such as "wh*1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^wh*[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The destination, now disabled by owner request. | application/json: Webhook |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | No destination with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
enableWebhook
POST /v1/webhooks/{webhook_id}/enable
Enable a webhook destination
Resume deliveries to one destination, whether the owner disabled it or repeated delivery failures did, and reset its consecutive failure count. Enabling a destination that is already enabled returns the same answer again rather than an error, so a retried request is never rejected.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
webhook_id | path | Yes | The destination's public identifier, such as "wh*1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^wh*[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The destination, enabled again. | application/json: Webhook |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | No destination with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
replayWebhook
POST /v1/webhooks/{webhook_id}/replay
Replay a webhook destination's failed events
Return every event of one destination that ran out of delivery attempts to the queue with a fresh attempt budget. An event gets eight attempts per creation or replay, spread over roughly one day of growing backoff; after that it is failed and waits here. A replayed delivery carries the same event id and the same body as before, with a current signature and timestamp, so a receiver that stores event ids deduplicates it like any other retry. Replaying a destination with no failed events answers a count of zero rather than an error, so a retried request is never rejected.
Authentication: bearer API key. See authentication.
| Parameter | Location | Required | Rule |
|---|---|---|---|
webhook_id | path | Yes | The destination's public identifier, such as "wh*1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^wh*[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | How many failed events returned to the queue. | application/json: WebhookReplay |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 403 | The authenticated account is suspended. The code is account_suspended. | application/json: ErrorEnvelope |
| 404 | No destination with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
Schemas
Each table lists the fields for one request or response. Follow a linked type name to see its fields. The JSON rules below each table cover nested values and other limits. Response headers and shared errors are also in the OpenAPI file.
UploadedImage
| Field | Required | Description and type |
|---|---|---|
id | Yes | {"type":"string","pattern":"^asset_[a-f0-9]{32}$"} |
reference | Yes | Private template reference. This is not a fetchable URL. {"type":"string","pattern":"^asset://asset_[a-f0-9]{32}$"} |
media_type | Yes | {"type":"string","enum":["image/png","image/jpeg","image/webp"]} |
byte_size | Yes | {"type":"integer","minimum":1,"maximum":10485760} |
width | Yes | Display width after image orientation. {"type":"integer","minimum":1,"maximum":7680} |
height | Yes | Display height after image orientation. {"type":"integer","minimum":1,"maximum":7680} |
Schema rules: {"type":"object","additionalProperties":false}.
FieldDetail
One request field that failed, its fixed safe reason, and an optional bounded source location. Details never carry template source, customer values, rendered output, or raw evaluator prose.
| Field | Required | Description and type |
|---|---|---|
field | Yes | The path to the field, such as "pdf.scale". Schema errors use data followed by a JSON Pointer, such as data/items/0/count. The pointer is cut at 256 UTF-8 bytes. Empty for a problem with the whole document. {"type":"string","maxLength":260} |
reason | Yes | A fixed, safe reason. It never repeats the value the caller sent. {"type":"string","enum":["malformed JSON","missing field","unknown field","wrong type","invalid value","A required value is missing.","Use the expected value type.","Declare this variable before using it.","Choose an allowed value.","Use the required format.","Add a value.","Use a shorter value.","Use a number within the allowed range.","Check this value against its data rule."]} |
line | No | The one-based template source line when the evaluator provides one within the published bound. {"type":"integer","minimum":1,"maximum":1000000} |
column | No | The one-based template source column when the evaluator provides one within the published bound. {"type":"integer","minimum":1,"maximum":1000000} |
Schema rules: {"type":"object","additionalProperties":false}.
BrandKitId
Schema rules: {"type":"string","pattern":"^kit_[0-9a-f]{32}$"}.
BrandAssetId
Schema rules: {"type":"string","pattern":"^asset_[0-9a-f]{32}$"}.
BrandKitWrite
| Field | Required | Description and type |
|---|---|---|
name | Yes | {"type":"string","minLength":1,"maxLength":100} |
colours | No | {"type":"array","maxItems":16,"uniqueItems":true,"items":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},"default":[]} |
tone_guidance | No | {"type":["string","null"],"minLength":1,"maxLength":500} |
Schema rules: {"type":"object","additionalProperties":false}.
BrandKitPatch
| Field | Required | Description and type |
|---|---|---|
name | No | {"type":"string","minLength":1,"maxLength":100} |
colours | No | {"type":"array","maxItems":16,"uniqueItems":true,"items":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"}} |
tone_guidance | No | {"type":["string","null"],"minLength":1,"maxLength":500} |
Schema rules: {"type":"object","minProperties":1,"additionalProperties":false}.
BrandKitSummary
| Field | Required | Description and type |
|---|---|---|
id | Yes | BrandKitId |
name | Yes | {"type":"string","minLength":1,"maxLength":100} |
colours | Yes | {"type":"array","maxItems":16,"uniqueItems":true,"items":{"type":"string","pattern":"^#[0-9a-f]{6}$"}} |
tone_guidance | Yes | {"type":["string","null"],"minLength":1,"maxLength":500} |
created_at | Yes | {"type":"string","format":"date-time"} |
updated_at | Yes | {"type":"string","format":"date-time"} |
archived_at | Yes | {"type":["string","null"],"format":"date-time"} |
Schema rules: {"type":"object","additionalProperties":false}.
BrandAsset
| Field | Required | Description and type |
|---|---|---|
id | Yes | BrandAssetId |
kind | Yes | {"type":"string","enum":["logo","font"]} |
media_type | Yes | {"type":"string","enum":["image/png","image/jpeg","image/webp","font/woff2"]} |
byte_size | Yes | {"type":"integer","minimum":1,"maximum":10485760} |
created_at | Yes | {"type":"string","format":"date-time"} |
Schema rules: {"type":"object","additionalProperties":false}.
BrandKitDetail
| Field | Required | Description and type |
|---|---|---|
id | Yes | BrandKitId |
name | Yes | {"type":"string","minLength":1,"maxLength":100} |
colours | Yes | {"type":"array","maxItems":16,"uniqueItems":true,"items":{"type":"string","pattern":"^#[0-9a-f]{6}$"}} |
tone_guidance | Yes | {"type":["string","null"],"minLength":1,"maxLength":500} |
created_at | Yes | {"type":"string","format":"date-time"} |
updated_at | Yes | {"type":"string","format":"date-time"} |
archived_at | Yes | {"type":["string","null"],"format":"date-time"} |
assets | Yes | {"type":"array","maxItems":16,"items":{"$ref":"#/components/schemas/BrandAsset"}} |
Schema rules: {"type":"object","additionalProperties":false}.
BrandKitPage
| Field | Required | Description and type |
|---|---|---|
items | Yes | {"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/BrandKitSummary"}} |
next_cursor | Yes | {"type":["string","null"],"maxLength":128} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateBuildId
Schema rules: {"type":"string","pattern":"^build_[0-9a-f]{32}$"}.
TemplateMessageId
Schema rules: {"type":"string","pattern":"^msg_[0-9a-f]{32}$"}.
TemplateBuildInput
Schema rules: {"oneOf":[{"type":"object","properties":{"type":{"const":"prompt"},"prompt":{"type":"string","minLength":1,"maxLength":65536}},"required":["type","prompt"],"additionalProperties":false},{"type":"object","properties":{"type":{"const":"html"},"html":{"type":"string","minLength":1,"maxLength":65536}},"required":["type","html"],"additionalProperties":false},{"type":"object","properties":{"type":{"const":"image"},"base64":{"type":"string","minLength":1,"maxLength":13981016,"contentEncoding":"base64"}},"required":["type","base64"],"additionalProperties":false},{"type":"object","description":"Your own working template, sent as data. It must pass the saved-template checks. It becomes the draft without a model call and costs 0 credits.","properties":{"type":{"const":"source"},"source":{"type":"string","minLength":1,"maxLength":1048576},"sample_data":{"type":"object"},"schema":{"oneOf":[{"type":"object"},{"type":"boolean"}]}},"required":["type","source"],"additionalProperties":false}]}.
TemplateOutputIntent
Schema rules: {"oneOf":[{"type":"object","description":"Let the model choose PDF or image from the message. An image result from a source image uses the source dimensions.","properties":{"type":{"const":"auto"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"const":"pdf"}},"required":["type"],"additionalProperties":false},{"type":"object","description":"Request an image. Set both width and height to override the source dimensions. Without a source image, omitted dimensions use 1200 by 628.","properties":{"type":{"const":"image"},"format":{"type":"string","enum":["png","jpeg","webp"],"default":"png"},"width":{"type":"integer","minimum":320,"maximum":7680},"height":{"type":"integer","minimum":200,"maximum":4320}},"required":["type"],"additionalProperties":false}]}.
TemplateOutput
Schema rules: {"oneOf":[{"type":"object","properties":{"type":{"const":"pdf"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"const":"image"},"format":{"type":"string","enum":["png","jpeg","webp"]},"width":{"type":"integer","minimum":320,"maximum":7680},"height":{"type":"integer","minimum":200,"maximum":4320}},"required":["type","format","width","height"],"additionalProperties":false}]}.
TemplateBuildCreate
| Field | Required | Description and type |
|---|---|---|
brand_kit_id | Yes | BrandKitId |
input | Yes | TemplateBuildInput |
input_mode | No | How new manual work starts. Only source input accepts this field. Omit it when the source comes from a saved template or an existing chat. {"type":"string","enum":["blank","html"]} |
output | No | TemplateOutputIntent |
Schema rules: {"type":"object","if":{"required":["input_mode"]},"then":{"properties":{"input":{"properties":{"type":{"const":"source"}}}}},"additionalProperties":false}.
TemplateMessageCreate
| Field | Required | Description and type |
|---|---|---|
input | Yes | TemplateBuildInput |
output | No | TemplateOutputIntent |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateMessage
| Field | Required | Description and type |
|---|---|---|
id | Yes | TemplateMessageId |
state | Yes | {"type":"string","enum":["queued","running","previewing","succeeded","failed","cancelled"]} |
input_type | Yes | {"type":"string","enum":["prompt","html","image","source"]} |
credits | Yes | {"type":"integer","enum":[0,10,50]} |
preview_id | Yes | {"type":["string","null"],"pattern":"^(pdf|image)_[0-9a-f]{32}$"} |
output | Yes | {"oneOf":[{"$ref":"#/components/schemas/TemplateOutput"},{"type":"null"}]} |
review_count | Yes | {"type":"integer","minimum":0,"maximum":5} |
max_reviews | Yes | {"type":"integer","minimum":0,"maximum":5} |
input_text | No | {"type":"string","maxLength":65536} |
failure_category | Yes | {"type":["string","null"],"enum":["provider","validation","renderer","unsupported_input","internal",null]} |
failure_code | Yes | {"type":["string","null"],"pattern":"^[a-z][a-z0-9_]{0,63}$"} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateBuild
| Field | Required | Description and type |
|---|---|---|
id | Yes | TemplateBuildId |
brand_kit_id | Yes | BrandKitId |
current_draft | Yes | {"type":"boolean"} |
current_message_id | Yes | {"oneOf":[{"$ref":"#/components/schemas/TemplateMessageId"},{"type":"null"}]} |
published_template_id | Yes | {"oneOf":[{"$ref":"#/components/schemas/TemplateId"},{"type":"null"}]} |
message | Yes | TemplateMessage |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateBuildDraft
| Field | Required | Description and type |
|---|---|---|
source | Yes | {"type":"string","maxLength":1048576} |
sample_data | Yes | {"type":"object"} |
schema | Yes | {} |
output | Yes | TemplateOutput |
Schema rules: {"type":"object","additionalProperties":false}.
TemplatePublication
| Field | Required | Description and type |
|---|---|---|
template_id | Yes | TemplateId |
version | Yes | {"type":"integer","minimum":1} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateId
Schema rules: {"type":"string","pattern":"^tpl_[0-9a-f]{32}$"}.
TemplateName
The customer's own name for a template. It is trimmed and can have 1 to 120 characters. Null or blank means no name.
Schema rules: {"type":["string","null"],"maxLength":120}.
TemplateWriteRequest
| Field | Required | Description and type |
|---|---|---|
name | No | TemplateName |
input_mode | No | How this new template starts. It records a blank-page start or imported HTML. New versions do not accept this field. {"type":"string","enum":["blank","html"],"default":"html"} |
source | Yes | {"type":"string","maxLength":1048576} |
schema | No | {"oneOf":[{"type":"object"},{"type":"boolean"},{"type":"null"}]} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateVersionWriteRequest
| Field | Required | Description and type |
|---|---|---|
source | Yes | {"type":"string","maxLength":1048576} |
schema | No | {"oneOf":[{"type":"object"},{"type":"boolean"},{"type":"null"}]} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplatePreviewRequest
| Field | Required | Description and type |
|---|---|---|
brand_kit_id | No | Select an owned active brand kit. This uses template evaluation and its 1 MiB source and data limits even when data is omitted. Overrides the source thirds-brand-kit meta default. Supplies reserved brand.name, brand.palette, brand.colours.primary/secondary/accent, brand.logo, brand.logos, and brand.fonts entries with family and src. data.brand is refused when a kit is selected. Only present palette roles and logos are supplied. Assets are captured before enqueue. Schemas validate customer data before brand is added. {"type":["string","null"],"pattern":"^kit_[a-f0-9]{32}$"} |
source | Yes | {"type":"string","maxLength":1048576} |
data | Yes | Template values, bounded to 1,048,576 encoded JSON bytes with the template depth and collection limits. {"type":"object"} |
schema | No | {"oneOf":[{"type":"object"},{"type":"boolean"},{"type":"null"}]} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplatePreview
| Field | Required | Description and type |
|---|---|---|
html | Yes | {"type":"string","maxLength":5242880} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateRename
| Field | Required | Description and type |
|---|---|---|
name | No | TemplateName |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateSummary
| Field | Required | Description and type |
|---|---|---|
id | Yes | TemplateId |
name | Yes | TemplateName |
created_at | Yes | {"type":"string","format":"date-time"} |
archived_at | Yes | {"type":["string","null"],"format":"date-time"} |
latest_version | Yes | {"type":"integer","minimum":1} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateDetail
| Field | Required | Description and type |
|---|---|---|
id | Yes | TemplateId |
name | Yes | TemplateName |
created_at | Yes | {"type":"string","format":"date-time"} |
archived_at | Yes | {"type":["string","null"],"format":"date-time"} |
latest_version | Yes | {"type":"integer","minimum":1} |
source | Yes | {"type":"string","maxLength":1048576} |
schema | Yes | {} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplatePage
| Field | Required | Description and type |
|---|---|---|
items | Yes | {"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/TemplateSummary"}} |
next_cursor | Yes | {"type":["string","null"],"maxLength":128} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateVersionSummary
| Field | Required | Description and type |
|---|---|---|
version | Yes | {"type":"integer","minimum":1} |
created_at | Yes | {"type":"string","format":"date-time"} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateVersionDetail
| Field | Required | Description and type |
|---|---|---|
version | Yes | {"type":"integer","minimum":1} |
created_at | Yes | {"type":"string","format":"date-time"} |
source | Yes | {"type":"string","maxLength":1048576} |
schema | Yes | {} |
Schema rules: {"type":"object","additionalProperties":false}.
TemplateVersionPage
| Field | Required | Description and type |
|---|---|---|
items | Yes | {"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/TemplateVersionSummary"}} |
next_cursor | Yes | {"type":["string","null"],"maxLength":32} |
Schema rules: {"type":"object","additionalProperties":false}.
RetryInfo
How long the caller must wait before it retries.
| Field | Required | Description and type |
|---|---|---|
retry_after_seconds | Yes | {"type":"integer","minimum":1,"maximum":60} |
Schema rules: {"type":"object","additionalProperties":false}.
ImageRequestCommon
| Field | Required | Description and type |
|---|---|---|
brand_kit_id | No | Select an owned active brand kit. This uses template evaluation and its 1 MiB source and data limits even when data is omitted. Overrides the source thirds-brand-kit meta default. Supplies reserved brand.name, brand.palette, brand.colours.primary/secondary/accent, brand.logo, brand.logos, and brand.fonts entries with family and src. data.brand is refused when a kit is selected. Only present palette roles and logos are supplied. Assets are captured before enqueue. Schemas validate customer data before brand is added. {"type":["string","null"],"pattern":"^kit_[a-f0-9]{32}$"} |
image | Yes | {"type":"object","properties":{"format":{"type":"string","enum":["png","jpeg","webp"]},"width":{"type":"integer","minimum":320,"maximum":7680,"default":1280},"height":{"type":"integer","minimum":200,"maximum":4320,"default":720},"quality":{"type":"integer","minimum":1,"maximum":100},"transparent":{"type":"boolean","default":false}},"required":["format"],"allOf":[{"if":{"properties":{"format":{"const":"png"}}},"then":{"not":{"required":["quality"]}},"else":{"properties":{"quality":{"default":80}}}},{"if":{"properties":{"format":{"const":"jpeg"}}},"then":{"properties":{"transparent":{"const":false}}}}],"additionalProperties":false} |
javascript | No | {"type":"object","properties":{"mode":{"type":"string","enum":["disabled","enabled"]}},"additionalProperties":false} |
wait | No | {"type":"boolean","default":true} |
Schema rules: {"type":"object"}.
RawImageRequest
Schema rules: {"allOf":[{"$ref":"#/components/schemas/ImageRequestCommon"},{"type":"object","properties":{"html":{"type":"string","minLength":1,"maxLength":5242880}},"required":["html"]}],"unevaluatedProperties":false}.
StatelessTemplateImageRequest
Schema rules: {"allOf":[{"$ref":"#/components/schemas/ImageRequestCommon"},{"type":"object","properties":{"html":{"type":"string","minLength":1,"maxLength":1048576},"data":{"type":"object"}},"required":["html","data"]}],"unevaluatedProperties":false}.
SavedTemplateImageRequest
Schema rules: {"allOf":[{"$ref":"#/components/schemas/ImageRequestCommon"},{"type":"object","properties":{"template_id":{"$ref":"#/components/schemas/TemplateId"},"version":{"type":"integer","minimum":1},"data":{"type":"object"}},"required":["template_id","data"]}],"unevaluatedProperties":false}.
ImageRequest
Schema rules: {"oneOf":[{"$ref":"#/components/schemas/RawImageRequest"},{"$ref":"#/components/schemas/StatelessTemplateImageRequest"},{"$ref":"#/components/schemas/SavedTemplateImageRequest"}]}.
ImageJob
| Field | Required | Description and type |
|---|---|---|
id | Yes | {"type":"string","pattern":"^image_[0-9a-f]{32}$"} |
status | Yes | {"type":"string","enum":["queued","running","succeeded","failed","cancelled"]} |
created_at | Yes | {"type":"string","format":"date-time"} |
finished_at | Yes | {"type":["string","null"],"format":"date-time"} |
template | Yes | The exact saved template version this job rendered. Null for raw HTML or an inline stateless template. {"type":["object","null"],"properties":{"id":{"$ref":"#/components/schemas/TemplateId"},"version":{"type":"integer","minimum":1}},"required":["id","version"],"additionalProperties":false} |
credit | Yes | What happened to the credit this job holds: "reserved" while it is queued or running, "settled" once it succeeded, "released" once it failed or was cancelled, and "none" when the job was never billed. {"type":"string","enum":["reserved","settled","released","none"]} |
artifact | Yes | {"type":["object","null"],"properties":{"media_type":{"type":"string","enum":["image/png","image/jpeg","image/webp"]},"byte_size":{"type":"integer","minimum":0},"sha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"expires_at":{"type":"string","format":"date-time","description":"When retention ends and the file stops being served."},"removed_reason":{"type":["string","null"],"enum":["expired","deleted",null]}},"required":["media_type","byte_size","sha256","expires_at","removed_reason"],"additionalProperties":false} |
error | Yes | {"type":["object","null"],"properties":{"category":{"type":"string","enum":["invalid_input","unsafe_asset","resource_limit","timeout","renderer_failure","internal_failure"]},"code":{"type":"string"}},"required":["category","code"],"additionalProperties":false} |
download | Yes | {"type":["object","null"],"properties":{"url":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}},"required":["url","expires_at"],"additionalProperties":false} |
Schema rules: {"type":"object","additionalProperties":false}.
PdfRequestCommon
Render options shared by raw HTML, stateless-template, and saved-template requests. Every field is optional; an absent field takes the renderer's own default.
| Field | Required | Description and type |
|---|---|---|
brand_kit_id | No | Select an owned active brand kit. This uses template evaluation and its 1 MiB source and data limits even when data is omitted. Overrides the source thirds-brand-kit meta default. Supplies reserved brand.name, brand.palette, brand.colours.primary/secondary/accent, brand.logo, brand.logos, and brand.fonts entries with family and src. data.brand is refused when a kit is selected. Only present palette roles and logos are supplied. Assets are captured before enqueue. Schemas validate customer data before brand is added. {"type":["string","null"],"pattern":"^kit_[a-f0-9]{32}$"} |
pdf | No | PDF layout options. The page defaults to A4 when neither "format" nor both "width" and "height" are given. {"type":"object","properties":{"format":{"type":"string","enum":["A0","A1","A2","A3","A4","A5","A6","Letter","Legal","Ledger","Tabloid"],"description":"A named page size, such as \"A4\" or \"Letter\". Mutually exclusive with \"width\"/\"height\"."},"width":{"type":"string","pattern":"^(?:0\\.(?=[0-9]{1,4}(?:px|in|cm|mm)$)(?=[0-9]*[1-9])[0-9]{1,4}|[1-9][0-9]*(?:\\.[0-9]{1,4})?)(?:px|in|cm|mm)$","description":"An explicit page width with its unit, such as \"8.5in\". Requires \"height\"."},"height":{"type":"string","pattern":"^(?:0\\.(?=[0-9]{1,4}(?:px|in|cm|mm)$)(?=[0-9]*[1-9])[0-9]{1,4}|[1-9][0-9]*(?:\\.[0-9]{1,4})?)(?:px|in|cm|mm)$","description":"An explicit page height with its unit. Requires \"width\"."},"margins":{"type":"object","description":"Each side defaults to \"0mm\".","properties":{"top":{"type":"string","pattern":"^(?:0|[1-9][0-9]*)(?:\\.[0-9]{1,4})?(?:px|in|cm|mm)$"},"right":{"type":"string","pattern":"^(?:0|[1-9][0-9]*)(?:\\.[0-9]{1,4})?(?:px|in|cm|mm)$"},"bottom":{"type":"string","pattern":"^(?:0|[1-9][0-9]*)(?:\\.[0-9]{1,4})?(?:px|in|cm|mm)$"},"left":{"type":"string","pattern":"^(?:0|[1-9][0-9]*)(?:\\.[0-9]{1,4})?(?:px|in|cm|mm)$"}},"additionalProperties":false},"landscape":{"type":"boolean","description":"Defaults to false."},"print_background":{"type":"boolean","description":"Defaults to true."},"scale":{"type":"number","minimum":0.1,"maximum":2,"description":"Defaults to 1.0."},"display_header_footer":{"type":"boolean","description":"Defaults to false."},"header_template":{"type":"string","maxLength":100000,"description":"Defaults to empty."},"footer_template":{"type":"string","maxLength":100000,"description":"Defaults to empty."}},"oneOf":[{"required":["format"],"not":{"anyOf":[{"required":["width"]},{"required":["height"]}]}},{"required":["width","height"],"not":{"required":["format"]}},{"not":{"anyOf":[{"required":["format"]},{"required":["width"]},{"required":["height"]}]}}],"additionalProperties":false} |
viewport | No | {"type":"object","properties":{"width":{"type":"integer","minimum":320,"maximum":7680,"description":"Defaults to 1280."},"height":{"type":"integer","minimum":200,"maximum":4320,"description":"Defaults to 720."},"device_scale_factor":{"type":"number","minimum":1,"maximum":3,"description":"Defaults to 1.0."}},"additionalProperties":false} |
javascript | No | {"type":"object","properties":{"mode":{"type":"string","enum":["disabled","enabled"],"description":"Defaults to \"disabled\"."}},"additionalProperties":false} |
wait | No | Defaults to true. False answers as soon as the job is durable instead of holding the bounded wait; poll GET /v1/pdf/{id} for the terminal result. A delivery preference only — it is not part of the idempotency fingerprint and changes nothing about the render or its cost. {"type":"boolean"} |
Schema rules: {"type":"object"}.
RawPdfRequest
A raw HTML render. The HTML enters the existing render workflow unchanged.
Schema rules: {"allOf":[{"$ref":"#/components/schemas/PdfRequestCommon"},{"type":"object","properties":{"html":{"type":"string","minLength":1,"maxLength":5242880,"description":"The raw HTML document to render. Up to 5,242,880 bytes."}},"required":["html"]}],"unevaluatedProperties":false}.
StatelessTemplatePdfRequest
A bounded stateless template evaluation followed by the existing PDF render workflow. The request saves no reusable template. Data must be exactly one JSON object. Only evaluated HTML and the effective render options enter short-lived render input storage.
Schema rules: {"allOf":[{"$ref":"#/components/schemas/PdfRequestCommon"},{"type":"object","properties":{"html":{"type":"string","minLength":1,"maxLength":1048576,"description":"The inline Jinja-style template source. The evaluator accepts at most 1,048,576 bytes."},"data":{"type":"object","description":"The JSON-compatible data object available to the template. Arrays, scalars, and null are not accepted as the top-level value. Its canonical recursively key-sorted JSON representation may contain at most 1,048,576 bytes; JSON Schema cannot express that serialized-byte bound."}},"required":["html","data"]}],"unevaluatedProperties":false}.
SavedTemplatePdfRequest
Render one exact owned saved-template version. Omit version to resolve the latest version once before validation and admission. Data must be exactly one JSON object.
Schema rules: {"allOf":[{"$ref":"#/components/schemas/PdfRequestCommon"},{"type":"object","properties":{"template_id":{"$ref":"#/components/schemas/TemplateId"},"version":{"type":"integer","minimum":1,"description":"An exact immutable version. Omit to select latest once."},"data":{"type":"object","description":"The JSON-compatible data object validated against the selected version's optional schema before evaluation or admission."}},"required":["template_id","data"]}],"unevaluatedProperties":false}.
PdfRequest
Exactly one supported PDF input mode: raw HTML, an inline stateless template, or one saved-template version. Image request shapes are not part of this endpoint.
Schema rules: {"oneOf":[{"$ref":"#/components/schemas/RawPdfRequest"},{"$ref":"#/components/schemas/StatelessTemplatePdfRequest"},{"$ref":"#/components/schemas/SavedTemplatePdfRequest"}]}.
PdfJob
The state of one render job. This is the same shape whether the job just finished (200) or is still in progress (202).
| Field | Required | Description and type |
|---|---|---|
id | Yes | The job's public identifier: "pdf*" followed by 32 lowercase hexadecimal characters. {"type":"string","pattern":"^pdf*[0-9a-f]{32}$"} |
status | Yes | {"type":"string","enum":["queued","running","succeeded","failed","cancelled"]} |
created_at | Yes | {"type":"string","format":"date-time"} |
finished_at | Yes | {"type":["string","null"],"format":"date-time"} |
template | Yes | The exact saved template version this job rendered. Null for raw HTML or an inline stateless template. {"type":["object","null"],"properties":{"id":{"$ref":"#/components/schemas/TemplateId"},"version":{"type":"integer","minimum":1}},"required":["id","version"],"additionalProperties":false} |
credit | Yes | What happened to the credit this job holds: "reserved" while it is queued or running, "settled" once it succeeded, "released" once it failed or was cancelled, and "none" when the job was never billed. {"type":"string","enum":["reserved","settled","released","none"]} |
artifact | Yes | Present only for a succeeded job. {"type":["object","null"],"properties":{"media_type":{"const":"application/pdf"},"byte_size":{"type":"integer","minimum":0},"sha256":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"64 lowercase hexadecimal characters."},"expires_at":{"type":"string","format":"date-time","description":"When retention ends and the file stops being served."},"removed_reason":{"type":["string","null"],"enum":["expired","deleted",null],"description":"Null while the file is still available. \"expired\" once its retention window or an internal reconciliation has passed; \"deleted\" once the account removed it through DELETE /v1/pdf/{id}."}},"required":["media_type","byte_size","sha256","expires_at","removed_reason"],"additionalProperties":false} |
error | Yes | Present only for a terminal failure. Never carries a message, only a fixed code. {"type":["object","null"],"properties":{"category":{"type":"string","enum":["invalid_input","unsafe_asset","resource_limit","timeout","renderer_failure","internal_failure"]},"code":{"type":"string"}},"required":["category","code"],"additionalProperties":false} |
download | Yes | Present only while a succeeded job's PDF is still available. A fresh signed link every time this job is read. {"type":["object","null"],"properties":{"url":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}},"required":["url","expires_at"],"additionalProperties":false} |
Schema rules: {"type":"object","additionalProperties":false}.
PdfHistoryItem
One job in a bounded account history — the subset of PdfJob a history page lists. Never carries a download link, since a history page can name many jobs at once.
| Field | Required | Description and type |
|---|---|---|
id | Yes | {"type":"string","pattern":"^pdf_[0-9a-f]{32}$"} |
status | Yes | {"type":"string","enum":["queued","running","succeeded","failed","cancelled"]} |
created_at | Yes | {"type":"string","format":"date-time"} |
finished_at | Yes | {"type":["string","null"],"format":"date-time"} |
template | Yes | The exact saved template version this job rendered. Null for raw HTML or an inline stateless template. {"type":["object","null"],"properties":{"id":{"$ref":"#/components/schemas/TemplateId"},"version":{"type":"integer","minimum":1}},"required":["id","version"],"additionalProperties":false} |
credit | Yes | What happened to the credit this job holds: "reserved" while it is queued or running, "settled" once it succeeded, "released" once it failed or was cancelled, and "none" when the job was never billed. {"type":"string","enum":["reserved","settled","released","none"]} |
artifact | Yes | {"type":["object","null"],"properties":{"media_type":{"const":"application/pdf"},"byte_size":{"type":"integer","minimum":0},"sha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"expires_at":{"type":"string","format":"date-time","description":"When retention ends and the file stops being served."},"removed_reason":{"type":["string","null"],"enum":["expired","deleted",null]}},"required":["media_type","byte_size","sha256","expires_at","removed_reason"],"additionalProperties":false} |
error | Yes | {"type":["object","null"],"properties":{"category":{"type":"string","enum":["invalid_input","unsafe_asset","resource_limit","timeout","renderer_failure","internal_failure"]},"code":{"type":"string"}},"required":["category","code"],"additionalProperties":false} |
Schema rules: {"type":"object","additionalProperties":false}.
PdfHistoryPage
One bounded page of an account's render history, newest first.
| Field | Required | Description and type |
|---|---|---|
items | Yes | {"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/PdfHistoryItem"}} |
next_cursor | Yes | Pass as the cursor query parameter to read the next page. Null on the last page. {"type":["string","null"],"maxLength":128} |
Schema rules: {"type":"object","additionalProperties":false}.
ImageHistoryItem
One job in a bounded account history — the subset of ImageJob a history page lists. Never carries a download link, since a history page can name many jobs at once.
| Field | Required | Description and type |
|---|---|---|
id | Yes | {"type":"string","pattern":"^image_[0-9a-f]{32}$"} |
status | Yes | {"type":"string","enum":["queued","running","succeeded","failed","cancelled"]} |
created_at | Yes | {"type":"string","format":"date-time"} |
finished_at | Yes | {"type":["string","null"],"format":"date-time"} |
template | Yes | The exact saved template version this job rendered. Null for raw HTML or an inline stateless template. {"type":["object","null"],"properties":{"id":{"$ref":"#/components/schemas/TemplateId"},"version":{"type":"integer","minimum":1}},"required":["id","version"],"additionalProperties":false} |
credit | Yes | What happened to the credit this job holds: "reserved" while it is queued or running, "settled" once it succeeded, "released" once it failed or was cancelled, and "none" when the job was never billed. {"type":"string","enum":["reserved","settled","released","none"]} |
artifact | Yes | {"type":["object","null"],"properties":{"media_type":{"type":"string","enum":["image/png","image/jpeg","image/webp"]},"byte_size":{"type":"integer","minimum":0},"sha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"expires_at":{"type":"string","format":"date-time","description":"When retention ends and the file stops being served."},"removed_reason":{"type":["string","null"],"enum":["expired","deleted",null]}},"required":["media_type","byte_size","sha256","expires_at","removed_reason"],"additionalProperties":false} |
error | Yes | {"type":["object","null"],"properties":{"category":{"type":"string","enum":["invalid_input","unsafe_asset","resource_limit","timeout","renderer_failure","internal_failure"]},"code":{"type":"string"}},"required":["category","code"],"additionalProperties":false} |
Schema rules: {"type":"object","additionalProperties":false}.
ImageHistoryPage
One bounded page of an account's image history, newest first.
| Field | Required | Description and type |
|---|---|---|
items | Yes | {"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/ImageHistoryItem"}} |
next_cursor | Yes | Pass as the cursor query parameter to read the next page. Null on the last page. {"type":["string","null"],"maxLength":128} |
Schema rules: {"type":"object","additionalProperties":false}.
NewApiKey
What a new key is named.
| Field | Required | Description and type |
|---|---|---|
name | Yes | 1 to 100 printable ASCII characters. {"type":"string","minLength":1,"maxLength":100,"pattern":"^(?=.*[^ ])[ -~]+$"} |
Schema rules: {"type":"object","additionalProperties":false}.
ApiKey
One key, without its secret.
| Field | Required | Description and type |
|---|---|---|
id | Yes | The key's public identifier: "key*" followed by 32 lowercase hexadecimal characters. {"type":"string","pattern":"^key*[0-9a-f]{32}$"} |
name | Yes | {"type":"string"} |
display_prefix | Yes | The first characters of the secret, enough to tell keys apart in a list. {"type":"string"} |
status | Yes | {"type":"string","enum":["active","revoked"]} |
created_at | Yes | {"type":"string","format":"date-time"} |
revoked_at | Yes | {"type":["string","null"],"format":"date-time"} |
last_used_at | Yes | {"type":["string","null"],"format":"date-time"} |
monthly_overage_cap | Yes | The most overage credits this key may take on per calendar month (UTC). Null means the key sets no cap of its own. {"type":["integer","null"],"minimum":1} |
Schema rules: {"type":"object","additionalProperties":false}.
ApiKeyUpdate
What an update may change about a key.
| Field | Required | Description and type |
|---|---|---|
monthly_overage_cap | No | The most overage credits this key may take on per calendar month (UTC), or null to clear the cap. {"type":["integer","null"],"minimum":1} |
Schema rules: {"type":"object","additionalProperties":false}.
ApiKeyWithSecret
One key exactly as creation answers it. This is the only shape that ever carries the secret.
| Field | Required | Description and type |
|---|---|---|
id | Yes | The key's public identifier: "key*" followed by 32 lowercase hexadecimal characters. {"type":"string","pattern":"^key*[0-9a-f]{32}$"} |
name | Yes | {"type":"string"} |
secret | Yes | The full secret: "thirdssk_v1" followed by 64 lowercase hexadecimal characters. It is shown here once and never again. {"type":"string","pattern":"^thirds_sk_v1_[0-9a-f]{64}$"} |
display_prefix | Yes | The first characters of the secret, enough to tell keys apart in a list. {"type":"string"} |
status | Yes | {"type":"string","enum":["active"]} |
created_at | Yes | {"type":"string","format":"date-time"} |
Schema rules: {"type":"object","additionalProperties":false}.
ApiKeyList
One bounded page of the account's key history.
| Field | Required | Description and type |
|---|---|---|
data | Yes | {"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/ApiKey"}} |
next_cursor | Yes | {"type":["string","null"],"maxLength":128} |
Schema rules: {"type":"object","additionalProperties":false}.
NewWebhook
What creating a webhook destination needs.
| Field | Required | Description and type |
|---|---|---|
url | Yes | The HTTPS URL deliveries are sent to. It must use port 443, carry no credentials, and resolve to public addresses only; private, loopback, link-local, metadata, and reserved destinations are refused, at registration and again on every delivery connection. {"type":"string","format":"uri","maxLength":2048} |
events | No | The terminal render events this destination receives. A body without this field selects every event. {"type":"array","minItems":1,"maxItems":3,"uniqueItems":true,"items":{"type":"string","enum":["render.cancelled","render.failed","render.succeeded"]}} |
Schema rules: {"type":"object","additionalProperties":false}.
WebhookFields
| Field | Required | Description and type |
|---|---|---|
id | Yes | The destination's public identifier: "wh*" followed by 32 lowercase hexadecimal characters. {"type":"string","pattern":"^wh*[0-9a-f]{32}$"} |
url | Yes | {"type":"string","format":"uri","maxLength":2048} |
events | Yes | The selected events, always sorted and distinct. {"type":"array","minItems":1,"maxItems":3,"uniqueItems":true,"items":{"type":"string","enum":["render.cancelled","render.failed","render.succeeded"]}} |
status | Yes | {"type":"string","enum":["enabled","disabled"]} |
disabled_reason | Yes | Why a disabled destination is disabled: the owner asked, or delivery failed repeatedly. Null while the destination is enabled. {"type":["string","null"],"enum":["owner_request","delivery_failure",null]} |
display_prefix | Yes | The first characters of the signing secret, enough to tell destinations apart in a list. {"type":"string"} |
created_at | Yes | {"type":"string","format":"date-time"} |
previous_secret_expires_at | Yes | Set while a rotation overlap is running: until this instant every delivery also carries a signature made with the previous secret. {"type":["string","null"],"format":"date-time"} |
Schema rules: {"type":"object"}.
Webhook
One webhook destination, without its signing secret.
Schema rules: {"allOf":[{"$ref":"#/components/schemas/WebhookFields"}],"unevaluatedProperties":false}.
WebhookWithSecret
One webhook destination exactly as creation and rotation answer it. This is the only shape that ever carries the signing secret.
Schema rules: {"allOf":[{"$ref":"#/components/schemas/WebhookFields"},{"type":"object","properties":{"secret":{"type":"string","pattern":"^thirds_whsec_[0-9a-f]{64}$","description":"The full signing secret: \"thirds_whsec_\" followed by 64 lowercase hexadecimal characters. It is shown here once and never again; store it and verify every delivery's Thirds-Signature header with it."}},"required":["secret"]}],"unevaluatedProperties":false}.
WebhookList
Every webhook destination the account holds. The cap is ten, so the list is never paged.
| Field | Required | Description and type |
|---|---|---|
data | Yes | {"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/Webhook"}} |
Schema rules: {"type":"object","additionalProperties":false}.
WebhookReplay
What one replay request did.
| Field | Required | Description and type |
|---|---|---|
replayed | Yes | How many failed events returned to the queue with a fresh attempt budget. {"type":"integer","minimum":0} |
Schema rules: {"type":"object","additionalProperties":false}.
ErrorEnvelope
The one error shape every backend response uses.
| Field | Required | Description and type |
|---|---|---|
error | Yes | {"type":"object","properties":{"code":{"type":"string","enum":["account_suspended","account_concurrency_limited","download_expired","idempotency_conflict","internal_error","invalid_cursor","invalid_request","job_not_finished","key_concurrency_limited","key_limit_reached","method_not_allowed","not_found","overloaded","rate_limited","request_headers_too_large","request_too_large","template_data_collection_limit","template_data_depth_limit","template_data_invalid","template_data_limit","template_depth_limit","template_evaluation_error","template_invalid_filter_input","template_missing_data","template_output_limit","template_not_found","template_schema_complexity","template_schema_draft_unsupported","template_schema_invalid","template_schema_too_large","template_source_limit","template_syntax_error","template_timeout","template_version_changed","template_work_limit","unauthorized","unsupported_media_type","webhook_limit_reached"],"description":"A fixed, machine-readable error code."},"message":{"type":"string","description":"A fixed, human-readable message."},"request_id":{"type":"string","format":"uuid","description":"The identifier this answer also carries in its x-request-id header."},"details":{"type":"array","description":"Present on a validation failure. Schema failures return at most 16 entries with bounded data paths and fixed reasons. Values from the request are never repeated.","items":{"$ref":"#/components/schemas/FieldDetail"}},"retry":{"$ref":"#/components/schemas/RetryInfo"}},"required":["code","message","request_id"],"additionalProperties":false} |
Schema rules: {"type":"object","additionalProperties":false}.
Headers and security
XRequestId
{
"description": "The UUID that identifies this request and matches error.request_id on an error response.",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
}
bearerAuth
{
"type": "http",
"scheme": "bearer",
"description": "An API key's secret, sent as \"Authorization: Bearer thirds_sk_v1_...\"."
}