openapi: 3.1.0
info:
title: thirds.ai API
version: 1.0.0
description: Turn one design into content at scale. Create branded images and PDFs for your campaigns and clients. Automate
each new version through our API or your AI tools. Render saved templates with new data, or send HTML directly. Every
error uses one envelope, every response carries an x-request-id header, and every JSON request body rejects fields it
does not expect.
servers:
- url: https://thirds.ai
paths:
/v1/webhooks/{webhook_id}/test:
post:
summary: Send a signed test event
description: Queue one sample render.succeeded event through the normal delivery worker. It spends no credits and creates
no render. A recorded result schedules no retry, and tests cannot be replayed. Recovery can resend an unfinished attempt
with the same event ID. Disabled destinations can be tested. One test per destination can wait at a time; completed
tests are limited to one per minute. The destination signing secrets and egress policy apply.
operationId: testWebhook
security:
- bearerAuth: []
- sessionCookie: []
parameters:
- name: webhook_id
in: path
required: true
description: The destination's public identifier, such as "wh_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^wh_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'401':
description: A valid session or API key is required.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'403':
$ref: '#/components/responses/AccountSuspended'
'429':
description: The request limit was reached. Retry after the time in Retry-After.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: The request failed internally.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: No destination with this ID belongs to this account.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'200':
description: The test attempt finished.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookTest'
'202':
description: The test is still queued or in flight. Read the delivery log for its result before sending another.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookTest'
/v1/webhooks/{webhook_id}/deliveries:
get:
summary: Read recent webhook attempts
description: Return up to 20 newest attempts for this destination, in reverse time order. Attempts expire after 30 days.
No request or response body is stored.
operationId: listWebhookDeliveries
security:
- bearerAuth: []
- sessionCookie: []
parameters:
- name: webhook_id
in: path
required: true
description: The destination's public identifier, such as "wh_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^wh_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'401':
description: A valid session or API key is required.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'403':
$ref: '#/components/responses/AccountSuspended'
'429':
description: The request limit was reached. Retry after the time in Retry-After.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: The request failed internally.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: No destination with this ID belongs to this account.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'200':
description: The latest attempts for this destination.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookDeliveryList'
/v1/me/identities/email:
post:
summary: Add an email to an account without one
description: Requires a signed-in browser session and its CSRF token. Sends a one-use verification link bound to that
account and exact session. Open the link in the same browser while signed in, then press Complete sign in. POST /v1/auth/email/confirm
proves the address and grants the current free monthly credits once. An account with an email cannot change it through
this route.
operationId: requestAccountEmail
security:
- sessionCookie: []
parameters:
- name: x-csrf-token
in: header
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AccountEmailRequest'
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'401':
description: A live browser session is required. Expired, revoked, and suspended sessions are refused.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'403':
description: The CSRF token is missing or does not match this browser session.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'429':
description: The request limit was reached. Retry after the time in Retry-After.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: The request failed internally.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'202':
description: The request was accepted. This does not confirm mail delivery or whether another account holds the
address.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/AccountEmailAccepted'
'400':
description: The JSON request is invalid or has unknown fields.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'409':
description: This account already has an email address. The code is email_already_set.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'422':
description: The email address is invalid. The code is invalid_email.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'503':
description: Email delivery is not configured. The code is auth_unavailable.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/image-assets/preview:
post:
summary: Preview a URL image
description: 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.
operationId: previewImageUrl
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
maxLength: 4096
required:
- url
additionalProperties: false
responses:
'200':
description: The verified image bytes and dimensions after orientation.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
x-image-width:
schema:
type: integer
x-image-height:
schema:
type: integer
content:
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'422':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/image-assets:
post:
summary: Upload an image
description: 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.
operationId: uploadImage
security:
- bearerAuth: []
requestBody:
required: true
content:
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
responses:
'201':
description: The image is stored.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/UploadedImage'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/Error'
'404':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'422':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/image-assets/{id}/content:
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^asset_[a-f0-9]{32}$
get:
summary: Read an owned image
description: 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.
operationId: readImageContent
security:
- bearerAuth: []
responses:
'200':
description: The exact image bytes.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
font/woff2:
schema:
type: string
format: binary
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/Error'
'404':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'422':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/openapi.json:
get:
summary: Get the OpenAPI document
description: Return this API contract as a JSON document.
operationId: getOpenApiDocument
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The OpenAPI document.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
type: object
/v1/public-stats:
get:
summary: Read measured public facts
description: Return the count of successful customer and playground files, the reviewed gallery template count, and
the weakest uptime and current status from the public status page. The response is cached for five minutes. Customer
content and account data never enter this response.
operationId: getPublicStats
responses:
'200':
description: The measured public facts. Uptime is unavailable when the status provider does not answer.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
cache-control:
schema:
type: string
description: The five-minute public cache policy.
content:
application/json:
schema:
$ref: '#/components/schemas/PublicStats'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/testimonials:
post:
summary: Offer a testimonial for private review
description: Store one visitor's name, company, quote, and permission for owner review. Sending this request never publishes
the quote. A same-site browser origin and a fresh CAPTCHA pass protect the form.
operationId: submitTestimonial
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TestimonialRequest'
responses:
'201':
description: The testimonial is stored for private owner review.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TestimonialSubmission'
'400':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/pdf:
post:
summary: Create a PDF
description: 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.
operationId: createPdf
security:
- bearerAuth: []
parameters:
- name: Idempotency-Key
in: header
required: false
description: 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.
schema:
type: string
minLength: 1
maxLength: 255
pattern: ^[ -~]+$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PdfRequest'
examples:
raw_html:
summary: Raw HTML
value:
html:
Quarterly report
Ready to share.
stateless_template:
summary: Stateless template with one data object
value:
html: '{{ report.title }}
Total: {{ report.total | currency }}
'
data:
report:
title: Quarterly report
total: 125000
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'200':
description: The job is terminal. Check status for succeeded, failed, or cancelled; HTTP 200 does not mean the render
succeeded.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
Idempotency-Replayed:
description: '"true" when this answer replays a job an earlier request with the same idempotency key already
created.'
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/PdfJob'
'202':
description: The job was accepted and is still queued or running. Poll GET /v1/pdf/{id} for the terminal result.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
Idempotency-Replayed:
description: '"true" when this answer replays a job an earlier request with the same idempotency key already
created.'
schema:
type: string
Location:
description: The job's own status URL, /v1/pdf/{id} — where to poll for the terminal result.
schema:
type: string
Retry-After:
description: How soon polling the status URL is worthwhile, in seconds.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/PdfJob'
'400':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'402':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'409':
description: This idempotency key was already used for a different logical request, or the selected saved-template
version changed before admission completed.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'413':
description: The request is larger than this build accepts.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'415':
description: The request did not carry a JSON content type.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'429':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
minimum: 1
maximum: 60
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'503':
description: The service is at capacity.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
minimum: 1
maximum: 60
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
get:
summary: List render history
description: '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.'
operationId: listPdfHistory
security:
- bearerAuth: []
parameters:
- name: q
in: query
description: Case-insensitive search of references, template names, and file types.
schema:
type: string
maxLength: 200
pattern: ^[^\u0000-\u001f\u007f-\u009f]*$
- name: limit
in: query
required: false
description: 1 to 100. Defaults to 20. A value outside this range is clamped rather than refused.
schema:
type: integer
default: 20
- name: cursor
in: query
required: false
description: An opaque value from an earlier page's next_cursor. A cursor that cannot be read is refused with 400.
schema:
type: string
maxLength: 128
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: One page of the account's render history.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/PdfHistoryPage'
'400':
description: The limit parameter was not a whole number, the cursor could not be read, or the query held an unknown
parameter.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/image:
post:
summary: Create a static image
description: 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.
operationId: createImage
security:
- bearerAuth: []
parameters:
- name: Idempotency-Key
in: header
required: false
schema:
type: string
minLength: 1
maxLength: 255
pattern: ^[ -~]+$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ImageRequest'
examples:
png:
value:
html: Quarterly report
image:
format: png
width: 1280
height: 720
transparent: false
responses:
'200':
description: The job is terminal. Check status for succeeded, failed, or cancelled; HTTP 200 does not mean the render
succeeded.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ImageJob'
'202':
description: The image job is durable and remains queued or running.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
Location:
schema:
type: string
Retry-After:
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ImageJob'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'402':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'409':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
get:
summary: List image history
description: '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.'
operationId: listImageHistory
security:
- bearerAuth: []
parameters:
- name: q
in: query
description: Case-insensitive search of references, template names, and file types.
schema:
type: string
maxLength: 200
pattern: ^[^\u0000-\u001f\u007f-\u009f]*$
- name: limit
in: query
required: false
description: 1 to 100. Defaults to 20. A value outside this range is clamped rather than refused.
schema:
type: integer
default: 20
- name: cursor
in: query
required: false
description: An opaque value from an earlier page's next_cursor. A cursor that cannot be read is refused with 400.
schema:
type: string
maxLength: 128
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: One page of the account's image history.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ImageHistoryPage'
'400':
description: The limit parameter was not a whole number, the cursor could not be read, or the query held an unknown
parameter.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/image/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^image_[0-9a-f]{32}$
get:
summary: Get a static image's status
operationId: getImage
security:
- bearerAuth: []
responses:
'200':
description: The image job's current status.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ImageJob'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
delete:
summary: Delete an image file or an unsuccessful render
description: 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.
operationId: deleteImage
security:
- bearerAuth: []
responses:
'200':
description: The image job after file or history removal.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ImageJob'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'409':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/image-packs:
post:
summary: Create an image pack
description: 'Render one saved template version at every requested size: original for the version''s own canvas, a saved
size id, a preset slug, or a WxH custom size adapted from the version''s original. Creates one normal image job per
size. A size that cannot be adapted fails immediately with resize_source_unsupported; the other sizes still run. The
ledger reserves one credit per job, settles it on success, and releases it on failure.'
operationId: createImagePack
security:
- bearerAuth: []
parameters:
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 255
pattern: ^[ -~]+$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ImagePackCreate'
examples:
socialSet:
value:
template_id: tpl_2f6a2f6a2f6a2f6a2f6a2f6a2f6a2f6a
version: 3
data:
headline: Autumn sale
format: png
sizes:
- square-post
- story
- 1200x628
responses:
'201':
description: The pack was created, or an earlier request with the same idempotency key and body is replayed.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ImagePack'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'409':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/image-packs/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^pack_[0-9a-f]{32}$
get:
summary: Get an image pack's status
description: Returns the pack with each member's own state and, once any member has succeeded, a signed archive_url
for the ZIP of every succeeded file.
operationId: getImagePack
security:
- bearerAuth: []
responses:
'200':
description: The pack's current status.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ImagePack'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/image-packs/{id}/retry:
post:
summary: Retry an image pack's failed members
description: Creates one new job for every member whose last attempt failed or that never reached a job. A queued, running,
or already succeeded member is left untouched.
operationId: retryImagePack
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^pack_[0-9a-f]{32}$
responses:
'200':
description: The pack after retry, with a new job for each member that was retried.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ImagePackRetry'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/carousels:
post:
summary: Create a carousel
description: Render selected pages of one saved template version as images, in the requested order. Creates one normal
image job per page. The ledger reserves one credit per job, settles it on success, and releases it on failure. A successful
PDF of the same pages still costs one credit through POST /v1/pdf.
operationId: createCarousel
security:
- bearerAuth: []
parameters:
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 255
pattern: ^[ -~]+$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CarouselCreate'
examples:
launchSlides:
value:
template_id: tpl_2f6a2f6a2f6a2f6a2f6a2f6a2f6a2f6a
version: 1
data:
brand: Northline Studio
format: png
pages:
- cover
- message
- offer
- proof
- action
responses:
'201':
description: The carousel was created, or an earlier request with the same idempotency key and body is replayed.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Carousel'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'409':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/carousels/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^carousel_[0-9a-f]{32}$
get:
summary: Get a carousel's status
description: Returns the carousel with each member's own state and, once any member has succeeded, a signed archive_url
for the ZIP of every succeeded file.
operationId: getCarousel
security:
- bearerAuth: []
responses:
'200':
description: The carousel's current status.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Carousel'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/carousels/{id}/retry:
post:
summary: Retry a carousel's failed members
description: Creates one new job for every member whose last attempt failed or that never reached a job. A queued, running,
or already succeeded member is left untouched.
operationId: retryCarousel
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^carousel_[0-9a-f]{32}$
responses:
'200':
description: The carousel after retry, with a new job for each member that was retried.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/CarouselRetry'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/batches:
post:
summary: Create a render batch
description: 'Render one saved template version once per data row as PDF, PNG, JPEG, or WebP. The account plan sets
how many rows one run accepts: 10 on Free and a pack-only account, 50 on Starter, 100 on Growth, and 200 on Scale.
A run over that limit answers 400 batch_row_limit with error.plan_limit. Validates every row against the version''s
schema before any job exists. Creates one normal render job per row as account and key concurrency allow. Remaining
rows stay pending until a later retry or the background drain. The ledger reserves one credit per admitted job, settles
it on success, and releases it on failure or cancellation. quoted_credits is the row count, not a discount.'
operationId: createBatch
security:
- bearerAuth: []
parameters:
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 255
pattern: ^[ -~]+$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BatchCreate'
examples:
certificates:
value:
template_id: tpl_2f6a2f6a2f6a2f6a2f6a2f6a2f6a2f6a
version: 1
format: pdf
rows:
- issuer: Fieldnote Learning
learner: Alex Morgan
course: Clear plans
date: 4 September 2026
- issuer: Fieldnote Learning
learner: Theo Lee
course: Clear plans
date: 4 September 2026
responses:
'201':
description: The batch was created, or an earlier request with the same idempotency key and body is replayed.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Batch'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'409':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/batches/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^batch_[0-9a-f]{32}$
get:
summary: Get a render batch's status
description: Returns the batch with each row's own state and, once any row has succeeded, a signed archive_url for the
ZIP of every succeeded file. GET never starts billed work.
operationId: getBatch
security:
- bearerAuth: []
responses:
'200':
description: The batch's current status.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Batch'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/batches/{id}/retry:
post:
summary: Retry a batch's failed, cancelled, or pending rows
description: Creates one new job for every row whose last attempt failed or was cancelled, and admits still-pending
rows as concurrency allows. A queued, running, or already succeeded row is left untouched. Retry never bills a row
that already succeeded.
operationId: retryBatch
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^batch_[0-9a-f]{32}$
responses:
'200':
description: The batch after retry, with a new job for each row that was retried.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/BatchRetry'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/batches/{id}/cancel:
post:
summary: Cancel a batch's pending and queued rows
description: Stops rows that have no job yet and cancels queued jobs. Running jobs finish. Succeeded files stay downloadable.
Cancelled and failed rows cost nothing.
operationId: cancelBatch
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^batch_[0-9a-f]{32}$
responses:
'200':
description: The batch after cancel.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Batch'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/template-builds:
post:
summary: Start a template build
description: '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. Free monthly credits cannot pay for AI work: an account
whose trial, pack, and subscription credits cannot cover the price answers 402 ai_needs_paid_credits and holds nothing.
An AI message often takes a minute or two.'
operationId: createTemplateBuild
security:
- bearerAuth: []
parameters:
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 255
pattern: ^[ -~]+$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateBuildCreate'
responses:
'200':
description: An earlier matching request was replayed.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateBuild'
'202':
description: The durable build was accepted.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateBuild'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'402':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'409':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/template-builds/{build_id}:
parameters:
- name: build_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateBuildId'
get:
summary: Get a template build
operationId: getTemplateBuild
security:
- bearerAuth: []
responses:
'200':
description: The current draft and latest message facts.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateBuild'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/template-builds/{build_id}/messages:
parameters:
- name: build_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateBuildId'
get:
summary: List template chat messages
operationId: listTemplateMessages
security:
- bearerAuth: []
responses:
'200':
description: The user-visible messages in chat order. One chat holds at most 100 messages.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/TemplateMessage'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
post:
summary: Edit the working template
description: 'Run one later AI edit. A text or HTML edit costs 25 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. Free monthly credits cannot pay for AI work: an account
whose trial, pack, and subscription credits cannot cover the price answers 402 ai_needs_paid_credits and holds nothing.
An AI message often takes a minute or two.'
operationId: editTemplateBuild
security:
- bearerAuth: []
parameters:
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 255
pattern: ^[ -~]+$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateMessageCreate'
responses:
'200':
description: An earlier matching edit was replayed.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateBuild'
'202':
description: The durable edit was accepted.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateBuild'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'402':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'409':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/template-builds/{build_id}/draft:
parameters:
- name: build_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateBuildId'
get:
summary: Get the working template
description: Read the current draft as editable source, sample data, schema, and output. The build has no draft until
its first message succeeds.
operationId: getTemplateBuildDraft
security:
- bearerAuth: []
responses:
'200':
description: The current working template.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateBuildDraft'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/template-builds/{build_id}/messages/{message_id}/draft:
parameters:
- name: build_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateBuildId'
- name: message_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateMessageId'
get:
summary: Get a completed message draft
description: 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.
operationId: getTemplateMessageDraft
security:
- bearerAuth: []
responses:
'200':
description: The final draft. Cache-Control is private, no-store.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateBuildDraft'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/template-builds/{build_id}/messages/{message_id}:
parameters:
- name: build_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateBuildId'
- name: message_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateMessageId'
delete:
summary: Cancel a pending template message
operationId: cancelTemplateMessage
security:
- bearerAuth: []
responses:
'200':
description: The message was cancelled and its reservation was released.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateMessage'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'409':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/template-builds/{build_id}/publish:
parameters:
- name: build_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateBuildId'
post:
summary: Publish the current working template
description: Explicitly publish the current valid draft as one normal immutable saved-template version. Repeating publication
of the same draft returns the same version.
operationId: publishTemplateBuild
security:
- bearerAuth: []
responses:
'200':
description: This draft was already published.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplatePublication'
'201':
description: The immutable template version was published.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplatePublication'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'409':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/templates:
post:
summary: Create a saved template
operationId: createTemplate
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateWriteRequest'
responses:
'201':
description: The created template, including version 1 source and schema.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateDetail'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'413':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
get:
summary: List saved templates
description: 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.
operationId: listTemplates
security:
- bearerAuth: []
parameters:
- name: status
in: query
schema:
type: string
enum:
- active
- archived
- all
default: all
- name: q
in: query
description: Search names on the server.
schema:
type: string
maxLength: 120
pattern: ^[^\u0000-\u001f\u007f-\u009f]*$
- name: tag
in: query
description: Match one exact tag on the server.
schema:
type: string
minLength: 1
maxLength: 40
pattern: ^[^\u0000-\u001f\u007f-\u009f]+$
- name: limit
in: query
schema:
type: integer
default: 20
- name: cursor
in: query
schema:
type: string
maxLength: 128
responses:
'200':
description: One page of saved-template summaries.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplatePage'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/templates/preview:
post:
summary: Preview template data as HTML
description: 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.
operationId: previewTemplate
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TemplatePreviewRequest'
responses:
'200':
description: 'The expanded HTML. This response uses Cache-Control: private, no-store.'
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplatePreview'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'503':
$ref: '#/components/responses/Error'
/v1/templates/adapt:
post:
summary: Adapt one HTML source to many canvas sizes
description: Deterministically adapt one canonical HTML source to up to 20 target canvas sizes. This is free, needs
a session or key, and uses the preview rate limit. It does not save a template, create a render, or spend credits.
The response gives the adapted source per size, or a safe error code for that size.
operationId: adaptTemplate
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateAdaptRequest'
responses:
'200':
description: One entry per requested size, each with its adapted source or a safe error code.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateAdaptResponse'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'504':
$ref: '#/components/responses/Error'
/v1/templates/{template_id}:
parameters:
- name: template_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateId'
get:
summary: Get a saved template
operationId: getTemplate
security:
- bearerAuth: []
responses:
'200':
description: The template and its latest immutable version.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateDetail'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
patch:
summary: Rename a saved template
description: 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.
operationId: renameTemplate
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateRename'
responses:
'200':
description: The template with its new name.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateSummary'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
delete:
summary: Archive a saved template
description: Archive one owned template. The operation is idempotent. Archived templates and versions remain readable,
but they cannot receive another version or create another render.
operationId: archiveTemplate
security:
- bearerAuth: []
responses:
'200':
description: The template is archived.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateSummary'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/templates/{template_id}/versions:
parameters:
- name: template_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateId'
post:
summary: Create a template version
description: Edit a template by publishing one complete new immutable source and optional schema. Existing versions
never change. Archived templates cannot receive another version.
operationId: createTemplateVersion
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateVersionWriteRequest'
responses:
'201':
description: The new immutable version, including source and schema.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateVersionDetail'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
get:
summary: List template versions
description: List immutable versions newest first. Source and schema are omitted. Archived template versions remain
readable.
operationId: listTemplateVersions
security:
- bearerAuth: []
parameters:
- name: limit
in: query
schema:
type: integer
default: 20
- name: cursor
in: query
schema:
type: string
maxLength: 32
responses:
'200':
description: One page of immutable version summaries.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateVersionPage'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/templates/{template_id}/versions/{version}:
parameters:
- name: template_id
in: path
required: true
schema:
$ref: '#/components/schemas/TemplateId'
- name: version
in: path
required: true
schema:
type: integer
minimum: 1
get:
summary: Get a template version
operationId: getTemplateVersion
security:
- bearerAuth: []
responses:
'200':
description: The exact immutable template version.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TemplateVersionDetail'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/brand-kits:
post:
summary: Create a brand kit
description: 'Create one mutable account-owned brand kit. Logos and fonts are uploaded through the kit''s asset route
after creation. The account plan sets how many kits the account can keep: 1 on Free, a pack-only account, and Starter,
5 on Growth, and 20 on Scale. Every kit of the account counts, archived kits too. A kit over that limit answers 409
brand_kit_limit with error.plan_limit. An account above its limit after a downgrade keeps every kit and can still
change and archive them.'
operationId: createBrandKit
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BrandKitWrite'
responses:
'201':
description: The brand kit was created.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/BrandKitDetail'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'409':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'422':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
get:
summary: List brand kits
description: 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.
operationId: listBrandKits
security:
- bearerAuth: []
parameters:
- name: status
in: query
schema:
type: string
enum:
- active
- archived
- all
default: all
- name: limit
in: query
schema:
type: integer
default: 20
- name: cursor
in: query
schema:
type: string
maxLength: 128
responses:
'200':
description: One page of brand-kit summaries.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/BrandKitPage'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/brand-kits/{brand_kit_id}:
parameters:
- name: brand_kit_id
in: path
required: true
schema:
$ref: '#/components/schemas/BrandKitId'
get:
summary: Get a brand kit
description: Return one owned kit and its current logo and font metadata. Replaced assets remain private durable content
but are omitted.
operationId: getBrandKit
security:
- bearerAuth: []
responses:
'200':
description: The brand kit and its current assets.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/BrandKitDetail'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
patch:
summary: Update a brand kit
description: Replace any supplied mutable brand values. Omitted fields stay unchanged; a null tone_guidance clears it.
Archived kits cannot change.
operationId: updateBrandKit
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BrandKitPatch'
responses:
'200':
description: The updated kit and its current assets.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/BrandKitDetail'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'422':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
delete:
summary: Archive a brand kit
description: Archive one owned kit. The operation is idempotent. The kit and every current or replaced asset stay durable
for already captured references.
operationId: archiveBrandKit
security:
- bearerAuth: []
responses:
'204':
description: The brand kit is archived.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/brand-kits/{brand_kit_id}/assets:
parameters:
- name: brand_kit_id
in: path
required: true
schema:
$ref: '#/components/schemas/BrandKitId'
post:
summary: Add a brand asset
description: 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.
operationId: createBrandAsset
security:
- bearerAuth: []
requestBody:
required: true
content:
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
font/woff2:
schema:
type: string
format: binary
responses:
'201':
description: The immutable asset was accepted and selected by the kit.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/BrandAsset'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'422':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/brand-kits/{brand_kit_id}/assets/{asset_id}:
parameters:
- name: brand_kit_id
in: path
required: true
schema:
$ref: '#/components/schemas/BrandKitId'
- name: asset_id
in: path
required: true
schema:
$ref: '#/components/schemas/BrandAssetId'
get:
summary: Read a brand asset
description: 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.
operationId: readBrandAsset
security:
- bearerAuth: []
responses:
'200':
description: The validated image or WOFF2 font bytes.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
font/woff2:
schema:
type: string
format: binary
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
put:
summary: Replace a brand asset
description: 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.
operationId: replaceBrandAsset
security:
- bearerAuth: []
requestBody:
required: true
content:
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
font/woff2:
schema:
type: string
format: binary
responses:
'201':
description: The new immutable current asset.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/BrandAsset'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'422':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/pdf/{id}:
get:
summary: Get a PDF's status
description: 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.
operationId: getPdf
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
description: The job's public identifier, such as "pdf_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^pdf_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The job's current status.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/PdfJob'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
delete:
summary: Delete a PDF file or an unsuccessful render
description: 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.
operationId: deletePdf
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
description: The job's public identifier, such as "pdf_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^pdf_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The job's current status after file or history removal.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/PdfJob'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'409':
description: The job is queued or running, so it cannot be deleted.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/downloads/{token}:
get:
summary: Download a finished PDF or image
description: '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.'
operationId: getDownload
parameters:
- name: token
in: path
required: true
description: The signed download link.
schema:
type: string
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The finished PDF or image, bounded by the 25 MiB artifact limit.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/pdf:
schema:
type: string
format: binary
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
'403':
description: The link is genuine but its time is over.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/image-packs/archive/{token}:
get:
summary: Download an image pack's ZIP archive
description: Stream a ZIP of one pack's succeeded members, in requested order with numbered filenames, plus manifest.json
recording that order and any failed sizes. The link carries its own authority, so no other credential is read.
operationId: getImagePackArchive
parameters:
- name: token
in: path
required: true
description: The signed archive link.
schema:
type: string
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The ZIP archive of succeeded members plus manifest.json.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/zip:
schema:
type: string
format: binary
'403':
description: The link is genuine but its time is over.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: The link is not valid, or the pack has no succeeded member to archive.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/carousels/archive/{token}:
get:
summary: Download a carousel's ZIP archive
description: Stream a ZIP of one carousel's succeeded members, in requested order with numbered filenames such as 01-cover.png,
plus manifest.json recording that order and any failed pages. The link carries its own authority, so no other credential
is read.
operationId: getCarouselArchive
parameters:
- name: token
in: path
required: true
description: The signed archive link.
schema:
type: string
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The ZIP archive of succeeded members plus manifest.json.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/zip:
schema:
type: string
format: binary
'403':
description: The link is genuine but its time is over.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: The link is not valid, or the carousel has no succeeded member to archive.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/batches/archive/{token}:
get:
summary: Download a batch's ZIP archive
description: Stream a ZIP of one batch's succeeded rows, in requested order with numbered filenames such as 001.pdf,
plus manifest.json recording that order and any failed or cancelled rows. The link carries its own authority, so no
other credential is read. The manifest never includes customer data.
operationId: getBatchArchive
parameters:
- name: token
in: path
required: true
description: The signed archive link.
schema:
type: string
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The ZIP archive of succeeded rows plus manifest.json.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/zip:
schema:
type: string
format: binary
'403':
description: The link is genuine but its time is over.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: The link is not valid, or the batch has no succeeded row to archive.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/keys:
post:
summary: Create an API key
description: Create one API key for the authenticated account. The full secret is returned in this response and never
again; only its verifier is kept.
operationId: createApiKey
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewApiKey'
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'201':
description: The key was created. This is the only response that ever carries the secret.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKeyWithSecret'
'400':
description: The name was empty, longer than 100 characters, or held a character that is not printable ASCII.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'413':
description: The request body is larger than 16 KiB.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'415':
description: The request did not carry a JSON content type.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'409':
description: The account already holds ten active keys. Revoke one before creating another.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
get:
summary: List API keys
description: 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.
operationId: listApiKeys
security:
- bearerAuth: []
parameters:
- name: limit
in: query
required: false
description: The page size. Defaults to 20 and must be from 1 through 100.
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: cursor
in: query
required: false
description: An opaque value from an earlier page's next_cursor.
schema:
type: string
maxLength: 128
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: One page of the account's key history.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKeyList'
'400':
description: The page size or cursor is not valid.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/keys/{key_id}:
delete:
summary: Revoke an API key
description: 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.
operationId: revokeApiKey
security:
- bearerAuth: []
parameters:
- name: key_id
in: path
required: true
description: The key's public identifier, such as "key_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^key_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The key, now revoked.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKey'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
patch:
summary: Set or clear an API key's monthly overage cap
description: '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.'
operationId: updateApiKey
security:
- bearerAuth: []
parameters:
- name: key_id
in: path
required: true
description: The key's public identifier, such as "key_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^key_[0-9a-f]{32}$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKeyUpdate'
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The key with its new cap.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKey'
'400':
description: The cap was zero, negative, not a whole number, or the body held a field this operation does not know.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'413':
description: The request body is larger than 16 KiB.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'415':
description: The request did not carry a JSON content type.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/webhooks:
post:
summary: Register a webhook destination
description: '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=,v1=": each v1 value is HMAC-SHA256 over the exact bytes "." 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.'
operationId: createWebhook
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewWebhook'
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'403':
$ref: '#/components/responses/AccountSuspended'
'201':
description: The destination was registered and is enabled. This is the only response, with rotation's, that ever
carries the signing secret.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookWithSecret'
'400':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'413':
description: The request body is larger than 16 KiB.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'415':
description: The request did not carry a JSON content type.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'409':
description: The account already holds ten webhook destinations. Delete one before registering another.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
get:
summary: List webhook destinations
description: List every webhook destination of the authenticated account, most recently created first. The signing secret
never appears here.
operationId: listWebhooks
security:
- bearerAuth: []
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'403':
$ref: '#/components/responses/AccountSuspended'
'200':
description: The account's destinations.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookList'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/webhooks/{webhook_id}:
get:
summary: Read a webhook destination
description: Read one destination of the authenticated account, without its signing secret.
operationId: getWebhook
security:
- bearerAuth: []
parameters:
- name: webhook_id
in: path
required: true
description: The destination's public identifier, such as "wh_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^wh_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'403':
$ref: '#/components/responses/AccountSuspended'
'200':
description: The destination.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Webhook'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
delete:
summary: Delete a webhook destination
description: Delete one destination of the authenticated account. Deliveries stop at once, and events not yet delivered
to it are dropped.
operationId: deleteWebhook
security:
- bearerAuth: []
parameters:
- name: webhook_id
in: path
required: true
description: The destination's public identifier, such as "wh_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^wh_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'403':
$ref: '#/components/responses/AccountSuspended'
'204':
description: The destination is deleted. There is no body.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/webhooks/{webhook_id}/rotate:
post:
summary: Rotate a webhook destination's signing secret
description: '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.'
operationId: rotateWebhookSecret
security:
- bearerAuth: []
parameters:
- name: webhook_id
in: path
required: true
description: The destination's public identifier, such as "wh_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^wh_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'403':
$ref: '#/components/responses/AccountSuspended'
'200':
description: The destination with its new secret. This is the only response, with creation's, that ever carries
the signing secret.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookWithSecret'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/webhooks/{webhook_id}/disable:
post:
summary: Disable a webhook destination
description: 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.
operationId: disableWebhook
security:
- bearerAuth: []
parameters:
- name: webhook_id
in: path
required: true
description: The destination's public identifier, such as "wh_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^wh_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'403':
$ref: '#/components/responses/AccountSuspended'
'200':
description: The destination, now disabled by owner request.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Webhook'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/webhooks/{webhook_id}/enable:
post:
summary: Enable a webhook destination
description: 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.
operationId: enableWebhook
security:
- bearerAuth: []
parameters:
- name: webhook_id
in: path
required: true
description: The destination's public identifier, such as "wh_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^wh_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'403':
$ref: '#/components/responses/AccountSuspended'
'200':
description: The destination, enabled again.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/Webhook'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/webhooks/{webhook_id}/replay:
post:
summary: Replay a webhook destination's failed events
description: 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.
operationId: replayWebhook
security:
- bearerAuth: []
parameters:
- name: webhook_id
in: path
required: true
description: The destination's public identifier, such as "wh_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^wh_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'403':
$ref: '#/components/responses/AccountSuspended'
'200':
description: How many failed events returned to the queue.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookReplay'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: 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.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
x-unmatched-v1-responses:
description: A request below /v1 that matches no operation receives the shared safe envelope. OpenAPI has no standard path
item for an unmatched route, so this extension records the fallback contract without claiming that a catch-all operation
exists.
'404':
$ref: '#/components/responses/NotFound'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
components:
headers:
XRequestId:
description: The UUID that identifies this request and matches error.request_id on an error response.
required: true
schema:
type: string
format: uuid
schemas:
AccountEmailRequest:
type: object
properties:
email:
type: string
next:
type: string
description: A safe local path after proof; defaults to /account.
required:
- email
additionalProperties: false
AccountEmailAccepted:
type: object
additionalProperties: false
WebhookTest:
type: object
properties:
status_code:
type:
- integer
- 'null'
minimum: 200
maximum: 599
delivered:
type: boolean
required:
- status_code
- delivered
additionalProperties: false
WebhookDelivery:
type: object
properties:
id:
type: string
pattern: ^wa_[0-9a-f]{32}$
event:
type: string
enum:
- render.succeeded
- render.failed
- render.cancelled
created_at:
type: string
format: date-time
status_code:
type:
- integer
- 'null'
minimum: 200
maximum: 599
description: Null when no HTTP status is received.
is_test:
type: boolean
description: True for a sample sent by the test action.
retry_count:
type: integer
minimum: 0
maximum: 7
required:
- id
- event
- created_at
- status_code
- retry_count
- is_test
additionalProperties: false
WebhookDeliveryList:
type: object
properties:
data:
type: array
maxItems: 20
items:
$ref: '#/components/schemas/WebhookDelivery'
required:
- data
additionalProperties: false
PublicStats:
type: object
description: Measured facts shown on the public sales site.
properties:
renders_served:
type: integer
minimum: 0
gallery_templates:
type: integer
minimum: 0
uptime_percent:
type:
- number
- 'null'
minimum: 0
maximum: 100
uptime_days:
type: integer
minimum: 0
status:
type: string
enum:
- operational
- degraded
- downtime
- maintenance
- unavailable
required:
- renders_served
- gallery_templates
- uptime_percent
- uptime_days
- status
additionalProperties: false
TestimonialRequest:
type: object
description: One private testimonial offer and the fresh browser check that protects it.
properties:
name:
type: string
minLength: 2
maxLength: 80
company:
type: string
minLength: 2
maxLength: 120
quote:
type: string
minLength: 20
maxLength: 1000
permission:
type: boolean
const: true
description: The visitor permits contact about the quote. This does not publish it.
captcha_token:
type: string
minLength: 1
maxLength: 2048
required:
- name
- company
- quote
- permission
- captcha_token
additionalProperties: false
TestimonialSubmission:
type: object
description: The private submission identifier returned after storage.
properties:
id:
type: string
format: uuid
required:
- id
additionalProperties: false
UploadedImage:
type: object
additionalProperties: false
required:
- id
- reference
- media_type
- byte_size
- width
- height
properties:
id:
type: string
pattern: ^asset_[a-f0-9]{32}$
reference:
type: string
pattern: ^asset://asset_[a-f0-9]{32}$
description: Private template reference. This is not a fetchable URL.
media_type:
type: string
enum:
- image/png
- image/jpeg
- image/webp
byte_size:
type: integer
minimum: 1
maximum: 10485760
width:
type: integer
minimum: 1
maximum: 7680
description: Display width after image orientation.
height:
type: integer
minimum: 1
maximum: 7680
description: Display height after image orientation.
FieldDetail:
type: object
description: 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.
properties:
field:
type: string
maxLength: 260
description: 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.
reason:
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.
description: A fixed, safe reason. It never repeats the value the caller sent.
line:
type: integer
minimum: 1
maximum: 1000000
description: The one-based template source line when the evaluator provides one within the published bound.
column:
type: integer
minimum: 1
maximum: 1000000
description: The one-based template source column when the evaluator provides one within the published bound.
required:
- field
- reason
additionalProperties: false
BrandKitId:
type: string
pattern: ^kit_[0-9a-f]{32}$
BrandAssetId:
type: string
pattern: ^asset_[0-9a-f]{32}$
BrandKitWrite:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 100
colours:
type: array
maxItems: 16
uniqueItems: true
items:
type: string
pattern: ^#[0-9A-Fa-f]{6}$
default: []
tone_guidance:
type:
- string
- 'null'
minLength: 1
maxLength: 500
required:
- name
additionalProperties: false
BrandKitPatch:
type: object
properties:
archived_at:
type: 'null'
description: Restore the item. Omit this field to keep its archive state.
name:
type: string
minLength: 1
maxLength: 100
colours:
type: array
maxItems: 16
uniqueItems: true
items:
type: string
pattern: ^#[0-9A-Fa-f]{6}$
tone_guidance:
type:
- string
- 'null'
minLength: 1
maxLength: 500
minProperties: 1
additionalProperties: false
BrandKitSummary:
type: object
properties:
id:
$ref: '#/components/schemas/BrandKitId'
name:
type: string
minLength: 1
maxLength: 100
colours:
type: array
maxItems: 16
uniqueItems: true
items:
type: string
pattern: ^#[0-9a-f]{6}$
tone_guidance:
type:
- string
- 'null'
minLength: 1
maxLength: 500
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
archived_at:
type:
- string
- 'null'
format: date-time
assets:
type: array
maxItems: 16
items:
$ref: '#/components/schemas/BrandAsset'
required:
- id
- name
- colours
- tone_guidance
- created_at
- updated_at
- archived_at
- assets
additionalProperties: false
BrandAsset:
type: object
properties:
id:
$ref: '#/components/schemas/BrandAssetId'
kind:
type: string
enum:
- logo
- font
media_type:
type: string
enum:
- image/png
- image/jpeg
- image/webp
- font/woff2
byte_size:
type: integer
minimum: 1
maximum: 10485760
created_at:
type: string
format: date-time
required:
- id
- kind
- media_type
- byte_size
- created_at
additionalProperties: false
BrandKitDetail:
type: object
properties:
archive_history:
type: array
items:
$ref: '#/components/schemas/ArchivePeriod'
id:
$ref: '#/components/schemas/BrandKitId'
name:
type: string
minLength: 1
maxLength: 100
colours:
type: array
maxItems: 16
uniqueItems: true
items:
type: string
pattern: ^#[0-9a-f]{6}$
tone_guidance:
type:
- string
- 'null'
minLength: 1
maxLength: 500
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
archived_at:
type:
- string
- 'null'
format: date-time
assets:
type: array
maxItems: 16
items:
$ref: '#/components/schemas/BrandAsset'
required:
- archive_history
- id
- name
- colours
- tone_guidance
- created_at
- updated_at
- archived_at
- assets
additionalProperties: false
BrandKitPage:
type: object
properties:
items:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/BrandKitSummary'
next_cursor:
type:
- string
- 'null'
maxLength: 128
required:
- items
- next_cursor
additionalProperties: false
TemplateBuildId:
type: string
pattern: ^build_[0-9a-f]{32}$
TemplateMessageId:
type: string
pattern: ^msg_[0-9a-f]{32}$
TemplateBuildInput:
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
description: Rebuild a still PNG, JPEG, or WebP as an editable branded template. Inspired by the source, not a pixel
copy. At most 10 MiB, 7680 by 4320, and 32 million pixels. An invalid file fails before the model.
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 and evaluate with sample_data.
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:
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:
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:
type: object
properties:
brand_kit_id:
$ref: '#/components/schemas/BrandKitId'
input:
$ref: '#/components/schemas/TemplateBuildInput'
input_mode:
type: string
enum:
- blank
- html
description: 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.
output:
$ref: '#/components/schemas/TemplateOutputIntent'
required:
- brand_kit_id
- input
if:
required:
- input_mode
then:
properties:
input:
properties:
type:
const: source
additionalProperties: false
TemplateMessageCreate:
type: object
properties:
input:
$ref: '#/components/schemas/TemplateBuildInput'
output:
$ref: '#/components/schemas/TemplateOutputIntent'
required:
- input
additionalProperties: false
TemplateMessage:
type: object
properties:
id:
$ref: '#/components/schemas/TemplateMessageId'
state:
type: string
enum:
- queued
- running
- previewing
- succeeded
- failed
- cancelled
phase:
type: string
enum:
- generate
- candidate
- review
- final
input_type:
type: string
enum:
- prompt
- html
- image
- source
credits:
type: integer
enum:
- 0
- 25
- 50
preview_id:
type:
- string
- 'null'
pattern: ^(pdf|image)_[0-9a-f]{32}$
output:
oneOf:
- $ref: '#/components/schemas/TemplateOutput'
- type: 'null'
review_count:
type: integer
minimum: 0
maximum: 5
max_reviews:
type: integer
minimum: 0
maximum: 5
input_text:
type: string
maxLength: 65536
repair_code:
type: string
pattern: ^[a-z][a-z0-9_]{0,63}$
description: The last safe generation or render code that blocked this message. Present while a repair is in progress
and after a failed message. It never holds customer content.
failure_category:
type:
- string
- 'null'
enum:
- provider
- validation
- renderer
- unsupported_input
- internal
- null
failure_code:
type:
- string
- 'null'
pattern: ^[a-z][a-z0-9_]{0,63}$
required:
- id
- state
- phase
- input_type
- credits
- preview_id
- output
- review_count
- max_reviews
- failure_category
- failure_code
additionalProperties: false
TemplateBuild:
type: object
properties:
id:
$ref: '#/components/schemas/TemplateBuildId'
brand_kit_id:
$ref: '#/components/schemas/BrandKitId'
current_draft:
type: boolean
current_message_id:
oneOf:
- $ref: '#/components/schemas/TemplateMessageId'
- type: 'null'
published_template_id:
oneOf:
- $ref: '#/components/schemas/TemplateId'
- type: 'null'
message:
$ref: '#/components/schemas/TemplateMessage'
required:
- id
- brand_kit_id
- current_draft
- current_message_id
- published_template_id
- message
additionalProperties: false
TemplateBuildDraft:
type: object
properties:
source:
type: string
maxLength: 1048576
sample_data:
type: object
schema: {}
output:
$ref: '#/components/schemas/TemplateOutput'
required:
- source
- sample_data
- schema
- output
additionalProperties: false
TemplatePublication:
type: object
properties:
template_id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
required:
- template_id
- version
additionalProperties: false
TemplateId:
type: string
pattern: ^tpl_[0-9a-f]{32}$
ArchivePeriod:
type: object
properties:
archived_at:
type: string
format: date-time
restored_at:
type:
- string
- 'null'
format: date-time
required:
- archived_at
- restored_at
additionalProperties: false
TemplateTags:
type: array
maxItems: 20
uniqueItems: true
items:
type: string
minLength: 1
maxLength: 40
description: Trimmed tags. Each tag is nonblank. Case is preserved.
TemplateName:
type:
- string
- 'null'
maxLength: 120
description: The customer's own name for a template. It is trimmed and can have 1 to 120 characters. Null or blank means
no name.
TemplateWriteRequest:
type: object
properties:
tags:
$ref: '#/components/schemas/TemplateTags'
name:
$ref: '#/components/schemas/TemplateName'
input_mode:
type: string
enum:
- blank
- html
default: html
description: How this new template starts. It records a blank-page start or imported HTML. New versions do not accept
this field.
source:
type: string
maxLength: 1048576
schema:
oneOf:
- type: object
- type: boolean
- type: 'null'
sizes:
$ref: '#/components/schemas/SizeVariantList'
required:
- source
additionalProperties: false
TemplateVersionWriteRequest:
type: object
properties:
source:
type: string
maxLength: 1048576
schema:
oneOf:
- type: object
- type: boolean
- type: 'null'
sizes:
$ref: '#/components/schemas/SizeVariantList'
required:
- source
additionalProperties: false
TemplatePreviewRequest:
type: object
properties:
brand_kit_id:
type:
- string
- 'null'
pattern: ^kit_[a-f0-9]{32}$
description: 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.
source:
type: string
maxLength: 1048576
data:
type: object
description: Template values, bounded to 1,048,576 encoded JSON bytes with the template depth and collection limits.
schema:
oneOf:
- type: object
- type: boolean
- type: 'null'
required:
- source
- data
additionalProperties: false
TemplatePreview:
type: object
properties:
html:
type: string
maxLength: 5242880
required:
- html
additionalProperties: false
TemplateRename:
type: object
description: Omitted fields keep their current values. Set name to null to clear it, tags to an empty array to clear
them, or archived_at to null to restore the template.
properties:
archived_at:
type: 'null'
description: Restore the item. Omit this field to keep its archive state.
tags:
$ref: '#/components/schemas/TemplateTags'
name:
$ref: '#/components/schemas/TemplateName'
additionalProperties: false
TemplateSummary:
type: object
properties:
tags:
$ref: '#/components/schemas/TemplateTags'
id:
$ref: '#/components/schemas/TemplateId'
name:
$ref: '#/components/schemas/TemplateName'
created_at:
type: string
format: date-time
archived_at:
type:
- string
- 'null'
format: date-time
latest_version:
type: integer
minimum: 1
required:
- id
- name
- tags
- created_at
- archived_at
- latest_version
additionalProperties: false
TemplateDetail:
type: object
properties:
archive_history:
type: array
items:
$ref: '#/components/schemas/ArchivePeriod'
tags:
$ref: '#/components/schemas/TemplateTags'
id:
$ref: '#/components/schemas/TemplateId'
name:
$ref: '#/components/schemas/TemplateName'
created_at:
type: string
format: date-time
archived_at:
type:
- string
- 'null'
format: date-time
latest_version:
type: integer
minimum: 1
source:
type: string
maxLength: 1048576
schema: {}
sizes:
$ref: '#/components/schemas/SizeVariantList'
required:
- archive_history
- id
- name
- tags
- created_at
- archived_at
- latest_version
- source
- schema
- sizes
additionalProperties: false
TemplatePage:
type: object
properties:
tag_options:
type: array
maxItems: 100
uniqueItems: true
items:
type: string
minLength: 1
maxLength: 40
description: The first 100 distinct account tags, sorted. Use the tag filter for any other tag.
items:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/TemplateSummary'
next_cursor:
type:
- string
- 'null'
maxLength: 128
required:
- items
- next_cursor
- tag_options
additionalProperties: false
TemplateVersionSummary:
type: object
properties:
version:
type: integer
minimum: 1
created_at:
type: string
format: date-time
required:
- version
- created_at
additionalProperties: false
TemplateVersionDetail:
type: object
properties:
version:
type: integer
minimum: 1
created_at:
type: string
format: date-time
source:
type: string
maxLength: 1048576
schema: {}
sizes:
$ref: '#/components/schemas/SizeVariantList'
required:
- version
- created_at
- source
- schema
- sizes
additionalProperties: false
TemplateVersionPage:
type: object
properties:
items:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/TemplateVersionSummary'
next_cursor:
type:
- string
- 'null'
maxLength: 32
required:
- items
- next_cursor
additionalProperties: false
SizeVariant:
type: object
description: One saved canvas size for a template version. `docs/SMART-RESIZE.md` owns the rules.
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9-]{0,39}$
description: Unique within the version. The id original is reserved for the version's own canvas.
name:
type: string
minLength: 1
maxLength: 120
width:
type: integer
minimum: 320
maximum: 7680
height:
type: integer
minimum: 200
maximum: 4320
status:
type: string
enum:
- auto
- edited
source:
type: string
maxLength: 1048576
description: The source canvas must declare exactly width by height.
data_overrides:
type: object
description: Merged over the version's rendered data for this size only.
required:
- id
- name
- width
- height
- status
- source
additionalProperties: false
SizeVariantList:
type: array
maxItems: 20
items:
$ref: '#/components/schemas/SizeVariant'
TemplateAdaptRequest:
type: object
description: Adapt one HTML source to many target canvas sizes with the deterministic layout algorithm. This is free
and does not save anything.
properties:
source:
type: string
maxLength: 1048576
sizes:
type: array
maxItems: 20
items:
type: object
properties:
width:
type: integer
minimum: 320
maximum: 7680
height:
type: integer
minimum: 200
maximum: 4320
required:
- width
- height
additionalProperties: false
required:
- source
- sizes
additionalProperties: false
TemplateAdaptResponse:
type: object
properties:
sizes:
type: array
items:
type: object
properties:
width:
type: integer
height:
type: integer
source:
type: string
maxLength: 1048576
error:
type: object
properties:
code:
type: string
enum:
- resize_source_unsupported
- resize_no_layout
required:
- code
additionalProperties: false
required:
- width
- height
additionalProperties: false
required:
- sizes
additionalProperties: false
PlanLimitInfo:
type: object
description: 'Present when the account''s plan causes the refusal: brand_kit_limit and batch_row_limit. It names the
limit and the plan that sets it, so a caller can act without a second request. The message text never names the number.'
properties:
plan:
type: string
enum:
- scale
- growth
- starter
- pack
- free
description: The account's plan at the time of the refusal.
limit:
type: integer
minimum: 1
description: 'What that plan allows: brand kits for brand_kit_limit, rows in one run for batch_row_limit.'
required:
- plan
- limit
additionalProperties: false
RetryInfo:
type: object
description: How long the caller must wait before it retries.
properties:
retry_after_seconds:
type: integer
minimum: 1
maximum: 60
required:
- retry_after_seconds
additionalProperties: false
ImageRequestCommon:
type: object
properties:
filename:
type: string
minLength: 1
maxLength: 120
pattern: ^(?!.* $)(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9 ._-]*$
description: Download name. Surrounding spaces are refused. The server uses the real output extension. Omit it for
a safe template-name and UTC creation-date name, or html-render and date for HTML.
reference:
type: string
minLength: 1
maxLength: 200
pattern: ^[^\u0000-\u001f\u007f-\u009f]+$
description: Your reference, preserved in the job, history, and webhook. Omitted when not supplied.
brand_kit_id:
type:
- string
- 'null'
pattern: ^kit_[a-f0-9]{32}$
description: 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.
image:
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:
type: object
properties:
mode:
type: string
enum:
- disabled
- enabled
additionalProperties: false
wait:
type: boolean
default: true
required:
- image
RawImageRequest:
allOf:
- $ref: '#/components/schemas/ImageRequestCommon'
- type: object
properties:
html:
type: string
minLength: 1
maxLength: 5242880
required:
- html
unevaluatedProperties: false
StatelessTemplateImageRequest:
allOf:
- $ref: '#/components/schemas/ImageRequestCommon'
- type: object
properties:
html:
type: string
minLength: 1
maxLength: 1048576
data:
type: object
required:
- html
- data
unevaluatedProperties: false
SavedTemplateImageRequest:
allOf:
- $ref: '#/components/schemas/ImageRequestCommon'
- type: object
properties:
template_id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
data:
type: object
size_id:
type:
- string
- 'null'
pattern: ^[a-z0-9][a-z0-9-]{0,39}$
description: Selects one saved size of the template instead of the original. The image width and height must match
the size exactly.
required:
- template_id
- data
unevaluatedProperties: false
ImageRequest:
oneOf:
- $ref: '#/components/schemas/RawImageRequest'
- $ref: '#/components/schemas/StatelessTemplateImageRequest'
- $ref: '#/components/schemas/SavedTemplateImageRequest'
ImageJob:
type: object
properties:
reference:
type: string
minLength: 1
maxLength: 200
pattern: ^[^\u0000-\u001f\u007f-\u009f]+$
description: Your reference, preserved in the job, history, and webhook. Omitted when not supplied.
id:
type: string
pattern: ^image_[0-9a-f]{32}$
status:
type: string
enum:
- queued
- running
- succeeded
- failed
- cancelled
created_at:
type: string
format: date-time
finished_at:
type:
- string
- 'null'
format: date-time
template:
type:
- object
- 'null'
description: The exact saved template version this job rendered. Null for raw HTML or an inline stateless template.
properties:
id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
required:
- id
- version
additionalProperties: false
credit:
type: string
enum:
- reserved
- settled
- released
- none
description: '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.'
artifact:
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:
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:
type:
- object
- 'null'
properties:
url:
type: string
expires_at:
type: string
format: date-time
required:
- url
- expires_at
additionalProperties: false
required:
- id
- status
- created_at
- finished_at
- template
- credit
- artifact
- error
- download
additionalProperties: false
ImagePackCreate:
type: object
properties:
template_id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
description: Omitted to use the template's latest version at creation time.
data:
type: object
description: Template data. Each size's own saved data overrides apply over this data. Omitted to use an empty object.
format:
type: string
enum:
- png
- jpeg
- webp
sizes:
type: array
items:
type: string
minItems: 1
maxItems: 20
description: '1 to 20 different entries: original for the version''s own canvas, a saved size id, a preset slug,
or a WxH custom size.'
required:
- template_id
- format
- sizes
additionalProperties: false
ImagePackMember:
type: object
properties:
position:
type: integer
minimum: 0
size_id:
type: string
name:
type: string
width:
type: integer
minimum: 1
height:
type: integer
minimum: 1
state:
type: string
enum:
- queued
- running
- succeeded
- failed
error:
type:
- object
- 'null'
properties:
code:
type: string
required:
- code
additionalProperties: false
download_url:
type:
- string
- 'null'
required:
- position
- size_id
- name
- width
- height
- state
- error
- download_url
additionalProperties: false
ImagePack:
type: object
properties:
id:
type: string
pattern: ^pack_[0-9a-f]{32}$
template_id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
format:
type: string
enum:
- png
- jpeg
- webp
created_at:
type: string
format: date-time
state:
type: string
enum:
- queued
- running
- complete
- partial
- failed
members:
type: array
items:
$ref: '#/components/schemas/ImagePackMember'
archive_url:
type:
- string
- 'null'
description: A signed ZIP download link, present once any member has succeeded.
required:
- id
- template_id
- version
- format
- created_at
- state
- members
- archive_url
additionalProperties: false
ImagePackRetry:
$ref: '#/components/schemas/ImagePack'
CarouselCreate:
type: object
properties:
template_id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
description: Omitted to use the template's latest version at creation time.
data:
type: object
description: Template data applied to every selected page. Omitted to use an empty object.
format:
type: string
enum:
- png
- jpeg
- webp
pages:
type: array
items:
type: string
minLength: 1
maxLength: 64
pattern: ^[A-Za-z][A-Za-z0-9_-]{0,63}$
minItems: 1
maxItems: 100
description: 1 to 100 different canvas data-thirds-page ids in output order.
required:
- template_id
- format
- pages
additionalProperties: false
CarouselMember:
type: object
properties:
position:
type: integer
minimum: 0
page_id:
type: string
name:
type: string
width:
type: integer
minimum: 1
height:
type: integer
minimum: 1
state:
type: string
enum:
- queued
- running
- succeeded
- failed
error:
type:
- object
- 'null'
properties:
code:
type: string
required:
- code
additionalProperties: false
download_url:
type:
- string
- 'null'
required:
- position
- page_id
- name
- width
- height
- state
- error
- download_url
additionalProperties: false
Carousel:
type: object
properties:
id:
type: string
pattern: ^carousel_[0-9a-f]{32}$
template_id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
format:
type: string
enum:
- png
- jpeg
- webp
created_at:
type: string
format: date-time
state:
type: string
enum:
- queued
- running
- complete
- partial
- failed
members:
type: array
items:
$ref: '#/components/schemas/CarouselMember'
archive_url:
type:
- string
- 'null'
description: A signed ZIP download link, present once any member has succeeded.
required:
- id
- template_id
- version
- format
- created_at
- state
- members
- archive_url
additionalProperties: false
CarouselRetry:
$ref: '#/components/schemas/Carousel'
BatchCreate:
type: object
properties:
template_id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
description: Omitted to use the template's latest version at creation time. The batch pins that exact version.
format:
type: string
enum:
- pdf
- png
- jpeg
- webp
rows:
type: array
items:
type: object
minItems: 1
maxItems: 200
description: 1 to 200 JSON objects. Each object is one render. 200 is the system cap; the account plan can allow
fewer. The CSV parser lives in the studio; this API accepts rows only.
required:
- template_id
- format
- rows
additionalProperties: false
BatchRow:
type: object
properties:
position:
type: integer
minimum: 0
state:
type: string
enum:
- queued
- running
- succeeded
- failed
- cancelled
error:
type:
- object
- 'null'
properties:
code:
type: string
required:
- code
additionalProperties: false
download_url:
type:
- string
- 'null'
required:
- position
- state
- error
- download_url
additionalProperties: false
Batch:
type: object
properties:
id:
type: string
pattern: ^batch_[0-9a-f]{32}$
template_id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
format:
type: string
enum:
- pdf
- png
- jpeg
- webp
created_at:
type: string
format: date-time
state:
type: string
enum:
- queued
- running
- complete
- partial
- failed
- cancelled
quoted_credits:
type: integer
minimum: 1
maximum: 200
description: The row count. Each successful file settles one credit. Failed and cancelled rows cost nothing. There
is no bulk discount.
rows:
type: array
items:
$ref: '#/components/schemas/BatchRow'
archive_url:
type:
- string
- 'null'
description: A signed ZIP download link, present once any row has succeeded.
required:
- id
- template_id
- version
- format
- created_at
- state
- quoted_credits
- rows
- archive_url
additionalProperties: false
BatchRetry:
$ref: '#/components/schemas/Batch'
PdfRequestCommon:
type: object
description: 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.
properties:
filename:
type: string
minLength: 1
maxLength: 120
pattern: ^(?!.* $)(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9 ._-]*$
description: Download name. Surrounding spaces are refused. The server uses the real output extension. Omit it for
a safe template-name and UTC creation-date name, or html-render and date for HTML.
reference:
type: string
minLength: 1
maxLength: 200
pattern: ^[^\u0000-\u001f\u007f-\u009f]+$
description: Your reference, preserved in the job, history, and webhook. Omitted when not supplied.
brand_kit_id:
type:
- string
- 'null'
pattern: ^kit_[a-f0-9]{32}$
description: 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.
pdf:
type: object
description: PDF layout options. The page defaults to A4 when neither "format" nor both "width" and "height" are
given.
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.0
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.
title:
type: string
maxLength: 256
author:
type: string
maxLength: 256
subject:
type: string
maxLength: 256
password:
type: string
minLength: 1
maxLength: 127
description: PDF open password, at most 127 UTF-8 bytes. AES-256 encryption.
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:
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.0
maximum: 3.0
description: Defaults to 1.0.
additionalProperties: false
javascript:
type: object
properties:
mode:
type: string
enum:
- disabled
- enabled
description: Defaults to "disabled".
additionalProperties: false
wait:
type: boolean
description: 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.
pages:
type: array
items:
type: string
minLength: 1
maxLength: 64
pattern: ^[A-Za-z][A-Za-z0-9_-]{0,63}$
minItems: 1
maxItems: 100
description: Canvas data-thirds-page ids in output order. Omitted keeps every page in document order. Duplicate
or unknown ids are refused.
RawPdfRequest:
description: A raw HTML render. The HTML enters the existing render workflow unchanged.
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:
description: 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.
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:
description: 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.
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:
description: '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.'
oneOf:
- $ref: '#/components/schemas/RawPdfRequest'
- $ref: '#/components/schemas/StatelessTemplatePdfRequest'
- $ref: '#/components/schemas/SavedTemplatePdfRequest'
PdfJob:
type: object
description: The state of one render job. This is the same shape whether the job just finished (200) or is still in
progress (202).
properties:
reference:
type: string
minLength: 1
maxLength: 200
pattern: ^[^\u0000-\u001f\u007f-\u009f]+$
description: Your reference, preserved in the job, history, and webhook. Omitted when not supplied.
id:
type: string
pattern: ^pdf_[0-9a-f]{32}$
description: 'The job''s public identifier: "pdf_" followed by 32 lowercase hexadecimal characters.'
status:
type: string
enum:
- queued
- running
- succeeded
- failed
- cancelled
created_at:
type: string
format: date-time
finished_at:
type:
- string
- 'null'
format: date-time
template:
type:
- object
- 'null'
description: The exact saved template version this job rendered. Null for raw HTML or an inline stateless template.
properties:
id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
required:
- id
- version
additionalProperties: false
credit:
type: string
enum:
- reserved
- settled
- released
- none
description: '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.'
artifact:
type:
- object
- 'null'
description: Present only for a succeeded job.
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:
type:
- object
- 'null'
description: Present only for a terminal failure. Never carries a message, only a fixed code.
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:
type:
- object
- 'null'
description: Present only while a succeeded job's PDF is still available. A fresh signed link every time this job
is read.
properties:
url:
type: string
expires_at:
type: string
format: date-time
required:
- url
- expires_at
additionalProperties: false
required:
- id
- status
- created_at
- finished_at
- template
- credit
- artifact
- error
- download
additionalProperties: false
PdfHistoryItem:
type: object
description: 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.
properties:
reference:
type: string
minLength: 1
maxLength: 200
pattern: ^[^\u0000-\u001f\u007f-\u009f]+$
description: Your reference, preserved in the job, history, and webhook. Omitted when not supplied.
id:
type: string
pattern: ^pdf_[0-9a-f]{32}$
status:
type: string
enum:
- queued
- running
- succeeded
- failed
- cancelled
created_at:
type: string
format: date-time
finished_at:
type:
- string
- 'null'
format: date-time
template:
type:
- object
- 'null'
description: The exact saved template version this job rendered. Null for raw HTML or an inline stateless template.
properties:
id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
required:
- id
- version
additionalProperties: false
credit:
type: string
enum:
- reserved
- settled
- released
- none
description: '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.'
artifact:
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:
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
required:
- id
- status
- created_at
- finished_at
- template
- credit
- artifact
- error
additionalProperties: false
PdfHistoryPage:
type: object
description: One bounded page of an account's render history, newest first.
properties:
items:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/PdfHistoryItem'
next_cursor:
type:
- string
- 'null'
maxLength: 128
description: Pass as the cursor query parameter to read the next page. Null on the last page.
required:
- items
- next_cursor
additionalProperties: false
ImageHistoryItem:
type: object
description: 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.
properties:
reference:
type: string
minLength: 1
maxLength: 200
pattern: ^[^\u0000-\u001f\u007f-\u009f]+$
description: Your reference, preserved in the job, history, and webhook. Omitted when not supplied.
id:
type: string
pattern: ^image_[0-9a-f]{32}$
status:
type: string
enum:
- queued
- running
- succeeded
- failed
- cancelled
created_at:
type: string
format: date-time
finished_at:
type:
- string
- 'null'
format: date-time
template:
type:
- object
- 'null'
description: The exact saved template version this job rendered. Null for raw HTML or an inline stateless template.
properties:
id:
$ref: '#/components/schemas/TemplateId'
version:
type: integer
minimum: 1
required:
- id
- version
additionalProperties: false
credit:
type: string
enum:
- reserved
- settled
- released
- none
description: '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.'
artifact:
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:
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
required:
- id
- status
- created_at
- finished_at
- template
- credit
- artifact
- error
additionalProperties: false
ImageHistoryPage:
type: object
description: One bounded page of an account's image history, newest first.
properties:
items:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/ImageHistoryItem'
next_cursor:
type:
- string
- 'null'
maxLength: 128
description: Pass as the cursor query parameter to read the next page. Null on the last page.
required:
- items
- next_cursor
additionalProperties: false
NewApiKey:
type: object
description: What a new key is named.
properties:
name:
type: string
minLength: 1
maxLength: 100
pattern: ^(?=.*[^ ])[ -~]+$
description: 1 to 100 printable ASCII characters.
required:
- name
additionalProperties: false
ApiKey:
type: object
description: One key, without its secret.
properties:
id:
type: string
pattern: ^key_[0-9a-f]{32}$
description: 'The key''s public identifier: "key_" followed by 32 lowercase hexadecimal characters.'
name:
type: string
display_prefix:
type: string
description: The first characters of the secret, enough to tell keys apart in a list.
status:
type: string
enum:
- active
- revoked
created_at:
type: string
format: date-time
revoked_at:
type:
- string
- 'null'
format: date-time
last_used_at:
type:
- string
- 'null'
format: date-time
monthly_overage_cap:
type:
- integer
- 'null'
minimum: 1
description: The most overage credits this key may take on per calendar month (UTC). Null means the key sets no
cap of its own.
required:
- id
- name
- display_prefix
- status
- created_at
- revoked_at
- last_used_at
- monthly_overage_cap
additionalProperties: false
ApiKeyUpdate:
type: object
description: What an update may change about a key.
properties:
name:
type: string
minLength: 1
maxLength: 100
pattern: ^(?=.*[!-~])[ -~]+$
description: New key name. The key secret and prefix stay the same.
monthly_overage_cap:
type:
- integer
- 'null'
minimum: 1
description: The most overage credits this key may take on per calendar month (UTC), or null to clear the cap. Omit
it to keep the current cap.
additionalProperties: false
ApiKeyWithSecret:
type: object
description: One key exactly as creation answers it. This is the only shape that ever carries the secret.
properties:
id:
type: string
pattern: ^key_[0-9a-f]{32}$
description: 'The key''s public identifier: "key_" followed by 32 lowercase hexadecimal characters.'
name:
type: string
secret:
type: string
pattern: ^thirds_sk_v1_[0-9a-f]{64}$
description: 'The full secret: "thirds_sk_v1_" followed by 64 lowercase hexadecimal characters. It is shown here
once and never again.'
display_prefix:
type: string
description: The first characters of the secret, enough to tell keys apart in a list.
status:
type: string
enum:
- active
created_at:
type: string
format: date-time
required:
- id
- name
- secret
- display_prefix
- status
- created_at
additionalProperties: false
ApiKeyList:
type: object
description: One bounded page of the account's key history.
properties:
data:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/ApiKey'
next_cursor:
type:
- string
- 'null'
maxLength: 128
required:
- data
- next_cursor
additionalProperties: false
NewWebhook:
type: object
description: What creating a webhook destination needs.
properties:
url:
type: string
format: uri
maxLength: 2048
description: 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.
events:
type: array
minItems: 1
maxItems: 3
uniqueItems: true
items:
type: string
enum:
- render.cancelled
- render.failed
- render.succeeded
description: The terminal render events this destination receives. A body without this field selects every event.
required:
- url
additionalProperties: false
WebhookFields:
type: object
properties:
id:
type: string
pattern: ^wh_[0-9a-f]{32}$
description: 'The destination''s public identifier: "wh_" followed by 32 lowercase hexadecimal characters.'
url:
type: string
format: uri
maxLength: 2048
events:
type: array
minItems: 1
maxItems: 3
uniqueItems: true
items:
type: string
enum:
- render.cancelled
- render.failed
- render.succeeded
description: The selected events, always sorted and distinct.
status:
type: string
enum:
- enabled
- disabled
disabled_reason:
type:
- string
- 'null'
enum:
- owner_request
- delivery_failure
- null
description: 'Why a disabled destination is disabled: the owner asked, or delivery failed repeatedly. Null while
the destination is enabled.'
display_prefix:
type: string
description: The first characters of the signing secret, enough to tell destinations apart in a list.
last_delivery_at:
type:
- string
- 'null'
format: date-time
description: Time of the most recent recorded attempt, including tests.
failure_count:
type: integer
minimum: 0
description: Consecutive normal delivery failures. A success or enable resets it. Test events do not change it.
created_at:
type: string
format: date-time
previous_secret_expires_at:
type:
- string
- 'null'
format: date-time
description: 'Set while a rotation overlap is running: until this instant every delivery also carries a signature
made with the previous secret.'
required:
- id
- url
- events
- status
- disabled_reason
- display_prefix
- created_at
- last_delivery_at
- failure_count
- previous_secret_expires_at
Webhook:
description: One webhook destination, without its signing secret.
allOf:
- $ref: '#/components/schemas/WebhookFields'
unevaluatedProperties: false
WebhookWithSecret:
description: One webhook destination exactly as creation and rotation answer it. This is the only shape that ever carries
the signing secret.
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:
type: object
description: Every webhook destination the account holds. The cap is ten, so the list is never paged.
properties:
data:
type: array
maxItems: 10
items:
$ref: '#/components/schemas/Webhook'
required:
- data
additionalProperties: false
WebhookReplay:
type: object
description: What one replay request did.
properties:
replayed:
type: integer
minimum: 0
description: How many failed events returned to the queue with a fresh attempt budget.
required:
- replayed
additionalProperties: false
ErrorEnvelope:
type: object
description: The one error shape every backend response uses.
properties:
error:
type: object
properties:
code:
type: string
enum:
- account_suspended
- account_concurrency_limited
- abuse_limited
- ai_failure_limit_reached
- ai_needs_paid_credits
- already_subscribed
- auth_unavailable
- batch_row_limit
- billing_unavailable
- brand_asset_account_limit_reached
- brand_asset_invalid
- brand_asset_kind_invalid
- brand_asset_kind_mismatch
- brand_asset_limit_reached
- brand_asset_too_large
- brand_asset_type_unsupported
- brand_data_conflict
- brand_font_glyphs_exceeded
- brand_font_tables_invalid
- brand_image_animated
- brand_image_dimensions_invalid
- brand_image_pixels_exceeded
- brand_kit_colours_invalid
- brand_kit_conflict
- brand_kit_limit
- brand_kit_name_invalid
- brand_kit_not_found
- brand_kit_patch_empty
- brand_kit_tone_invalid
- brand_kit_unavailable
- captcha_rejected
- checkout_superseded
- cross_origin_rejected
- csrf_rejected
- download_expired
- email_already_set
- gallery_template_not_found
- generated_template_invalid
- idempotency_conflict
- image_asset_header_unsupported
- image_asset_invalid
- image_asset_limit
- image_asset_not_found
- image_asset_reference_invalid
- image_asset_too_large
- image_asset_type_unsupported
- image_asset_unavailable
- image_url_invalid
- image_url_unavailable
- insufficient_credits
- internal_error
- invalid_cursor
- invalid_email
- invalid_event
- invalid_link
- invalid_profile
- invalid_request
- invalid_upload
- job_not_finished
- key_concurrency_limited
- key_limit_reached
- method_not_allowed
- no_billing_customer
- not_found
- operation_conflict
- operation_limit_exceeded
- operation_pending
- overage_limit_reached
- overage_unavailable
- overloaded
- playground_busy
- playground_request_invalid
- playground_selection_invalid
- playground_session_limited
- playground_unavailable
- policy_version_stale
- provider_unavailable
- rate_limited
- render_probe_busy
- render_probe_failed
- render_probe_not_configured
- render_probe_required
- render_probe_timeout
- request_headers_too_large
- request_too_large
- resize_timeout
- signed_out
- spend_cap_reached
- template_build_not_found
- template_data_collection_limit
- template_data_depth_limit
- template_data_invalid
- template_data_limit
- template_depth_limit
- template_draft_not_found
- 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_size_canvas_mismatch
- template_size_data_overrides_too_large
- template_size_duplicate_id
- template_size_invalid_dimensions
- template_size_invalid_id
- template_size_invalid_name
- template_sizes_too_large
- template_sizes_too_many
- template_source_limit
- template_syntax_error
- template_timeout
- template_version_changed
- template_work_limit
- testimonial_busy
- testimonial_invalid
- testimonial_rate_limited
- testimonial_unavailable
- 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'
plan_limit:
$ref: '#/components/schemas/PlanLimitInfo'
required:
- code
- message
- request_id
additionalProperties: false
required:
- error
additionalProperties: false
responses:
AccountSuspended:
description: The authenticated account is suspended. The code is account_suspended.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
Error:
description: The one error envelope every backend response uses.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
MethodNotAllowed:
description: The method is not allowed on this route.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
NotFound:
description: The requested backend route does not exist.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
RequestHeadersTooLarge:
description: The request has more than 64 headers or more than 32 KiB of header names and values.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
securitySchemes:
sessionCookie:
type: apiKey
in: cookie
name: __Host-thirds_session
description: A browser session. Browser writes also require the matching x-csrf-token header from GET /v1/me.
bearerAuth:
type: http
scheme: bearer
description: 'An API key''s secret, sent as "Authorization: Bearer thirds_sk_v1_...".'