WhatsApp API & Developers

WhatsApp to Slack: Route Customer Messages Into Channels

Your team lives in Slack. Your customers live in WhatsApp. So leads and questions sit on one person's phone until they happen to mention them in standup. Getting WhatsApp to Slack sounds like one integration, but there are really three different things people mean: a ping when a new chat arrives, a copy of every message in a channel, or a full bridge where the team replies from Slack threads.

Each has a different cost in code, privacy and how well it holds up. This guide covers all three, with working code for the relay and an honest look at the bridge, plus the mistakes that make WhatsApp-in-Slack setups fall apart after a month.

First decide: alerts or a place to work?

Slack is excellent at telling people something happened. It is poor at being the place you actually answer customers. A Slack channel does not know about WhatsApp's 24-hour window, does not show who already replied, has no contact record, and spreads customer phone numbers and messages across a tool your whole company can search.

GoalBest approachEffort
"Tell sales when a new lead messages us"Post new conversations to a channelNo code
"Let ops see every message from key accounts"Relay inbound messages by webhookA small service
"Let the team reply without leaving Slack"Two-way bridge with threadsA real integration to maintain
"Let the team share WhatsApp properly"A shared inbox, with Slack for alertsA tool, not a project

Teams that start with the bridge often discover they wanted the last row. Start with alerts, and only build more if the alerts are not enough.

Option 1: post new conversations to a channel

The simplest version: when someone starts a new WhatsApp conversation with your business, a message appears in #leads or #support with their name and number. Someone claims it and answers in whatever tool holds WhatsApp.

Slack's side is an incoming webhook, a URL that posts into one channel. You create a Slack app, turn on Incoming Webhooks, and pick the channel. Anything that can send an HTTP POST can then write there:

curl -X POST "$SLACK_WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -d '{"text": "New WhatsApp chat from Priya Nair (+91 98111 22233)"}'

If your WhatsApp runs through Tenashi, this exists without code: paste the incoming webhook URL into the Slack integration, and new WhatsApp conversations are posted to that channel. It fires on new conversations, not every message, which is what you want for lead alerts. Busy channels that ping on every message get muted within a week.

Option 2: relay every inbound message with a webhook

For a key-accounts channel or an escalation feed, you want each inbound message. That needs a small service: WhatsApp webhook in, Slack incoming webhook out.

Here is a relay for Tenashi's message.received event in Node.js with Express. It verifies the signature on the raw body, deduplicates on event id, filters to what the channel should see, and posts to Slack.

import express from "express";
import crypto from "node:crypto";

const app = express();
const seen = new Set(); // use Redis or a table in production

function verifyTenashi(req) {
  const ts = req.get("X-Tenashi-Timestamp");
  const sig = (req.get("X-Tenashi-Signature") || "").replace(/^v1=/, "");
  const expected = crypto
    .createHmac("sha256", process.env.TENASHI_WEBHOOK_SECRET)
    .update(`${ts}.${req.body}`)
    .digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  return fresh && sig.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

app.post("/hooks/tenashi", express.raw({ type: "application/json" }), async (req, res) => {
  if (!verifyTenashi(req)) return res.sendStatus(401);
  const event = JSON.parse(req.body);
  res.sendStatus(200); // answer fast, work after

  if (event.type !== "message.received" || seen.has(event.id)) return;
  seen.add(event.id);

  const m = event.data.message;
  const where = m.chat.type === "group" ? `group ${m.chat.name}` : "a DM";
  const text = m.text || `(${m.type})`;

  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text: `*${m.sender.name || m.sender.phone}* in ${where} on ${m.number.label}:\n${text}`,
    }),
  });
});

app.listen(3000);

Three details that matter:

  • Raw body for the signature. express.raw keeps the exact bytes. Parsing first and re-serializing breaks the HMAC. Our post on verifying WhatsApp webhook signatures covers this for Cloud API too.
  • Reply 2xx fast. Tenashi expects a 2xx within 10 seconds and retries failures. Delivery is at least once and not always in order, so deduplicate on the event id.
  • Filter hard. Relay only certain numbers, labels or groups. A channel that copies all customer traffic is a privacy problem.

The Cloud API version

On Meta's Cloud API, the shape differs but the pattern holds. Subscribe your app to the messages webhook field, verify X-Hub-Signature-256 (HMAC-SHA256 of the raw body with your app secret, prefixed sha256=), then read messages from entry[].changes[].value.messages[]. The sender's name is in value.contacts[]. Status updates arrive on the same webhook as value.statuses[], so skip those. The WhatsApp API webhooks guide walks through the payload.

Option 3: a two-way bridge with Slack threads

The bridge lets a teammate reply in a Slack thread and have the reply go to the customer on WhatsApp. It works like this:

  1. One thread per conversation. When a message arrives for a conversation with no thread yet, post it with Slack's chat.postMessage using a bot token, and store the returned ts against the WhatsApp conversation id. Later messages post into that thread with thread_ts.
  2. Listen for replies. Subscribe your Slack app to message events in that channel through the Events API. Answer Slack's URL verification challenge on setup, verify its signing secret on every request, and respond quickly, since Slack retries slow responses.
  3. Send the reply. When a human posts in a mapped thread, ignore bot messages, look up the conversation and send the text to WhatsApp.

With Tenashi the send is one call, because conversationId targets the existing DM or group:

curl https://usetenashi.com/api/v1/messages \
  -H "Authorization: Bearer $TENASHI_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: slack-1730198400.123456" \
  -d '{"conversationId": "cmh0a9s8d7f6g5h4j3k2l1", "text": "Hi Priya, yes, Thursday at 6 works. See you then."}'

Using the Slack message ts as the idempotency key means a retried Slack event cannot send the reply twice. On Cloud API, the reply is a free-form message to the customer's number, which only works inside the 24-hour window. Outside it, your bridge needs to pick a template, and Slack users will not know why their reply failed unless you post the error back into the thread.

Why bridges get abandoned

The bridge is buildable in a few days. What wears teams down is everything around it: two people replying in the same thread, nobody knowing which chats are still open, media and voice notes that need special handling, history that lives in Slack instead of with the customer record, and an integration someone has to keep running. The guide to a WhatsApp API team inbox covers what teams end up needing on top of a pipe.

Where Tenashi fits

The usual reason people want WhatsApp in Slack is that WhatsApp is stuck on one phone. Tenashi fixes that directly: link your existing number with a QR scan and the team works DMs and groups from one shared inbox, with assignment, notes and @mentions, a CRM under every chat and SLAs on tickets. Slack then does what it is good at. The built-in Slack integration posts new conversations to a channel with no code, and webhooks such as message.received feed any custom relay like the one above.

The honest limits: Tenashi runs on your own number as a linked device, not Meta's official API, so there are no templates or interactive buttons, sends are paced, and WhatsApp can still ban numbers that spam. There is no native Tenashi app in Slack's directory; the integration uses Slack's incoming webhooks. For the bigger picture of official and linked-number options, see the complete guide to the WhatsApp API.

See every feature · Read the API docs

FAQ

Can I forward WhatsApp messages to Slack without code?

Yes, for alerts. Tools that hold your WhatsApp, such as Tenashi, can post new conversations to a Slack channel through an incoming webhook you paste in. Zapier, Make or n8n can also catch a WhatsApp webhook and post to Slack. Forwarding every message, or replying from Slack, usually needs custom code.

Can my team reply to WhatsApp customers from Slack?

Only with a two-way bridge: a Slack app that maps threads to WhatsApp conversations, listens for replies through the Events API and sends them through a WhatsApp API. It works, but it needs maintenance, and Slack has no notion of WhatsApp's 24-hour window, assignment or contact history, which a shared inbox handles for you.

Is it safe to put customer WhatsApp messages in Slack?

It depends on what you relay. Customer numbers and messages in a widely visible channel are a data protection risk, and they stay searchable. Relay only what a channel needs, use private channels for sensitive traffic, avoid forwarding media, and check that your data processing terms cover Slack as a processor.

Do Slack incoming webhooks work with WhatsApp group messages?

Yes, if your WhatsApp source can see groups. Meta's Cloud API cannot read the existing groups a number is in. A linked-number tool like Tenashi receives group messages with the group's name, so your relay can post them to Slack with the group labelled. Filter carefully; busy groups flood channels quickly.

Why do my WhatsApp alerts arrive in Slack twice?

Webhooks are delivered at least once, so retries happen when your endpoint is slow or returns an error. Return a 2xx quickly, do the Slack post afterwards, and store each event id so repeats are skipped. Tenashi includes a unique event id on every delivery for exactly this purpose.

Slack is the right place to hear about a WhatsApp chat, not the best place to work it. See how Tenashi's shared inbox and Slack alerts fit together.