Webhooks
Webhooks
Webhooks send a signed HTTP POST to your HTTPS endpoint when something happens in Skrap. Add endpoints in the app (Integrations → Webhooks) or subscribe from an integration with the REST-hook endpoints below. Paid plans.
Events
| Event | Sent when |
|---|---|
bulk_job.completed | A bulk verify or bulk find job finished. data: jobId, type, name, total, processed, creditsUsed, statusCounts, resultsUrl. |
bulk_job.failed | A bulk job stopped with an error (processed rows are kept). |
credits.low | Your balance dropped below your alert threshold (once a day at most). data: balance, threshold, plan. |
list.lead_added | A lead was saved to one of your lists. data: listId, listName, lead { fullName, email, emailStatus, title, company, domain, linkedinUrl }. |
Payload & headers
Every delivery is a POST with a JSON body in the same envelope: an event id, its type, when it was created, and event-specific data.
bulk_job.completed
1{2 "id": "evt_65f1c2a9e4b0a1b2c3d4e5f6",3 "type": "bulk_job.completed",4 "created": "2026-11-03T09:12:44.120Z",5 "data": {6 "jobId": "65f1c2a9e4b0a1b2c3d4e5f6",7 "type": "bulk-verify",8 "total": 1200,9 "creditsUsed": 874,10 "statusCounts": { "valid": 801, "risky": 73, "invalid": 290, "unknown": 36 }11 }12}| Header | Value |
|---|---|
Skrap-Event | Event type, e.g. bulk_job.completed |
Skrap-Event-Id | Same for every delivery of one event — use it to de-duplicate |
Skrap-Delivery | Unique per delivery attempt chain |
Skrap-Signature | t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${raw body}` with your endpoint secret> |
Verify signatures
Compute an HMAC-SHA256 of ${t}.${raw body} with your endpoint secret and compare it with v1 in the Skrap-Signature header. Reject timestamps older than five minutes to stop replays.
1import crypto from "node:crypto";23function verifySkrapSignature(rawBody, header, secret) {4 const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));5 if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false; // replay window6 const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");7 return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));8}Use the raw body
Verify against the exact bytes you received, before any JSON parsing or re-serialisation, or the signature will not match.Retries
Answer 2xx within 10 seconds. Anything else (or a timeout) is retried after 1 min, 5 min, 30 min, 2 h, 6 h and 12 h. Redirects are not followed. An endpoint that fails 50 times in a row is paused — re-enable it in the app.
Use
Skrap-Event-Id to make your handler idempotent: a retried event keeps the same id.REST hooks
For integration builders (Zapier, Make, n8n): subscribe and unsubscribe programmatically with your API key.
- POST
/api/v2/webhooksSubscribe: { "url" (or "target_url"), "events": [...] (or "event") } → 201 { id, url, events, secret }. - GET
/api/v2/webhooksList subscriptions (no secrets). - DELETE
/api/v2/webhooks/{id}Unsubscribe. - GET
/api/v2/webhooks/samples/{event}Example payload (array of one) for mapping fields in an integration builder.