技能 编程开发 Intercom Webhook处理与事件追踪

Intercom Webhook处理与事件追踪

v20260423
intercom-webhooks-events
本指南用于处理Incoming Intercom Webhook通知,包含严格的HMAC-SHA1签名验证步骤。它指导如何根据不同的事件主题(如联系人创建、对话更新)进行逻辑分支处理,并使用Redis实现幂等性机制,确保系统能够可靠地处理所有事件,适用于将Intercom数据集成到后台系统或CRM。
获取技能
283 次下载
概览

Intercom Webhooks & Events

Overview

Handle incoming Intercom webhooks (notifications) with signature verification and implement outbound data event tracking via the Events API.

Prerequisites

  • HTTPS endpoint accessible from internet
  • Webhook secret from Intercom Developer Hub
  • intercom-client SDK installed
  • Redis or database for idempotency (recommended)

Part 1: Incoming Webhooks (Notifications)

Step 1: Webhook Endpoint with Signature Verification

Intercom signs webhooks with HMAC-SHA1 via the X-Hub-Signature header.

import express from "express";
import crypto from "crypto";

const app = express();

// IMPORTANT: Use raw body for signature verification
app.post(
  "/webhooks/intercom",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const signature = req.headers["x-hub-signature"] as string;
    const secret = process.env.INTERCOM_WEBHOOK_SECRET!;

    if (!signature) {
      return res.status(401).json({ error: "Missing X-Hub-Signature" });
    }

    // Verify HMAC-SHA1 signature
    const expected = "sha1=" + crypto
      .createHmac("sha1", secret)
      .update(req.body)
      .digest("hex");

    if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
      console.error("Webhook signature verification failed");
      return res.status(401).json({ error: "Invalid signature" });
    }

    // MUST respond within 5 seconds or Intercom treats as failure
    // Parse and queue for async processing
    const notification = JSON.parse(req.body.toString());
    res.status(200).json({ received: true });

    // Process asynchronously after responding
    processWebhookAsync(notification).catch(console.error);
  }
);

Step 2: Notification Payload Shape

// Every Intercom webhook notification follows this structure
interface IntercomNotification {
  type: "notification_event";
  id: string;                        // Unique notification ID
  topic: string;                     // e.g., "conversation.user.created"
  app_id: string;                    // Your app ID
  created_at: number;                // Unix timestamp
  delivery_attempts: number;         // 1 on first try, 2 on retry
  data: {
    type: "notification_event_data";
    item: any;                       // The actual resource (conversation, contact, etc.)
  };
}

// Example: conversation.user.created
// {
//   "type": "notification_event",
//   "id": "notif_abc123",
//   "topic": "conversation.user.created",
//   "created_at": 1711100000,
//   "data": {
//     "type": "notification_event_data",
//     "item": {
//       "type": "conversation",
//       "id": "123",
//       "state": "open",
//       "source": { "body": "Hi, I need help!" },
//       "contacts": { "contacts": [{ "id": "contact-1", "type": "contact" }] }
//     }
//   }
// }

Step 3: Topic-Based Event Router

type WebhookHandler = (data: any) => Promise<void>;

const handlers: Record<string, WebhookHandler> = {
  "conversation.user.created": async (data) => {
    const conversation = data.item;
    console.log(`New conversation: ${conversation.id}`);
    // Notify support channel, auto-assign, etc.
  },

  "conversation.user.replied": async (data) => {
    const conversation = data.item;
    console.log(`Customer replied to: ${conversation.id}`);
    // Update ticket system, escalate if needed
  },

  "conversation.admin.closed": async (data) => {
    const conversation = data.item;
    console.log(`Conversation closed: ${conversation.id}`);
    // Send satisfaction survey, update CRM
  },

  "contact.created": async (data) => {
    const contact = data.item;
    console.log(`New contact: ${contact.id} (${contact.email})`);
    // Sync to CRM, enrich data, trigger welcome flow
  },

  "contact.tag.created": async (data) => {
    const contact = data.item;
    console.log(`Contact tagged: ${contact.id}`);
    // Trigger automation based on tag
  },
};

async function processWebhookAsync(notification: IntercomNotification): Promise<void> {
  const handler = handlers[notification.topic];

  if (!handler) {
    console.log(`Unhandled topic: ${notification.topic}`);
    return;
  }

  try {
    await handler(notification.data);
    console.log(`Processed ${notification.topic}: ${notification.id}`);
  } catch (error) {
    console.error(`Failed ${notification.topic}: ${notification.id}`, error);
    // Dead-letter queue for failed events
  }
}

Step 4: Idempotency (Prevent Duplicate Processing)

Intercom retries failed webhooks once after 1 minute. Guard against duplicates:

import { Redis } from "ioredis";

const redis = new Redis(process.env.REDIS_URL);

async function processIdempotent(
  notification: IntercomNotification,
  handler: () => Promise<void>
): Promise<void> {
  const key = `intercom:webhook:${notification.id}`;

  // SET NX: only succeeds if key doesn't exist
  const acquired = await redis.set(key, "processing", "EX", 86400 * 7, "NX");

  if (!acquired) {
    console.log(`Duplicate webhook skipped: ${notification.id}`);
    return;
  }

  try {
    await handler();
    await redis.set(key, "completed", "EX", 86400 * 7);
  } catch (error) {
    await redis.del(key); // Allow retry on failure
    throw error;
  }
}

Part 2: Outbound Data Events

Submit custom events to track contact activity in Intercom.

Step 5: Track Data Events

import { IntercomClient } from "intercom-client";

const client = new IntercomClient({
  token: process.env.INTERCOM_ACCESS_TOKEN!,
});

// Submit a data event
await client.dataEvents.create({
  eventName: "completed-onboarding",
  createdAt: Math.floor(Date.now() / 1000),
  userId: "user-12345", // External ID of the contact
  metadata: {
    steps_completed: 5,
    time_to_complete_minutes: 12,
    plan: "pro",
  },
});

// Event naming convention: past-tense verb-noun
// Good: "placed-order", "upgraded-plan", "invited-teammate"
// Bad: "order", "click", "page_view"

Step 6: Bulk Event Submission

// Submit events for multiple users efficiently
async function trackBulkEvents(
  client: IntercomClient,
  events: Array<{ userId: string; eventName: string; metadata?: Record<string, any> }>
): Promise<{ succeeded: number; failed: number }> {
  let succeeded = 0;
  let failed = 0;

  // Intercom doesn't have a batch events endpoint; throttle individual calls
  for (const event of events) {
    try {
      await client.dataEvents.create({
        eventName: event.eventName,
        createdAt: Math.floor(Date.now() / 1000),
        userId: event.userId,
        metadata: event.metadata,
      });
      succeeded++;

      // Rate limit: slight delay between calls
      if (succeeded % 50 === 0) {
        await new Promise(r => setTimeout(r, 500));
      }
    } catch (error) {
      failed++;
      console.error(`Failed to track event for ${event.userId}:`, error);
    }
  }

  return { succeeded, failed };
}

Available Webhook Topics

Topic Description
conversation.user.created New conversation from contact
conversation.user.replied Contact replies to conversation
conversation.admin.replied Admin replies to conversation
conversation.admin.closed Conversation closed by admin
conversation.admin.opened Conversation reopened
conversation.admin.snoozed Conversation snoozed
conversation.admin.assigned Conversation reassigned
contact.created New contact created
contact.signed_up Lead converts to user
contact.tag.created Tag applied to contact
contact.tag.deleted Tag removed from contact
visitor.signed_up Visitor becomes lead

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 user_id or event_name Verify contact exists, use verb-noun naming

Resources

Next Steps

For performance optimization, see intercom-performance-tuning.

信息
Category 编程开发
Name intercom-webhooks-events
版本 v20260423
大小 9.13KB
更新时间 2026-04-28
语言