intercom-webhooks-events
'Implement Intercom webhook handling and data event tracking.
Allowed Tools
Provided by Plugin
intercom-pack
Claude Code skill pack for Intercom (24 skills)
Installation
This skill is included in the intercom-pack plugin:
/plugin install intercom-pack@claude-code-plugins-plus
Click to copy
Instructions
Intercom Webhooks & Events
Overview
Handle incoming Intercom webhooks (notifications) with signature verification and implement outbound data event tracking via the Events API. Incoming webhooks push conversation and contact changes to your endpoint; outbound data events push custom activity into Intercom for segmentation and messaging.
The full, copy-ready code lives in references/; this file walks the workflow at a high level so you can follow it start to finish, then drill in for depth.
Prerequisites
- HTTPS endpoint accessible from internet
- Webhook secret from Intercom Developer Hub
- Access token from Intercom Developer Hub (for outbound data events)
intercom-clientSDK installed- Redis or database for idempotency (recommended)
Authentication
Two distinct credentials, both issued from the Intercom Developer Hub and read from environment variables — never hard-code them:
- Incoming webhooks are verified with
INTERCOMWEBHOOKSECRET. Intercom signs every delivery with HMAC-SHA1 in theX-Hub-Signatureheader; you recompute the digest over the raw request body and compare withcrypto.timingSafeEqual. Reject any request that fails or is missing the header. - Outbound data events authenticate with a bearer
INTERCOMACCESSTOKENpassed toIntercomClient.
Instructions
Read the relevant reference file, then use Write/Edit to scaffold the handler into the target project.
- Build the signed endpoint. Create an Express route that captures the raw body (
express.raw), verifies theX-Hub-SignatureHMAC-SHA1 digest againstINTERCOMWEBHOOKSECRET, and returns200within 5 seconds — Intercom treats a slower response as a failure. Respond first, process after. See incoming webhooks walkthrough Step 1. - Model the payload. Every delivery is a
notification_eventenvelope carryingtopic,id, anddata.item(the changed resource). Type it so the router is safe. See incoming webhooks walkthrough Step 2. - Route by topic. Dispatch on
notification.topicthrough a handler map; log and no-op unknown topics rather than throwing. Pick topics from the topics reference. See incoming webhooks walkthrough Step 3. - Add idempotency. Intercom retries a failed delivery once after ~1 minute. Guard each
notification.idwith a RedisSET NXlock so retries never double-process; release the lock on failure to allow a genuine retry. See incoming webhooks walkthrough Step 4. - Track outbound events. Submit custom activity with
client.dataEvents.createusing a past-tenseverb-nouneventName, a UnixcreatedAt, the contact's externaluserId, and optionalmetadata. See data events walkthrough Step 5. - Submit in bulk when needed. There is no batch events endpoint — throttle individual
dataEvents.createcalls (e.g. a short delay every 50) and tally succeeded/failed. See data events walkthrough Step 6.
Output
A working integration produces:
- A signed
/webhooks/intercomendpoint that returns200 {"received": true}on valid deliveries and401on missing/invalid signatures. - Per-topic handler execution logged as
Processed, with unknown topics logged and skipped.: - Idempotent processing — duplicate
notification.iddeliveries are skipped withDuplicate webhook skipped:. - Outbound
dataEvents.createcalls that appear on the contact's Intercom timeline; bulk runs return{ succeeded, failed }counts.
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Invalid signature | Secret mismatch | Verify secret matches Developer Hub |
| Timeout (5s) | Slow processing | Queue events, respond immediately |
| Duplicate events | Retry delivery | Implement idempotency with Redis |
| Missing topic handler | New topic added | Log unhandled topics, add handler |
| Event rejected (422) | Invalid userid or eventname | Verify contact exists, use verb-noun naming |
Examples
Verify a signature and respond fast (skeleton — full version in incoming webhooks):
const expected = "sha1=" + crypto
.createHmac("sha1", process.env.INTERCOM_WEBHOOK_SECRET!)
.update(req.body) // raw Buffer, not parsed JSON
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).json({ error: "Invalid signature" });
}
res.status(200).json({ received: true }); // respond within 5s, then process async
Track a custom data event (full version in data events):
await client.dataEvents.create({
eventName: "completed-onboarding", // past-tense verb-noun
createdAt: Math.floor(Date.now() / 1000),
userId: "user-12345", // contact's external ID
metadata: { steps_completed: 5, plan: "pro" },
});
More: incoming webhooks · data events · webhook topics.
Resources
Next Steps
For performance optimization once the webhook endpoint and event pipeline are live, see the intercom-performance-tuning skill to profile throughput and tune rate limits.