thirds.ai REST API

REST API for HTML-to-PDF, HTML-to-image, AI template generation, template editing, brand kits, rendering, and batch operations. Bearer API key auth with idempotency support.

Operations 69

POST /v1/webhooks/{webhook_id}/test Send a signed test event #
GET /v1/webhooks/{webhook_id}/deliveries Read recent webhook attempts #
POST /v1/me/identities/email Add an email to an account without one #
POST /v1/image-assets/preview Preview a URL image #
POST /v1/image-assets Upload an image #
GET /v1/image-assets/{id}/content Read an owned image #
GET /v1/openapi.json Get the OpenAPI document #
GET /v1/public-stats Read measured public facts #
POST /v1/testimonials Offer a testimonial for private review #
POST /v1/pdf Create a PDF #
GET /v1/pdf List render history #
POST /v1/image Create a static image #
GET /v1/image List image history #
GET /v1/image/{id} Get a static image's status #
DELETE /v1/image/{id} Delete an image file or an unsuccessful render #
POST /v1/image-packs Create an image pack #
GET /v1/image-packs/{id} Get an image pack's status #
POST /v1/image-packs/{id}/retry Retry an image pack's failed members #
POST /v1/carousels Create a carousel #
GET /v1/carousels/{id} Get a carousel's status #
POST /v1/carousels/{id}/retry Retry a carousel's failed members #
POST /v1/batches Create a render batch #
GET /v1/batches/{id} Get a render batch's status #
POST /v1/batches/{id}/retry Retry a batch's failed, cancelled, or pending rows #
POST /v1/batches/{id}/cancel Cancel a batch's pending and queued rows #
POST /v1/template-builds Start a template build #
GET /v1/template-builds/{build_id} Get a template build #
GET /v1/template-builds/{build_id}/messages List template chat messages #
POST /v1/template-builds/{build_id}/messages Edit the working template #
GET /v1/template-builds/{build_id}/draft Get the working template #
GET /v1/template-builds/{build_id}/messages/{message_id}/draft Get a completed message draft #
DELETE /v1/template-builds/{build_id}/messages/{message_id} Cancel a pending template message #
POST /v1/template-builds/{build_id}/publish Publish the current working template #
POST /v1/templates Create a saved template #
GET /v1/templates List saved templates #
POST /v1/templates/preview Preview template data as HTML #
POST /v1/templates/adapt Adapt one HTML source to many canvas sizes #
GET /v1/templates/{template_id} Get a saved template #
PATCH /v1/templates/{template_id} Rename a saved template #
DELETE /v1/templates/{template_id} Archive a saved template #
POST /v1/templates/{template_id}/versions Create a template version #
GET /v1/templates/{template_id}/versions List template versions #
GET /v1/templates/{template_id}/versions/{version} Get a template version #
POST /v1/brand-kits Create a brand kit #
GET /v1/brand-kits List brand kits #
GET /v1/brand-kits/{brand_kit_id} Get a brand kit #
PATCH /v1/brand-kits/{brand_kit_id} Update a brand kit #
DELETE /v1/brand-kits/{brand_kit_id} Archive a brand kit #
POST /v1/brand-kits/{brand_kit_id}/assets Add a brand asset #
GET /v1/brand-kits/{brand_kit_id}/assets/{asset_id} Read a brand asset #
PUT /v1/brand-kits/{brand_kit_id}/assets/{asset_id} Replace a brand asset #
GET /v1/pdf/{id} Get a PDF's status #
DELETE /v1/pdf/{id} Delete a PDF file or an unsuccessful render #
GET /v1/downloads/{token} Download a finished PDF or image #
GET /v1/image-packs/archive/{token} Download an image pack's ZIP archive #
GET /v1/carousels/archive/{token} Download a carousel's ZIP archive #
GET /v1/batches/archive/{token} Download a batch's ZIP archive #
POST /v1/keys Create an API key #
GET /v1/keys List API keys #
DELETE /v1/keys/{key_id} Revoke an API key #
PATCH /v1/keys/{key_id} Set or clear an API key's monthly overage cap #
POST /v1/webhooks Register a webhook destination #
GET /v1/webhooks List webhook destinations #
GET /v1/webhooks/{webhook_id} Read a webhook destination #
DELETE /v1/webhooks/{webhook_id} Delete a webhook destination #
POST /v1/webhooks/{webhook_id}/rotate Rotate a webhook destination's signing secret #
POST /v1/webhooks/{webhook_id}/disable Disable a webhook destination #
POST /v1/webhooks/{webhook_id}/enable Enable a webhook destination #
POST /v1/webhooks/{webhook_id}/replay Replay a webhook destination's failed events #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/thirdsai-rest-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

thirds-ai-openapi.yml Raw ↑
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: <h1>Quarterly report</h1><p>Ready to share.</p>
              stateless_template:
                summary: Stateless template with one data object
                value:
                  html: '<h1>{{ report.title }}</h1><p>Total: {{ report.total | currency }}</p>'
                  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: <h1>Quarterly report</h1>
                  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'
       

# --- truncated at 32 KB (218 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/thirds-ai/refs/heads/main/openapi/thirds-ai-openapi.yml