Developer docs
Webhooks
Register a URL on any POST /v1/batch call and we'll POST a signed payload when the job completes.
Events
| Event | When it fires |
|---|---|
| batch.completed | Job status → completed. Body has counts + results_url. |
| batch.failed | Job status → failed after retries exhausted. |
| reputation.warning | Your egress IP hit a greylist. Advisory — we auto-cool. |
Payload shape
{
"event": "batch.completed",
"job_id": "b85551e9-b4fa-4227-b481-f0974e1358c5",
"processed": 12500,
"successful": 12480,
"completed_at": "2026-07-15T12:41:53Z"
} Signature verification
Every request includes X-ZI2V-Signature:
sha256=<hex> — HMAC-SHA256 of the raw request body, keyed with your endpoint secret.
Timing-safe compare it against your local computation before trusting the payload.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(req, secret) {
const sig = req.headers['x-zi2v-signature']?.replace('sha256=', '');
const expected = createHmac('sha256', secret).update(req.rawBody).digest('hex');
return sig && timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
} Retries
Non-2xx or timeout ⇒ retry with exponential backoff (30s, 2m, 10m, 1h, 6h). Give up after 24h and move to the dead-letter queue (visible in the dashboard).
Testing
Dashboard → Webhooks → "Fire test event" replays the latest payload against your endpoint. Or use webhook.site for a scratch URL during development.