Guides

Webhook integration

Set up a webhook URL, verify messages, and see example payloads.

Outbound webhooks notify your systems when Furnace events occur. Furnace POSTs JSON to your HTTPS endpoint; your endpoint must return any 2xx response.

New to webhooks? Start with the short Webhooks concept page.

Quick start

  1. Open Account Settings → Webhooks (or a campaign override in Mission Control).
  2. Configure — paste an HTTPS URL, optionally set a signing secret, and select individual events (expand groups to pick specific types). Only selected event types are delivered.
  3. Click Next to open the Test step. Use View sample to inspect JSON for each event type, then Send test webhook to POST a sample to your URL.
  4. Click Done to save. Deliveries start immediately when matching events occur.

Campaign overrides replace the account URL (and optionally secret or enabled events) for that campaign only. Leave the override URL empty to inherit the account default.

Receiving webhooks

Furnace sends:

POST {your_url}
Content-Type: application/json
X-Furnace-Event: email.sent
X-Furnace-Delivery: {delivery_id}
X-Furnace-Signature: sha256=...   # when a signing secret is configured

Body envelope:

{
  "id": "event-uuid",
  "type": "email.sent",
  "occurred_at": "2026-06-25T12:00:00.000Z",
  "data": { ... }
}
  • id — unique event id (stable across delivery retries for that event).
  • type — event constant (matches X-Furnace-Event).
  • occurred_at — ISO-8601 timestamp.
  • data — event-specific payload (see Webhook events in the sidebar).

Test webhooks

When you use Send test webhook in Furnace, the payload uses real event types with "test": true inside data. The examples in this guide show the live shape (no test field).

Retries and failures

Furnace retries failed deliveries up to 3 times. Your endpoint must return any 2xx HTTP status. Non-2xx responses or network errors are recorded in Account Settings → Failed deliveries.

Use X-Furnace-Delivery as a unique delivery id for idempotency on your side.

Verifying signatures

When a signing secret is configured, Furnace sets X-Furnace-Signature to sha256= followed by the hex-encoded HMAC-SHA256 of the raw JSON request body (exact bytes POSTed).

Node.js example:

import crypto from 'node:crypto';

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

Most no-code tools (Zoho Flow, Zapier, Make) can ignore the signature and accept the POST directly.

Single actions vs bulk

A single action (adding one person, one send) fires its own event. A bulk action (an import, or anything touching more than one person at once) fires one completion event for the whole operation instead of one per person.

Per-row lead.created / lead.updated / lead.deleted events are never emitted during bulk processing.

Single actions

ActionEvent
POST /v1/campaigns/{id}/leads (single)lead.created / lead.updated
PATCH …/leads/{leadId}lead.updated
DELETE …/leads/{leadId}lead.deleted
Campaign pause/stop/resumecampaign.paused / campaign.stopped / campaign.resumed
Worker: email sent, reply, bounceemail.sent / reply.received / bounce.detected
Block list add or removeblocklist.entry_added / blocklist.entry_removed
Thread category assign/change/clearreply.categorized

Bulk actions

OperationCompletion event
api_lead_import / csv_lead_import_stagedlead.bulk_import.completed
add_to_campaignlead.added_to_campaign.completed
remove_from_campaignlead.removed_from_campaign.completed
remove_from_all_campaignslead.removed_from_all_campaigns.completed
add_to_lead_listlead.added_to_list.completed
remove_from_lead_listlead.removed_from_list.completed
export_leadslead.export.completed
pause_enrollmentsenrollment.pause_completed
resume_enrollmentsenrollment.resume_completed

Sync bulk shortcuts use the same completion events with source: "sync" and job_id: null. Batch completion data matches the BatchCompletionWebhookPayload schema in the Schemas section.

enrollment.created and enrollment.updated are not emitted.

Campaign overrides

When a webhook event includes a campaign_id, Furnace resolves delivery settings in this order:

  1. URL — campaign webhook_url_override if set, otherwise account webhook_url. If no URL is configured, the event is not delivered.
  2. Signing secret — campaign override if set, otherwise account secret.
  3. Enabled events — campaign webhook_enabled_events_override if set (array), otherwise account webhook_enabled_events. If the resolved list is empty, no events are delivered. If non-empty, only listed types are delivered.

When the campaign override URL is empty, the account URL and account signing secret are used.

Shared lead identity fields

Every lead-scoped email-activity event (email.sent, reply.received, reply.categorized, bounce.detected) repeats the same identity block so a CRM can match a contact without a follow-up API call. Block list email examples include this block when a lead exists; domain examples do not.

FieldNotes
emailLead address for CRM matching. Reply events also keep from_email.
mailbox_emailSending or receiving inbox.
campaign_nameHuman-readable campaign name.
first_name, last_name, full_name, company_name, title, website, linkedin_urlPresent only when stored on the lead. title is promoted from custom_lead_data.
custom_fieldsNested object of leads.custom_lead_data. Keys that collide with reserved fields stay nested.
custom_fields_truncatedtrue only when custom_fields exceeded the 8 KB byte budget.

Empty or whitespace-only values are omitted. Furnace never sends "" for these fields. custom_fields is capped at 8192 UTF-8 bytes; overflow keys are dropped and custom_fields_truncated is set. body_text is capped at 16,000 characters.

No-code tools (Zoho Flow, Zapier, Make)

  1. Create an incoming webhook trigger in your tool and copy its HTTPS URL.
  2. Paste the URL in Furnace Account Settings → Webhooks and enable Email activity (or other groups you need).
  3. On the Test step, send email.sent or reply.received and map fields from the sample JSON.
  4. No echo-token or custom verification handler is required.

Event payloads

Live JSON examples for every event type:

Troubleshooting

SymptomLikely cause
No webhooks receivedURL empty, event type filtered out, or campaign override blocking delivery
Test works, live events missingEvent group not enabled, or non-2xx response on live delivery
Duplicate deliveriesRetries after timeout; dedupe on X-Furnace-Delivery
Signature verification failsBody parsed/re-serialized before verify; use raw body bytes