Send WhatsApp with Express
Send a managed template from a server route, retain its message ID and inspect the message status. This example uses the existing Bird TypeScript SDK.
1. Prepare the workspace
Use a funded Bird workspace with WhatsApp sending enabled and a WhatsApp number you control. This recipe sends the managed bird_otp template to that number. Its literal code is demonstration content; use Verify to authenticate a customer. Recipient-permission requirements still apply. Complete the first-message setup and create a server-side API key before running the application.
2. Create the application
Use Node.js 20.3 or later and Express 5:
Exemple de code
mkdir bird-whatsapp-express
cd bird-whatsapp-express
npm init -y
npm install express@5 @messagebird/sdk tsx
export BIRD_API_KEY="YOUR_API_KEY"
export BIRD_TEST_PHONE="YOUR_WHATSAPP_NUMBER"3. Add the routes
Save this as server.mts. The fixed payload keeps this first test separate from a public message composer. Express routing supplies the HTTP handlers.
Exemple de code
import express from "express";
import { BirdClient } from "@messagebird/sdk";
function required(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Configure ${name}.`);
return value;
}
const sender = required("BIRD_TEST_PHONE");
const bird = new BirdClient({ apiKey: required("BIRD_API_KEY") });
const app = express();
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
app.post("/api/whatsapp/messages", async (req, res) => {
const key = req.get("Idempotency-Key");
if (!key || !uuid.test(key)) {
res.status(400).json({ error: "Supply an Idempotency-Key UUID." });
return;
}
try {
const message = await bird.whatsapp.send(
{
to: sender,
template: {
slug: "bird_otp",
language: "en",
components: [{ type: "body", parameters: [{ type: "text", text: "123456" }] }],
},
},
{ idempotencyKey: key },
);
res.status(202).json({ id: message.id, status: message.status });
} catch (error) {
console.error(error);
res
.status(503)
.json({ error: "Could not confirm the send. Inspect the original attempt before retrying." });
}
});
app.get("/api/whatsapp/messages", async (req, res) => {
if (typeof req.query.id !== "string" || !req.query.id) {
res.status(400).json({ error: "Supply a message id." });
return;
}
try {
const message = await bird.whatsapp.get(req.query.id);
res.json({ id: message.id, status: message.status });
} catch (error) {
console.error(error);
res.status(503).json({ error: "Could not read the message." });
}
});
app.listen(3000, "127.0.0.1");4. Run the example
Start the server:
Exemple de code
npx tsx server.mtsIn a second terminal, create an operation ID once and send the fixed example:
Exemple de code
export DEMO_OPERATION_ID="$(python3 -c 'import uuid; print(uuid.uuid4())')"
curl -i -X POST http://127.0.0.1:3000/api/whatsapp/messages \
-H "Idempotency-Key: $DEMO_OPERATION_ID"Keep DEMO_OPERATION_ID with this attempt. Do not regenerate it while resolving an uncertain response. The SDK infers the API region from the key.
Inspect the result and recover
The POST returns 202 with the message id and current status. Acceptance precedes delivery. Copy the ID and read it without sending again:
Exemple de code
curl "http://127.0.0.1:3000/api/whatsapp/messages?id=YOUR_MESSAGE_ID"The read returns the recorded status. Follow WhatsApp events for asynchronous outcomes, and use the product log or event guide to investigate a missing receipt. A missing receipt does not establish delivery or failure.
- A missing or malformed Idempotency-Key produces 400 in this application before it calls Bird.
- If the send fails, the application returns 503 and leaves the result unresolved. Inspect the SDK error in the server terminal and the product log. Correct authentication, sender, destination, template or balance errors before retrying.
- For an uncertain response, retain the same key and identical payload. Read the idempotency guide before retrying; a new key creates a new operation, and response retention does not provide an indefinite exactly-once guarantee.
A managed-template test does not configure your business-owned sender. Follow number setup, template authoring and the customer-service reply rules before connecting your own conversations.
These handlers use a fixed test recipient and bind the development server to loopback. Before publishing, authorize each customer action, associate message IDs with the owning account before permitting reads, and apply your application's abuse limits. The booking and workflow are sample application logic.
Continue the integration
- WhatsApp API reference: exact send contract.
- Events and webhooks: verify signatures and retain event identity.
- Migration guides: map an existing provider's behavior.
- WhatsApp resources: setup, operation and comparison paths.
Ressources associées
Poursuivez avec la documentation, les guides et les exemples sur ce sujet. Les ressources sont en anglais.
Regarder le guideConnecting WhatsApp to Bird: from buying a number to a live channelComprendre le conceptWhat is the 24-hour customer service window on WhatsApp?Utiliser l'outilWhatsApp message builderExplorer la fonctionnalitéWhatsApp
Essayez la pratique et obtenez un guide d'implémentation