Webhooks let EmailKit send real-time notifications to your server when events happen — like when a bulk verification job completes or your credits are running low. Instead of polling our API, you receive instant push notifications.
Event | Description |
|---|---|
| A single email verification has completed |
| A bulk verification job has finished processing |
| Your credit balance has dropped below a threshold |
Go to API Keys in the sidebar, then the Webhooks tab
Click Create Webhook
Enter the URL where you want to receive notifications (must be HTTPS)
Select which events to subscribe to
Choose payload mode: Full (complete verification data) or Summary (status only)
Click Create
Copy the signing secret — it's only shown once
curl -X POST https://api.emailkit.dev/api/v1/webhooks \
-H "Authorization: Bearer ek_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/emailkit",
"events": ["bulk.completed", "credits.low"]
}'Response:
{
"webhook": {
"id": "wh_abc123",
"url": "https://your-server.com/webhooks/emailkit",
"events": ["bulk.completed", "credits.low"],
"createdAt": "2026-01-15T10:30:00Z"
},
"signingSecret": "whsec_abc123..."
}When an event occurs, EmailKit sends a POST request to your webhook URL:
{
"event": "verification.completed",
"timestamp": "2026-01-15T10:30:00Z",
"data": {
"email": "user@example.com",
"status": "deliverable",
"reason": "valid_mailbox"
}
}{
"event": "bulk.completed",
"timestamp": "2026-01-15T10:30:00Z",
"data": {
"jobId": "job_abc123",
"totalCount": 5000,
"processedCount": 5000,
"resultsUrl": "/api/v1/verify/bulk/job_abc123/results"
}
}{
"event": "credits.low",
"timestamp": "2026-01-15T10:30:00Z",
"data": {
"currentBalance": 50,
"threshold": 100
}
}Every webhook request includes a signature header so you can verify it came from EmailKit and wasn't tampered with:
X-EmailKit-Signature: sha256=abc123...To verify the signature:
Get the raw request body
Compute an HMAC-SHA256 using your signing secret and the request body
Compare it to the signature in the header
const crypto = require('crypto');
function verifyWebhookSignature(body, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
app.post('/webhooks/emailkit', (req, res) => {
const signature = req.headers['x-emailkit-signature'];
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
'whsec_your_signing_secret'
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
const { event, data } = req.body;
console.log(`Received event: ${event}`, data);
res.status(200).send('OK');
});curl https://api.emailkit.dev/api/v1/webhooks \
-H "Authorization: Bearer ek_your_api_key"curl -X DELETE https://api.emailkit.dev/api/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer ek_your_api_key"Always verify signatures — Never process a webhook without verifying the signature
Respond with 200 quickly — Process webhooks asynchronously; return 200 immediately, then handle the event in the background
Handle duplicates — Webhooks may be delivered more than once. Use the event timestamp or a unique ID to deduplicate
Use HTTPS — Webhook URLs must use HTTPS for security
Monitor your endpoint — If your endpoint is consistently failing, webhook delivery may be paused