Skip to content

Webhooks

Add an endpoint under Webhooks in the dashboard, or with the API using a key that has the webhooks scope. We send a POST with a JSON body for each event the endpoint subscribes to.

Event When
credits.low Your balance drops below 25%, then again below 5%, of your monthly allowance.
credits.depleted Your balance reaches 0. Live lookups return 402 until credits arrive.
credits.added Credits arrive: a new month, an upgrade, or a top-up.
bulk.completed A bulk job finished. data has the job id, name, type, counts, and credits used.
ping You pressed Send test event. Sent to that endpoint only.

Each alert level is sent once on the way down. When credits arrive, the levels reset.

{
"id": "evt_3f9c…",
"type": "credits.low",
"created": 1792120504,
"api_version": "1",
"org_id": "…",
"data": { "balance": 124, "threshold_percent": 25, "monthly_credits": 500 }
}

Headers: VC-Signature, VC-Event-Id, VC-Event-Type, VC-Delivery-Id. A resent delivery keeps its event id, so use it to ignore duplicates.

VC-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of t + "." + rawBody with the endpoint’s signing secret (whsec_…). Reject requests that don’t match or are more than 5 minutes old.

import crypto from "node:crypto";
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));
}

Use the raw body exactly as received; parsing and re-serializing the JSON changes it.

  • Reply with any 2xx within 10 seconds. Redirects aren’t followed.
  • Failures are retried after 1 min, 5 min, 15 min, 30 min, 1 h, 2 h, 3 h, 4 h, 6 h, and 7 h: 11 attempts over about 24 hours.
  • If the last retry fails and nothing has succeeded for a day, the endpoint is turned off and owners and admins are emailed. Turn it back on in the dashboard and resend missed deliveries from its log.
  • Delivery logs are kept for 30 days.
Terminal window
curl https://api.validationcheck.net/v1/webhook-endpoints \
-H "Authorization: Bearer $VC_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/webhooks/validationcheck", "events": ["credits.low", "credits.depleted"]}'

The response includes the secret once. GET /v1/webhook-endpoints lists endpoints, POST /v1/webhook-endpoints/{id}/rotate-secret makes a new secret, and DELETE /v1/webhook-endpoints/{id} removes one. Endpoint URLs must be public https URLs. You can have up to 10.