Integrations

Webhooks

Cleanmails fires real-time webhook events when things happen in your outreach — emails sent, opened, clicked, replied, bounced, or leads unsubscribed. Use webhooks to sync with your CRM, trigger automations, or build custom dashboards.

Event Types

EventFired When
email.sentAn email is successfully delivered
email.openedRecipient opens the email (non-bot)
email.clickedRecipient clicks a tracked link (non-bot)
email.repliedRecipient replies to the email
email.bouncedEmail bounces (hard or soft)
lead.unsubscribedLead clicks unsubscribe
campaign.startedCampaign status changes to running
campaign.completedAll leads processed through all steps
campaign.pausedCampaign is paused (manual or auto)
lead.createdNew lead added to a list
lead.validatedLead email validation completed

Creating a Webhook

bash
curl -X POST http://YOUR_SERVER/v1/integrations/webhooks \
  -H "Content-Type: application/json" \
  -H "Cookie: auth_token=YOUR_SESSION" \
  -d '{
    "url": "https://your-app.com/webhook/cleanmails",
    "secret": "your-signing-secret",
    "events": ["email.sent", "email.replied", "email.bounced"]
  }'
Event filtering

Pass an empty events array or omit it to receive all events. Use specific event types to reduce noise.

Webhook Payload

POST to your URL
{
  "event": "email.replied",
  "workspace_id": 1,
  "timestamp": "2026-05-12T14:30:00Z",
  "data": {
    "lead_id": 42,
    "lead_email": "john@example.com",
    "lead_name": "John Smith",
    "lead_company": "Acme Inc",
    "campaign_id": 5,
    "campaign": "Q2 Outreach",
    "sender_id": 3,
    "sender": "alex@outreach.yourcompany.com",
    "message_id": "<abc123@outreach.yourcompany.com>"
  }
}

Signature Verification

If you provide a secret when creating the webhook, every delivery includes an HMAC-SHA256 signature in the header:

text
X-Webhook-Signature: sha256=a1b2c3d4e5f6...

Verify it server-side:

Node.js verification
const crypto = require('crypto');

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

Retry & Auto-Pause

  • Retries: 3 attempts with exponential backoff (2s → 4s → 8s)
  • Auto-pause: After 10 consecutive failures, the webhook is automatically paused
  • Re-enable: Update the webhook status to "active" to reset the failure counter

Testing

bash
# Send a test ping to your webhook
curl -X POST http://YOUR_SERVER/v1/integrations/webhooks/WEBHOOK_ID/test \
  -H "Cookie: auth_token=YOUR_SESSION"