whatsapp-cloud-ts
v1.0.3
Published
Type-safe, zero-dependency WhatsApp Cloud API client — templates, all message types, bulk send, webhook parsing
Maintainers
Readme
WhatsApp Cloud Template Client
A lightweight, strictly-typed, and zero-dependency Node.js client for Meta's official WhatsApp Cloud API.
Built exclusively for modern Node (18+) using the native fetch API. It provides 100% type safety for your templates, a powerful webhook parser, bulk sending capabilities, and supports all 24-hour window message types.
Features
- 🛡️ Type-Safe Templates: Catch missing or incorrect template parameters at compile time.
- 🤖 Auto-Codegen CLI: Generate TypeScript interfaces directly from your approved Meta templates.
- 📦 Bulk Sending: Send personalised templates to multiple users efficiently while respecting rate limits.
- 🪝 Powerful Webhook Parser: Flattens Meta's deeply nested webhook payloads into a clean, strongly-typed array of events (Text, Media, Status, Interactive, etc.).
- 🕒 24-Hour Window Support: Complete support for all standard message types (Text, Image, Video, Document, Location, Interactive Buttons/Lists, Reactions).
- 🪶 Zero Dependencies: Uses only standard Node APIs. Small, fast, and secure.
Installation
npm install whatsapp-cloud-tsQuick Start
import { WhatsAppClient, defineTemplate } from "whatsapp-cloud-ts";
const client = new WhatsAppClient({
accessToken: process.env.WA_ACCESS_TOKEN,
phoneNumberId: process.env.WA_PHONE_NUMBER_ID,
});
// 1. Define your template with strict typing
const orderTemplate = defineTemplate({
name: "order_confirmation",
language: "en_US",
header: { type: "IMAGE" },
body: {
params: {
1: "customerName",
2: "orderId"
},
},
} as const); // 'as const' is required for type inference!
// 2. Send it with full IDE autocomplete!
await client.sendFromDefinition(orderTemplate, "919876543210", {
customerName: "Rahul",
orderId: "#12345",
header: { url: "https://example.com/banner.jpg" },
});🛠 Feature Guide
1. Auto-Generating Template Types (CLI)
Instead of manually defining templates, you can automatically fetch all your approved templates from Meta and generate TypeScript interfaces for them.
Set up your .env file:
WA_ACCESS_TOKEN=your_token
WA_BUSINESS_ACCOUNT_ID=your_waba_idRun the generator:
npx whatsapp-cloud-ts
# or if installed locally: npm run codegenThis generates a whatsapp-templates/ folder in your project with an individual file for each template and an index.ts. You can then use it:
import { order_confirmation } from "./whatsapp-templates";
await client.sendFromDefinition(order_confirmation, "919876543210", {
param1: "Rahul", // Body param {{1}}
param2: "#12345", // Body param {{2}}
});2. Typed Bulk Sending
Send personalized messages to multiple recipients with built-in rate-limiting delays.
const results = await client.sendBulkTyped(
orderTemplate,
[
{
to: "919876543210",
params: { customerName: "Rahul", orderId: "#123", header: { url: "..." } },
},
{
to: "919876543211",
params: { customerName: "Priya", orderId: "#124", header: { url: "..." } },
},
],
{ delayMs: 500 } // 500ms delay between requests (default is 300ms)
);
console.log(`Successful sends: ${results.filter(r => r.success).length}`);3. Webhook Parsing
Meta's webhooks look like this: entry[0].changes[0].value.messages[0].
This package provides parseWebhook() to flatten this mess into strongly-typed WebhookEvent objects.
import { parseWebhook, verifyWebhook } from "whatsapp-cloud-ts";
import express from "express";
const app = express();
app.use(express.json());
// Verify webhook (GET challenge)
app.get("/webhook", (req, res) => {
const challenge = verifyWebhook(req.query, process.env.VERIFY_TOKEN!);
if (challenge) return res.send(challenge);
res.sendStatus(403);
});
// Handle incoming messages (POST)
app.post("/webhook", (req, res) => {
const events = parseWebhook(req.body);
for (const event of events) {
switch (event.type) {
case "text":
console.log(`Text from ${event.from}: ${event.text}`);
break;
case "image":
console.log(`Image received. ID: ${event.imageId}`);
break;
case "interactive_button_reply":
console.log(`User clicked button: ${event.buttonId}`);
break;
case "status":
// Track delivery receipts
console.log(`Message ${event.messageId} is now ${event.status}`);
break;
}
}
res.sendStatus(200);
});4. 24-Hour Window Standard Messages
If the user has replied within the last 24 hours, you can send standard messages without templates.
// Text
await client.sendText("919876543210", "How can I help you today?");
// Media (Image, Video, Document, Audio, Sticker)
await client.sendImage("919876543210", "https://example.com/image.jpg", "Check this out!");
await client.sendDocument("919876543210", "https://example.com/invoice.pdf", "invoice.pdf");
// Location
await client.sendLocation("919876543210", 28.7041, 77.1025, "Delhi", "Connaught Place");
// Interactive Buttons (Max 3)
await client.sendInteractiveButtons(
"919876543210",
"Are you satisfied with our service?",
[
{ id: "btn_yes", title: "✅ Yes" },
{ id: "btn_no", title: "❌ No" }
]
);
// Interactive Lists (Menus)
await client.sendInteractiveList(
"919876543210",
"Please select a department:",
"Select options",
[
{
title: "Support",
rows: [
{ id: "tech_support", title: "Technical Support" },
{ id: "billing", title: "Billing & Invoices" },
]
}
]
);
// Reactions
await client.sendReaction("919876543210", "wamid.xxx...", "👍");5. Media Utilities
Download media received via webhooks:
const media = await client.getMediaUrl("media-id-from-webhook");
console.log(`Download URL: ${media.url}`); // Use this URL with the WA_ACCESS_TOKEN to downloadMark a message as read (blue ticks):
await client.markAsRead("wamid.xxx...");Contributing
Contributions are welcome! Please open an issue or submit a PR on GitHub.
License
MIT
