API reference
Webhooks API
Create and manage webhooks programmatically to receive real-time notifications.
List webhooks
/v1/webhooks{
"success": true,
"data": {
"webhooks": [
{
"webhook_id": "wh_1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d",
"url": "https://your-app.com/webhook",
"description": "Production endpoint",
"events": ["job.completed", "job.failed"],
"is_active": true,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z",
"last_triggered_at": "2026-02-03T14:35:42Z",
"failure_count": 0
}
],
"count": 1
}
}Create webhook
/v1/webhooksRequest body
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Required | HTTPS URL to receive webhooks |
events | string[] | Required | Events to subscribe to. Only events with available: true in the catalog can be subscribed — the API rejects the rest |
description | string | Optional | Free-form label for the webhook |
curl -X POST https://api.kawaa.com/v1/webhooks \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhook",
"events": ["job.completed", "job.failed"]
}'The body accepts only url, events and description; any other key (for example event) returns HTTP 400 naming the supported fields rather than creating the webhook without it.
The response includes the webhook's signing secret — store it to verify the X-Kawaa-Signature header on deliveries. Deliverable events: verification.completed, verification.failed, job.completed, job.failed, credits.low, credits.exhausted, blacklist.detected and dmarc.failure. verification.* events fire for single verifications (POST /v1/verify) — fresh verifications only, since cached results return synchronously; bulk jobs emit job.* events instead. credits.low is delivered at most once per 7 days and credits.exhausted at most once per 24 hours.
Update webhook
/v1/webhooks/{id}Update an existing webhook. The body accepts url, events, description and is_active (set it to false to pause deliveries without deleting the webhook). Only the fields you send change. The read-only fields a GET returns (webhook_id, created_at, updated_at, last_triggered_at, failure_count) are accepted as no-ops so a GET payload can be sent straight back; any other key returns HTTP 400.
Get webhook
/v1/webhooks/{id}Return one webhook in the same shape as the list entries above. The signing secret is never included — it is shown once on creation and again only by the regenerate call below. An id that belongs to another account is a 404.
Delivery logs
/v1/webhooks/{id}/logsList the deliveries attempted to this webhook, newest first. Logs are kept for 30 days.
Query parameters
| Parameter | Description |
|---|---|
limit | Entries per page (default: 20, max: 100) |
offset | Entries to skip (default: 0, max: 1,000) |
cursor | Opaque cursor from the previous page's cursor, for paging past the offset bound |
{
"success": true,
"data": {
"logs": [
{
"log_id": "3f6c1a2e-8d4b-4b1f-9a70-5c2e7d1b9f11",
"event": "job.completed",
"status": "success",
"status_code": 200,
"response_time_ms": 184,
"request_body": "{\"event\":\"job.completed\",\"data\":{...}}",
"response_body": "OK",
"error_message": null,
"created_at": "2026-02-03T14:35:42.100Z",
"delivery_type": "event"
}
],
"total": 1,
"limit": 20,
"offset": 0
}
}status is success or failed; status_code and response_body are null when no HTTP response was received (timeout, DNS or connection error — see error_message). delivery_type distinguishes real event deliveries from test sends. A failed entry can be re-sent with POST /v1/webhooks/{id}/logs/{log_id}/retry.
Delete webhook
/v1/webhooks/{id}Remove a webhook subscription.
Test webhook
/v1/webhooks/{id}/testSend a test event to verify your webhook endpoint is working correctly.
Regenerate secret
/v1/webhooks/{id}/secretGenerate a new signing secret for the webhook. The old secret will be immediately invalidated.
Available events
/v1/webhooks/eventsGet the webhook event catalog. Each event is an object with an available flag — only events with available: true can be subscribed to.
{
"success": true,
"data": {
"events": [
{
"name": "job.completed",
"description": "Triggered when a bulk verification job is completed",
"category": "jobs",
"available": true,
"payload_example": {
"event": "job.completed",
"data": {
"job_id": "job_abc123",
"total_emails": 1000,
"summary": { "valid": 800, "invalid": 150, "risky": 50 },
"completed_at": "2025-01-01T00:00:00Z"
}
}
},
{
"name": "verification.completed",
"description": "Triggered when a single email verification completes",
"category": "verification",
"available": true,
"payload_example": {
"event": "verification.completed",
"data": {
"job_id": "job_abc123",
"email": "user@example.com",
"status": "valid",
"quality_score": 95,
"verified_at": "2025-01-01T00:00:00Z",
"credits_used": 1
}
}
}
]
}
}