Skip to main content
Supply webhook.url on a task and we POST the result there when the task reaches a terminal state — COMPLETED or FAILED.

Delivery payload

We POST Content-Type: application/json with this body:
object
required
The same task metadata you got at submit time, now with a terminal status and your idempotencyKey echoed back.
object
required
A nominal per-engine figure mirroring Cloro’s pricing table — 3 for Perplexity, 7 for ChatGPT, and so on. Nothing is billed. creditsCharged is 0 when the task failed. Unlike the submit and poll responses, which always report zero, this field is non-zero.
object
required
The engine-specific result. See Engines. On a failed task this is { "error": "<reason>" } instead of the engine result.
A failed task still delivers:

Retries

Return any 2xx to acknowledge. Anything else — including a timeout — is a failure and we retry with exponential backoff: After the fifth attempt the delivery is abandoned and logged. There is no dead-letter queue you can read from, so the task result is only recoverable by polling within the 24-hour retention window. Each attempt has a 30 second request timeout.
Delivery is fire-and-forget from the task’s perspective. A task that completes but whose webhook can never be delivered still shows COMPLETED on GET /v1/async/task/{id} — the status describes the engine work, not the notification.

Writing a safe handler

1

Acknowledge fast

Return 200 as soon as you have durably enqueued the payload. Do your parsing and database writes after. A slow handler burns the 30-second timeout and earns a retry you did not want.
2

Deduplicate on idempotencyKey

Retries mean your handler can legitimately see the same task more than once. Match on task.idempotencyKey (or task.id) and make the write idempotent.
3

Branch on status, not on presence

Check task.status === "FAILED" explicitly. A failed task still delivers a webhook.
Webhook requests carry no signature or shared secret. If your endpoint is public, treat the payload as untrusted and use an unguessable URL path, or place the handler behind network-level restrictions.