Connect your app

Receive results with webhooks

Use with your agent

Read https://thirds.ai/docs/webhooks and build a webhook receiver that verifies signatures and handles repeated render events safely

A webhook tells your server when a PDF or image finishes, so your app does not need to keep checking the job. Each event gives you IDs and a job status URL. Your server reads that URL with its API key to get the result and download link.

Register a receiver

Start with an HTTPS receiver on a server you control. It must use port 443 and resolve only to public IP addresses. Localhost, private networks, URL credentials, and redirects are not supported. Replace the URL and API key in this request with your own values:

sh request POST /v1/webhooks
curl --fail-with-body --silent --show-error https://thirds.ai/v1/webhooks \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  --data '{"url":"https://YOUR_RECEIVER.example/thirds-events","events":["render.succeeded","render.failed","render.cancelled"]}'

Save the returned signing secret in your receiver's secret store. You can read it only when you create the destination or rotate the secret. This secret checks incoming events. Your API key lets you read the job. Keep both out of URLs and logs.

An account can have ten destinations. Register and enable yours before you send the first-output request. One destination can receive events for both PDFs and images. A disabled destination receives no new events.

Verify before you act

Read the raw request body before you parse JSON. The thirds-signature header contains t=UNIX_SECONDS,v1=HEX_SIGNATURE. It can contain two v1 values while secrets rotate.

  1. Read the timestamp and reject it if it falls outside your accepted clock window. Five minutes is a reasonable window if your clocks stay in sync.
  2. Compute HMAC-SHA256 with your stored signing secret. The input is the exact timestamp text, a dot, and the raw body bytes. Compare the lowercase hex result with each v1 value using a constant-time comparison. Accept the event only if one matches.
  3. Parse the verified JSON. Store its event id with the work you queue. If you have already stored that ID, return success without repeating the work.
  4. Return a small 2xx response after you save the event and queued work to durable storage. Let your worker complete slow tasks.

Use the original body bytes for the signature check. Parsing and encoding JSON again can change spaces or key order, which changes the signature. Your platform's HMAC and constant-time comparison functions can handle the check.

Read the finished job

The event contains id, type, created_at, and data.job. The job contains id, state, output, and url. The event type is render.succeeded, render.failed, or render.cancelled. The output field is pdf or image.

After you verify the signature, check the job URL before you send your API key. It must belong to https://thirds.ai and match the expected /v1/pdf/{id} or /v1/image/{id} route. Then read the job and check its status. The HTTP request can succeed even when the render fails. Follow the recovery guide for failed jobs.

Events can repeat or arrive out of order. Use each event ID to detect repeats. Every retry and replay keeps the same event ID and body, but each attempt has a new signature timestamp.

Recover delivery

Your receiver has ten seconds to answer. Keep the complete response, including headers, within 16 KiB. thirds.ai checks the response status and discards the contents. Each event or replay gets eight attempts. Delays increase between attempts, up to eight hours. After the last failure, you must replay the event to try delivery again.

Fifty delivery failures in a row disable a destination. Read the destination to check its state, then repair your receiver. Enable the destination if needed and call the replay endpoint. Replay sends failed events again with their original IDs. It creates no new render and spends no render credits. The API reference lists the endpoints for disabling, enabling, replaying, deleting, and rotating secrets.

When you rotate a secret, save the new value when the API returns it. The previous secret stays valid for 24 hours, giving you time to update your receiver. Deleting a destination stops deliveries to it.

A missed event does not change the render result or refund a credit. You can still poll the job while you repair webhook delivery.