Skip to content

Integrate

Webhooks

Get an event when a batch finishes, and verify it came from Anyrow.

Instead of polling a batch, register a webhook: when an extraction finishes, Anyrow posts an event to your HTTPS endpoint, signed with a secret only you and Anyrow know. Webhooks belong to an organization and need a plan that includes them.

Events

EventWhen
batch.completeEvery file in the batch was extracted
batch.partialSome files were extracted, some failed
batch.failedThe batch produced no rows
pingYou asked for a test delivery

The body is JSON:

{  "api_version": "2026-03-01",  "batch_id": "batch_01h5a3g8k9m2n4p6q8r0t2v4x6",  "project_id": "proj_01h5a3g8k9m2n4p6q8r0t2v4x6",  "status": "complete",  "total_rows": 12,  "duration_ms": 8421}
  1. Register your endpoint

    The response carries the webhook's signing secret (whsec_...). Store it; it isn't shown again.

    curl -X POST "https://api.anyrow.ai/v1/organizations/$ORG_ID/webhooks" \  -H "Authorization: ApiKey $ANYROW_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "url": "https://example.com/hooks/anyrow", "events": ["batch.complete", "batch.failed"] }'
  2. Verify each delivery

    Deliveries follow the Standard Webhooks (opens in a new tab) spec. Three headers come with each one:

    HeaderHolds
    webhook-idThe delivery's id, the same on every retry: use it to skip duplicates
    webhook-timestampWhen it was sent, in Unix seconds
    webhook-signaturev1,<base64>: an HMAC-SHA256 of {id}.{timestamp}.{body}

    Compute the signature over the raw body with your secret (base64-decoded, without whsec_), compare it in constant time, and reject deliveries older than a few minutes. Any Standard Webhooks library does all of it:

    import { Webhook } from "standardwebhooks"const webhook = new Webhook(process.env.ANYROW_WEBHOOK_SECRET!.replace(/^whsec_/, ""))export async function POST(request: Request) {  const body = await request.text()  const event = webhook.verify(body, Object.fromEntries(request.headers)) // throws if forged  // handle event.batch_id, event.status ...  return new Response(null, { status: 204 })}
  3. Answer fast

    Return a 2xx within 30 seconds, and do slow work after answering.

Retries

A delivery that times out or gets a 5xx, 408 or 429 is retried up to 6 times, after 1 second, 5 seconds, 30 seconds, 5 minutes, 30 minutes and 2 hours. A Retry-After header on your 429 or 503 is honored (up to a day). Any other 4xx isn't retried. After 10 failed deliveries in a row the webhook is disabled and you get a notification.

Test and rotate

  • Send a test event to check your endpoint; it sends ping.
  • Rotate the secret if it leaks. For 24 hours after a rotation each delivery is signed with both secrets (two values in webhook-signature), so you can deploy the new one without dropping events.
  • Deliveries lists recent attempts, with the status and body your endpoint returned.