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

EventSent when
bulk_job.completedA bulk verify or bulk find job finished. data: jobId, type, name, total, processed, creditsUsed, statusCounts, resultsUrl.
bulk_job.failedA bulk job stopped with an error (processed rows are kept).
credits.lowYour balance dropped below your alert threshold (once a day at most). data: balance, threshold, plan.
list.lead_addedA 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}
HeaderValue
Skrap-EventEvent type, e.g. bulk_job.completed
Skrap-Event-IdSame for every delivery of one event — use it to de-duplicate
Skrap-DeliveryUnique per delivery attempt chain
Skrap-Signaturet=<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";2​3function 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.