Gareth Wilson Gareth Wilson

Guide to Intercom Webhooks: Features and Best Practices

Published


Intercom webhooks notify your application when conversations, contacts, and tickets change. If you're routing support events, syncing contacts to a CRM, or mirroring tickets, webhooks are how you react without polling.

This guide covers how Intercom webhooks work, the topics you'll subscribe to, how to verify the X-Hub-Signature (it's SHA-1), and the ping handshake.

What are Intercom webhooks?

Intercom webhooks are JSON POSTs delivered to a URL you configure in the Developer Hub. Each is signed with an X-Hub-Signature header formatted sha1=<hex>. The signature is an HMAC-SHA1 (a legacy scheme, not the modern SHA-256) over the raw request body, keyed with your app's client_secret, not a separate webhook secret and not the access token.

Intercom webhook features

FeatureDetails
ConfigurationDeveloper Hub > your app > Webhooks; subscribe to topics
Signature headerX-Hub-Signature, formatted sha1=<hex>
Signature schemeHMAC-SHA1 (hex) over the raw body, keyed with the app client_secret
Setup handshakeA signed ping on save; verify it and return 2xx
IdempotencyDedupe on notification.id
SDKNone for verification; verify manually

Common events

Intercom calls its events topics. A representative set:

TopicFires when
pingIntercom validates the endpoint on save
conversation.user.created / conversation.user.repliedA user starts or replies to a conversation
conversation.admin.replied / .assigned / .closed / .notedAn admin acts on a conversation
contact.user.created / contact.lead.createdA contact or lead is created
contact.user.tag.createdA contact is tagged
ticket.created / ticket.admin.assigned / ticket.state.updatedA ticket changes

This isn't exhaustive; subscribe to the topics your use case needs in the Developer Hub.

See Intercom webhook payloads in action. Inspect and replay sample Intercom webhook payloads in the Hookdeck Console — no account or setup required.

Setting up Intercom webhooks

In the Developer Hub, open your app > Webhooks, set the endpoint URL, choose the API version, and select topics. Get the signing key from Basic Information > Client secret and store it as INTERCOM_CLIENT_SECRET. Rotating the client secret invalidates signatures for all webhooks, so rotate and update the env in lockstep.

Securing Intercom webhooks

Split the X-Hub-Signature on =, confirm the algorithm is sha1, compute an HMAC-SHA1 over the raw body with your client_secret, hex-encode it, and compare in constant time. Verify against the raw body before parsing.

const crypto = require("crypto");

const CLIENT_SECRET = process.env.INTERCOM_CLIENT_SECRET;

function verify(rawBody, signatureHeader) {
  if (!signatureHeader) return false;
  const [algorithm, signature] = signatureHeader.split("=");
  if (algorithm !== "sha1" || !signature) return false;

  const expected = crypto.createHmac("sha1", CLIENT_SECRET).update(rawBody).digest("hex");
  try {
    return crypto.timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
  } catch {
    return false;
  }
}

app.post("/webhooks/intercom", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.headers["x-hub-signature"])) {
    return res.sendStatus(401);
  }

  const payload = JSON.parse(req.body.toString());
  if (payload.topic === "ping") return res.sendStatus(200); // handshake

  res.sendStatus(200); // acknowledge fast
  processQueue.add(payload); // dedupe on notification.id, async
});

The same check in Python:

import hashlib
import hmac
import os

CLIENT_SECRET = os.environ["INTERCOM_CLIENT_SECRET"].encode()

def verify(raw_body: bytes, signature_header: str) -> bool:
    if not signature_header:
        return False
    try:
        algorithm, signature = signature_header.split("=", 1)
    except ValueError:
        return False
    if algorithm != "sha1" or not signature:
        return False
    expected = hmac.new(CLIENT_SECRET, raw_body, hashlib.sha1).hexdigest()
    return hmac.compare_digest(signature, expected)

Make Intercom webhooks production-ready. Hookdeck Event Gateway verifies the X-Hub-Signature, deduplicates, and durably queues every event.

Intercom webhook limitations and pain points

It's SHA-1, not SHA-256

The Problem: X-Hub-Signature is HMAC-SHA1, the legacy scheme (not GitHub's modern X-Hub-Signature-256). Using SHA-256 never matches.

Why It Happens: Intercom uses the older SHA-1 X-Hub-Signature.

Workarounds:

  • Use SHA-1, hex, over the raw body, and strip the sha1= prefix.

How Hookdeck Can Help: Hookdeck verifies the signature at the edge with the right algorithm, so your app doesn't hard-code SHA-1.

The key is the app client_secret

The Problem: There's no separate webhook signing secret. Signatures are keyed with the app's client_secret, and using the access token (a common mix-up) fails.

Why It Happens: Intercom reuses the app client secret for webhook signing.

Workarounds:

  • Key the HMAC with client_secret from Basic Information, not the access token.

How Hookdeck Can Help: Hookdeck holds the secret and verifies centrally, so the right key is used in one place.

The ping handshake

The Problem: On save, Intercom sends a signed ping. A handler that tries to process it as a data event, or that skips verification for it, mishandles the setup check.

Why It Happens: Intercom validates the endpoint with a ping when you save the webhook.

Workarounds:

  • Verify the ping like any delivery, then return 2xx without treating it as a data event.

How Hookdeck Can Help: Hookdeck can accept the ping and forward only real events to your handler.

No timestamp, so dedupe on notification.id

The Problem: There's no timestamp or replay window, and retries can redeliver the same event.

Why It Happens: Intercom's scheme has no timestamp; delivery is at-least-once.

Workarounds:

  • Dedupe on notification.id and make handlers idempotent.

How Hookdeck Can Help: Hookdeck deduplicates deliveries at the edge. See our guide to webhook idempotency.

Best practices

Verify HMAC-SHA1 over the raw body with client_secret

Confirm the sha1= prefix, compute the hex HMAC-SHA1 over the raw body, and compare in constant time.

Handle the ping

Verify the ping and return 2xx so the webhook saves, without processing it as data.

Dedupe on notification.id

Retries can redeliver, so make handlers idempotent.

Acknowledge fast, process asynchronously

Return 2xx quickly (Intercom expects a fast response) and defer work to a queue. See why to process webhooks asynchronously.

Conclusion

Intercom webhooks are verified with an X-Hub-Signature HMAC-SHA1 over the raw body, formatted sha1=<hex> and keyed with your app's client_secret. Handle the signed ping on save, verify over the raw body, dedupe on notification.id, and acknowledge fast.

Hookdeck verifies the signature, handles the ping, deduplicates, and durably queues every event at the edge, so your app only ever processes verified, unique events.

Get started with Hookdeck for free and handle Intercom webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.