Skip to content

Webhooks

The platform POSTs to your endpoint when an async operation finishes, so you don’t have to poll. See the API reference for the exact payload schema of each event — segment.ready, segment.failed, delivery.completed, delivery.failed — this guide covers setup and signature verification.

Event Fires when
segment.ready An async segment build succeeded (file match, similarity, or propensity).
segment.failed An async segment build failed.
delivery.completed A file export/download is ready.
delivery.failed A file export/download failed.

Three ways, checked in this order:

  1. Per-request webhook_url — pass it on the individual call (e.g. POST /v1/match/file or POST /v1/match/{id}/deliveries). Used for that call only.
  2. Org-configured URL — set once via PUT /v1/settings/webhook, used for everything that doesn’t specify its own webhook_url.
  3. Durable subscriptionsPOST /v1/webhook-subscriptions with a target_url and an events array, for when you want a standing endpoint that receives every event of a given type regardless of which call triggered it (this is also the path OAuth-connected apps use to receive events for the account that authorized them).

Omitting webhook_url on a request falls back to the org-configured URL; it does not also call any durable subscriptions for that same event — those are independent delivery paths, not a fallback chain.

Terminal window
curl -X POST https://api.infiniteaudience.ai/v1/webhook-subscriptions \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target_url": "https://your-app.example.com/webhooks/infinite-audience",
"events": ["segment.ready", "segment.failed", "delivery.completed", "delivery.failed"]
}'
{
"id": "whsub_a1b2c3",
"target_url": "https://your-app.example.com/webhooks/infinite-audience",
"events": ["segment.ready", "segment.failed", "delivery.completed", "delivery.failed"],
"secret": "whsec_9f8e7d6c5b4a...",
"status": "active"
}

Requires the account scope — or, for OAuth-connected apps, you can manage a subscription tied to your own grant without it.

If a signing secret is configured (from Settings, or from a durable subscription’s secret), every outbound dispatch includes:

X-CF-Signature: sha256=<hmac-hex>

computed as HMAC-SHA256(secret, raw-request-body) — over the raw bytes of the request body, before any JSON parsing. Verify it like this:

import { createHmac, timingSafeEqual } from 'crypto';
function verifyWebhookSignature(rawBody: Buffer, signatureHeader: string, secret: string): boolean {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
const actual = Buffer.from(signatureHeader);
const expectedBuf = Buffer.from(expected);
if (actual.length !== expectedBuf.length) return false;
return timingSafeEqual(actual, expectedBuf);
}
import hashlib
import hmac
def verify_webhook_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature_header, expected)

Every delivery path — per-request webhook_url, the org-configured URL, and durable subscriptions — goes through the same retrying dispatcher, up to 10 attempts total per event. 5xx, 408, 429, and connection timeouts are retried; 410 Gone stops retrying immediately (it’s the documented signal for “this endpoint was intentionally removed,” not a transient failure). A durable subscription that racks up 20 consecutive failed deliveries is automatically disabled — check its status if events seem to have silently stopped.

Respond 2xx quickly and do slow processing (writing to a queue, calling other services) asynchronously after — a slow handler risks looking like a failure and triggering a retry for an event you actually already received.