Guide

Intercom webhook API the boring parts you can’t skip

Implementing Intercom webhooks feels like “just add an endpoint and process the request”. In production it turns into local dev pain, verification pitfalls, retries, and replay tooling.

Looking for a production workflow? Start with CRM webhooks .

You add a new endpoint, parse JSON, insert into your DB, call your CRM, and return 200. Done.

Then you try to debug locally. Signatures fail because your framework changed the body. Deployments cause timeouts. Retries become duplicates. A traffic spike drops half of your deliveries.

Normal flow

  1. Intercom calls your public endpoint.
  2. You parse the request and do real work inline (DB, CRM, side effects).
  3. Timeouts/5xx/network issues trigger retries.
  4. Retries become duplicates unless you designed idempotency.
  5. When something fails, you may not have the exact payload to replay.

With Hooque

  1. Intercom delivers to Hooque ingest (fast accept).
  2. Verification happens at ingest (or in your worker, depending on provider).
  3. Events land in a durable queue immediately.
  4. Your worker pulls locally or in production and acks/nacks/rejects explicitly.
  5. You inspect and redeliver from the UI after fixes.

Minimal consumer loop

Keep the provider delivery path simple. Pull events from the queue and control outcomes explicitly. Full local-dev guidance lives in the local development spoke .

// Minimal consumer loop (Node 18+)
// 1) GET next message
// 2) parse X-Hooque-Meta (ackUrl/nackUrl/rejectUrl)
// 3) POST ackUrl (or nack/reject on failure)
const nextUrl =
  process.env.HOOQUE_QUEUE_NEXT_URL ??
  "https://app.hooque.io/queues/cons_provider_events/next";
const token = process.env.HOOQUE_TOKEN ?? "hq_tok_replace_me";
const headers = { Authorization: `Bearer ${token}` };

const resp = await fetch(nextUrl, { headers });
if (resp.status === 204) process.exit(0);
if (!resp.ok) throw new Error(`next() failed: ${resp.status}`);

const payload = await resp.json();
const meta = JSON.parse(resp.headers.get("X-Hooque-Meta") ?? "{}");

try {
  await handle(payload); // your code
  await fetch(meta.ackUrl, { method: "POST", headers });
} catch (err) {
  const permanent = false; // set based on error type
  const url = permanent ? meta.rejectUrl : meta.nackUrl;
  await fetch(url, {
    method: "POST",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify({ reason: String(err) }),
  });
}

How Hooque helps

Hooque is a managed webhook-to-queue layer: fast ingest, durable persistence, and explicit processing control.

  • Built-in provider verification at ingest (fail closed before payloads reach workers).
  • Durable queueing so deploys and incidents don’t drop deliveries.
  • Explicit Ack / Nack / Reject outcomes so retries are under your control.
  • Inspection + redelivery from a dashboard when processing fails.
  • Per-webhook and per-consumer metrics to power alerting and SLOs.
  • Burst protection for traffic spikes (buffer now, process at your pace).

Want the deeper implementation details? Jump to security, retries, and debugging.

FAQ

Quick answers for the questions that show up right before you ship.

How do I test Intercom webhooks locally?

General: Local webhook testing usually requires a stable HTTPS URL and a replay workflow (capture one payload, sanitize it, and replay deterministically). How Hooque helps: Point Intercom to a managed Hooque endpoint, then pull events locally from a queue and replay/redeliver from the UI when you fix a bug.

How do I verify Intercom webhook signatures?

General: Verify authenticity over the raw request bytes using Intercom's signing scheme and fail closed before side effects. How Hooque helps: Hooque can verify Intercom requests at ingest and only queue verified events, so workers never process unverified payloads.

Why do I get duplicate webhook events?

General: Retries happen whenever the sender can’t confirm processing (timeouts, 5xx, network). Treat deliveries as at-least-once and make your side effects idempotent. How Hooque helps: Your worker controls outcomes explicitly with Ack/Nack/Reject and can safely retry with backoff without losing events.

How do I replay a failed webhook after a fix?

General: You need the original payload and metadata saved somewhere, plus a deterministic way to re-run your handler without duplicating side effects. How Hooque helps: Inspect the exact payload in the dashboard and redeliver it, or replay by pulling from the queue and re-processing after a deploy.

How do I avoid downtime during deployments?

General: Keep the inbound endpoint fast and stable and move processing out of the request path so deploys don’t block deliveries. How Hooque helps: Providers deliver to Hooque, Hooque persists instantly, and your workers consume when they’re ready.

Start processing Intercom webhooks reliably

Capture every webhook, persist instantly, and consume via REST or SSE with full Ack/Nack/Reject control.

Start for free

No credit card required