Webhooks

Receive real-time HTTP callbacks when email validations complete. Configure webhook endpoints, verify signatures, and handle delivery failures.

Webhooks

Webhooks let you receive HTTP POST callbacks the moment a validation event occurs, with no polling required. MailCop sends a signed JSON payload to your endpoint.

Managing Webhooks

Open Webhooks from the sidebar to create, edit, or delete webhook endpoints.

Creating a Webhook

  1. Click Add Webhook
  2. Enter a Name (for your reference), URL (your HTTPS endpoint), and select the Events to subscribe to
  3. Copy the full Secret Token shown in the banner immediately after creation. It is shown only once; afterwards only a masked version is displayed.

Regenerating a Secret Token

Open the webhook, click Regenerate next to the secret token. The new full token is shown once on the next screen; copy it then. All subsequent deliveries use the new token.

Deleting a Webhook

Click Delete on the webhook list. Delivery stops immediately.


Events

Event Fires when
api.validation_completed A single-email API call to /v1/verify completes
email_list.started A bulk list upload begins processing
email_list.completed A bulk list finishes validation
email_list.failed A bulk list fails (e.g. malformed CSV)

Example Payloads

api.validation_completed

{
  "event_type": "api.validation_completed",
  "timestamp": "2026-01-01T12:00:00Z",
  "data": {
    "email": "[email protected]",
    "validation_type": "mx",
    "result": { "status": "good", "credits_used": 1 }
  },
  "webhook": { "name": "My Webhook" }
}

email_list.completed

{
  "event_type": "email_list.completed",
  "timestamp": "2026-01-01T12:05:00Z",
  "data": {
    "email_list": {
      "id": "el_abc123",
      "title": "January List",
      "validation_type": "mx",
      "total_emails": 500,
      "good_emails": 480,
      "bad_emails": 20,
      "status": "completed",
      "completed_at": "2026-01-01T12:05:00Z"
    }
  },
  "webhook": { "name": "My Webhook" }
}

Verifying Signatures

Every delivery includes an X-Webhook-Signature header with an HMAC-SHA256 signature of the raw request body, prefixed with sha256=.

Always verify signatures before processing payloads.

Ruby

def valid_signature?(body, header, secret)
  expected = "sha256=#{OpenSSL::HMAC.hexdigest("sha256", secret, body)}"
  ActiveSupport::SecurityUtils.secure_compare(expected, header)
end

Node.js

const crypto = require("crypto");

function validSignature(body, header, secret) {
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(body).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}

Python

import hmac, hashlib

def valid_signature(body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header)

Failure Handling

MailCop retries failed deliveries up to 3 times with polynomial back-off. If all retries fail, the failure count increments by 1.

After 10 consecutive failures, the webhook is automatically disabled and you receive an email notification. To re-enable it, edit the webhook and toggle it back on. The failure counter resets to zero when you do.

A successful delivery resets the failure counter to zero.


Minimal Receiver Example

# Rails controller
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def mailcop
    body   = request.raw_post
    header = request.headers["X-Webhook-Signature"]
    secret = ENV["MAILCOP_WEBHOOK_SECRET"]

    unless valid_signature?(body, header, secret)
      head :unauthorized and return
    end

    payload = JSON.parse(body)
    # process payload["event_type"] ...
    head :ok
  end

  private

  def valid_signature?(body, header, secret)
    expected = "sha256=#{OpenSSL::HMAC.hexdigest("sha256", secret, body)}"
    ActiveSupport::SecurityUtils.secure_compare(expected, header)
  end
end