Developer docs
Get an API keyIotaBot WhatsApp API
Send and receive WhatsApp messages from your code — on every number you connected.
Send WhatsApp messages with Node.js
Send and receive from Node.js — built-in fetch and Express.
- Create an API key (a test key never sends) and a webhook endpoint in Developers.
- Set
IOTABOT_API_KEYandIOTABOT_WEBHOOK_SECRET(the endpoint'swhsec_…secret) in your environment. - Run the program below. Point the endpoint at
/iotabot/webhookon your server.
app.js
// npm install express (Node.js 18+: fetch is built in)
const crypto = require("crypto");
const express = require("express");
const API = "https://api.iotabot.com/v1";
const KEY = process.env.IOTABOT_API_KEY;
const SECRET = process.env.IOTABOT_WEBHOOK_SECRET; // the endpoint's signing secret (whsec_…)
async function send(body) {
// One Idempotency-Key per message: reuse it if you retry, so it is never sent twice.
const res = await fetch(`${API}/messages`, {
method: "POST",
headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify(body),
});
const { data, error } = await res.json();
if (!res.ok) throw new Error(`${error.code}: ${error.message}`);
return data;
}
// 1. Start a conversation with an approved template — works for any number, any time.
send({
channel: "whatsapp",
to: "+919812345678",
template: { name: "order_update", language: "en_US", parameters: ["#1042", "Friday"] },
}).then(
m => console.log("queued", m.id, "in conversation", m.conversation_id),
err => console.error("send refused:", err.message), // e.g. template_not_approved — use your own
);
// 2. Receive messages and reply within the 24-hour window.
function verified(raw, header) {
const parts = header.split(",").map(p => p.split("="));
const t = Number(parts.find(([k]) => k === "t")?.[1]);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // older than 5 minutes
const expected = crypto.createHmac("sha256", SECRET).update(`${t}.${raw}`).digest();
return parts.filter(([k]) => k === "v1").some(([, v]) => {
const got = Buffer.from(v, "hex");
return got.length === expected.length && crypto.timingSafeEqual(got, expected);
});
}
const app = express();
app.post("/iotabot/webhook", express.raw({ type: "application/json" }), async (req, res) => {
const event = JSON.parse(req.body);
if (event.type === "endpoint.verification") { // sent once, when you add the endpoint
return res.json({ challenge: event.data.challenge });
}
if (!verified(req.body, req.get("IotaBot-Signature") ?? "")) return res.sendStatus(401);
res.sendStatus(200); // answer fast, then do the work
if (event.type === "message.received") {
const { conversation, message } = event.data;
console.log(conversation.contact.phone, "wrote:", message.text);
await send({ conversation_id: conversation.id, text: "Thanks — we are on it!" });
}
});
app.listen(3000);
