Skip to main content

Overview

Webhooks notify your system when an email verification job completes or when an entire task finishes. Configure a webhook_url when creating a batch task or uploading a file. Webhook URLs must be HTTPS. You can receive two webhook types:
  • Per-email webhook: sent for each email as it completes (or is skipped).
  • Task completion webhook: sent once when the entire task finishes.
You can distinguish types by the data shape: per-email payloads include data.job, while task completion payloads include data.stats and data.jobs.

Delivery

Your endpoint receives an HTTP POST with JSON. Respond with a 2xx to acknowledge receipt. We retry on timeouts, connection errors, and 429/502/503/504 responses.

Headers

Each webhook includes:
  • Content-Type: application/json
  • User-Agent: EmailVerification/1.0
  • X-Webhook-ID: unique UUID per delivery
  • X-Webhook-Timestamp: Unix seconds when the request is sent
  • X-Webhook-Signature: HMAC-SHA256 of the raw request body (only when WEBHOOK_SECRET_KEY is configured). Format: sha256=<hex>

Signature verification

Compute the HMAC-SHA256 over the raw request body using your shared secret and compare to X-Webhook-Signature.
expected = "sha256=" + hex(hmac_sha256(secret, raw_body))

Payload format (HTTP request body)

All webhooks use the same outer envelope:
{
  "event_type": "email_verification_completed",
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "completed_at": "2025-01-01T12:05:00Z",
  "data": {}
}
Field notes:
  • event_type is currently always email_verification_completed.
  • upload_id is optional and omitted in most current webhook payloads.
  • completed_at is when the webhook is sent (UTC).
  • data depends on webhook type (see below).

Per-email webhook payload

Sent once per email address. data contains the task context and a job object for the email.
{
  "event_type": "email_verification_completed",
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "completed_at": "2025-01-01T12:01:12Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "user_id": "550e8400-e29b-41d4-a716-446655440999",
    "source": "frontend",
    "created_at": "2025-01-01T12:00:00Z",
    "updated_at": "2025-01-01T12:01:12Z",
    "job": {
      "email_address": "user@example.com",
      "status": "completed",
      "created_at": "2025-01-01T12:00:10Z",
      "updated_at": "2025-01-01T12:01:12Z",
      "email": {
        "id": "550e8400-e29b-41d4-a716-446655440222",
        "status": "valid",
        "address": "user@example.com",
        "local": "user",
        "domain": "example.com",
        "verification_steps": [
          { "step": "syntax", "status": "completed" },
          { "step": "domain", "status": "completed" },
          { "step": "smtp", "status": "completed" }
        ]
      }
    }
  }
}
Notes:
  • source and user_id are present when available; they may be omitted in some per-email sends.
  • job.email can be null or omitted if email details are unavailable.
  • job.skipped is included when an email is skipped.
  • verification_steps may be empty or omitted if no steps are recorded.

Task completion webhook payload

Sent once when all jobs for a task finish. data contains aggregated stats and the list of jobs.
{
  "event_type": "email_verification_completed",
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "completed_at": "2025-01-01T12:05:00Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "user_id": "550e8400-e29b-41d4-a716-446655440999",
    "source": "frontend",
    "created_at": "2025-01-01T12:00:00Z",
    "updated_at": "2025-01-01T12:05:00Z",
    "stats": {
      "total": 2,
      "pending": 0,
      "processing": 0,
      "completed": 2,
      "failed": 0
    },
    "jobs": [
      {
        "email_address": "user@example.com",
        "status": "completed",
        "created_at": "2025-01-01T12:00:10Z",
        "updated_at": "2025-01-01T12:01:12Z",
        "email": {
          "id": "550e8400-e29b-41d4-a716-446655440222",
          "status": "valid",
          "address": "user@example.com",
          "local": "user",
          "domain": "example.com",
          "verification_steps": [
            { "step": "syntax", "status": "completed" },
            { "step": "domain", "status": "completed" },
            { "step": "smtp", "status": "completed" }
          ]
        }
      },
      {
        "email_address": "ops@example.com",
        "status": "completed",
        "created_at": "2025-01-01T12:00:10Z",
        "updated_at": "2025-01-01T12:01:30Z",
        "email": {
          "id": "550e8400-e29b-41d4-a716-446655440223",
          "status": "valid",
          "address": "ops@example.com",
          "local": "ops",
          "domain": "example.com",
          "verification_steps": [
            { "step": "syntax", "status": "completed" },
            { "step": "domain", "status": "completed" },
            { "step": "smtp", "status": "completed" }
          ]
        }
      }
    ]
  }
}
Notes:
  • jobs can be large for big tasks; consider streaming/async processing on your side.
  • verification_steps may be empty or omitted if no steps are recorded.