Appearance
Webhooks
DISIFY can send your server a signed JSON notification when a bulk validation completes. Delivery is best-effort, so retain polling as a fallback.
Webhooks are configured per account, so they require an API account — they aren't available for anonymous usage.
Setup
- Add a public HTTPS URL in Webhooks. Local/private addresses are not accepted, and redirects are not followed.
- Save the generated signing secret in your server configuration. The full secret is shown once; regenerating it replaces the old secret immediately.
- Select the events you want. An empty selection subscribes to all events; remove the URL to disable delivery.
Events
| Event | When it fires |
|---|---|
test.ping | Sent on demand from your dashboard to verify the endpoint works. |
bulk.completed | A synchronous or asynchronous bulk validation has completed. |
Payload
Every delivery is an HTTP POST with a JSON body in this envelope:
json
{
"event": "bulk.completed",
"timestamp": "2026-07-13T12:00:00+00:00",
"data": {
"session": "d117271ce938bf91bc718f6cfb7954de",
"stats": {
"total": 8000,
"unique": 8000,
"invalid_format": 12,
"invalid_dns": 40,
"disposable": 156,
"valid": 7792,
"session": "d117271ce938bf91bc718f6cfb7954de"
}
}
}event— the event name (also sent as theX-Disify-Eventheader).timestamp— ISO 8601 time the event was dispatched.data— event-specific payload.bulk.completedincludes thesessionand summary statistics. The address list is not embedded; retrieve it throughGET /api/view/{session}within the 15-minute session lifetime, using the submitting backend or account.
Request headers
| Header | Value |
|---|---|
Content-Type | application/json |
X-Disify-Event | The event name, e.g. bulk.completed |
X-Disify-Signature | HMAC-SHA256 signature of the raw request body (see below) |
User-Agent | Disify-Webhook/1.0 |
Verifying signatures
X-Disify-Signature is a lowercase hexadecimal HMAC-SHA256 of the raw request body, keyed with your signing secret. Pass the exact bytes, the header value (or an empty string if absent), and the server-configured secret to a verifier:
javascript
import { createHmac, timingSafeEqual } from "node:crypto";
function verifySignature(rawBody, signature, secret) {
if (!secret || typeof signature !== "string" ||
signature.length !== 64 || !/^[0-9a-f]{64}$/.test(signature)) {
return false;
}
const expected = createHmac("sha256", secret).update(rawBody).digest();
return timingSafeEqual(expected, Buffer.from(signature, "hex"));
}php
function verifySignature(string $rawBody, string $signature, string $secret): bool
{
if ($secret === '' || !preg_match('/\A[0-9a-f]{64}\z/', $signature)) {
return false;
}
return hash_equals(hash_hmac('sha256', $rawBody, $secret), $signature);
}python
import hashlib
import hmac
import re
def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
if not secret or not re.fullmatch(r"[0-9a-f]{64}", signature):
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)Reject a failed signature before processing the event. Configure your framework to preserve the raw body; JSON re-encoding changes the signed bytes. After verification, parse and validate the payload. Use its signed timestamp to enforce an appropriate freshness window, and deduplicate bulk.completed by event name and session.
Delivery behavior
- The HTTP delivery timeout is 10 seconds. Verify the signature, durably enqueue the event, then return
2xx; process longer work asynchronously. - There are no automatic retries. Check your account's delivery log for status and errors; entries older than 7 days are pruned daily.
- If a notification is missed, poll the session returned by the original bulk request. Temporary results still expire after 15 minutes.
Testing
Send a test.ping from the dashboard and check the delivery log. Ensure your event selection includes test.ping (or all events). In automated tests, cover valid, tampered, missing-signature, and duplicate events with local fixtures.