Sending WhatsApp Notifications from Intercom Conversations via Webhooks
Intercom is built for the browser tab you have open. The moment a customer closes that tab, your reply sits unread until they remember to come back — or you chase them by email, which gets the same slow treatment as a support ticket. WhatsApp notifications close that gap: when an agent replies or a conversation gets resolved, the customer gets a message they'll actually see within minutes. You don't need an Intercom app from the marketplace for this — a webhook subscription and a small receiver that calls the TextMeFlow messages API is enough.
Step 1: Create a webhook subscription in Intercom
In Intercom, go to Settings → Developers → Webhooks (or create a private app under Settings → Developers → Developer Hub if your workspace requires that route) and add a new webhook subscription. You'll need:
- A target URL: a small receiver you host, e.g.
https://yourapp.example.com/intercom-hook - Topics to subscribe to — the two that matter here are
conversation.admin.replied(an agent sent a reply) andconversation.admin.closed(the conversation was resolved) - A client secret, which Intercom uses to sign every delivery with an
X-Hub-Signatureheader
Subscribe only to the topics you'll act on. Intercom offers dozens of conversation events (assigned, snoozed, tagged, part added by a bot, and more); picking exactly replied and closed keeps your receiver simple and avoids firing a WhatsApp message for internal housekeeping.
Step 2: Verify the signature before trusting the payload
Every Intercom webhook delivery includes an HMAC-SHA1 signature of the raw body, computed with your client secret. Verify it before reading anything out of the payload — this is the same signature-checking discipline TextMeFlow asks of its own webhook consumers (see verifying webhook signatures):
import crypto from 'crypto';
function verifyIntercomSignature(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha1', secret)
.update(rawBody)
.digest('hex');
const provided = signatureHeader.replace('sha1=', '');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(provided, 'hex')
);
}
Reject anything that doesn't match with a 401 before you touch req.body. An unverified payload is just a string someone POSTed to your endpoint.
Step 3: Map the conversation to a phone number
Intercom's webhook payload carries the conversation and the contact, but not necessarily a usable phone number — that depends on what you've stored on the Intercom contact record. If your signup flow or a previous sync already wrote the customer's phone number into a custom attribute (e.g. phone_whatsapp), read it from payload.data.item.contacts.contacts[0]. If you don't have that field reliably populated, you have two options: look the contact up via the Intercom API by id to pull their standard phone field, or skip sending when the field is missing rather than guessing a number.
Step 4: Receive the webhook and send the WhatsApp message
import express from 'express';
const app = express();
app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } }));
app.post('/intercom-hook', async (req, res) => {
const signature = req.headers['x-hub-signature'];
if (!verifyIntercomSignature(req.rawBody, signature, process.env.INTERCOM_CLIENT_SECRET)) {
return res.sendStatus(401);
}
const topic = req.body.topic;
const conversation = req.body.data?.item;
const phone = conversation?.contacts?.contacts?.[0]?.phone;
if (!phone) return res.sendStatus(200);
const text = topic === 'conversation.admin.closed'
? `Your support conversation has been resolved. Reply here if you still need help.`
: `You have a new reply in your support conversation. Check it when you get a chance.`;
await fetch('https://api.textmeflow.eu/v1/messages', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TEXTMEFLOW_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ to: phone, text }),
});
res.sendStatus(200);
});
app.listen(3000);
Respond 200 quickly and do the WhatsApp call without blocking Intercom's retry logic on it — Intercom will retry a delivery that times out, and you don't want duplicate messages because your receiver was slow, not failing.
Step 5: Keep it to transitions customers care about
conversation.admin.replied fires on every single admin message, including a quick "looking into this now" that gets followed by three more messages in the next minute. If your support team sends multiple short replies per conversation, consider debouncing: hold the notification for 60–90 seconds and send once if no further reply topic for that conversation ID arrives, or only notify on the first reply after a period of customer silence. A text for every typing burst trains customers to ignore your WhatsApp number, which is the opposite of what you're trying to build with this integration — see the anti-spam pipeline notes on how repetitive sends affect deliverability more broadly.
Only send to numbers with consent
Intercom conversations are already a conversation the customer started, which covers the transactional case (replies and resolutions on their own open ticket). It doesn't give you a blanket license to message that number for anything else — marketing follow-ups still need their own opt-in. Keep the two uses separate in your own records.
Once this is wired up, the same pattern — subscribe to a webhook topic, verify the signature, map to a phone number, call the messages API — works for any support tool with event webhooks, not just Intercom. Ready to try it on your own account?
Start free on TextMeFlow — 50 messages a month, no credit card.
Zelf WhatsApp-berichten versturen via API?
Gratis voor altijd tot 50 berichten/maand. QR scannen en binnen 5 minuten verstuur je je eerste bericht.
Gratis voor altijd