Skip to content

Webhooks

If you pass callbackUrl to createTask, we POST the final task state to that URL when the task finalizes. The URL is stored on the task hash at admission and expires with the result TTL.

Payload

http
POST https://yourapp.example.com/captchaai/webhook
Content-Type: application/json
X-CaptchaAI-Signature: t=1715865600,v1=…hex…
X-CaptchaAI-Event:    task.ready
json
{
  "taskId": "tsk_…",
  "status": "ready",
  "solution": { "text": "…" },
  "createdAt": "2026-05-16T08:40:00Z",
  "finalizedAt": "2026-05-16T08:40:12Z"
}

Signature verification

js
const t = Date.now() / 1000
const body = await req.text()
const expected = hmacSha256Hex(secret, `${t}.${body}`)
// Compare in constant time against the v1 value in X-CaptchaAI-Signature.

Reject if |now - t| > 300 s to prevent replay.

Retry schedule

AttemptDelay
1immediate
25 s
330 s
45 min
530 min
62 h

After attempt 6 the delivery is parked in callbacks.dlq. The admin Callback inspector page can replay it.

Idempotency on your side

We may deliver the same event more than once (network races, retries after a 5xx that did succeed). Treat taskId + status as the dedup key.

CaptchaAI · phase 1 docs