NewAds Insights — see what each Facebook ad costs and the WhatsApp leads it brings. Free on every plan. See Ads Insights →
Lead Pattern

Developers · Node.js

Send a WhatsApp message with Node.js

Working Node.js code (built-in fetch, no packages) for the Lead Pattern WhatsApp API — send an approved template, reply with text inside the 24-hour window, and handle errors and rate limits.

Last updated: 6 November 2026

✨ Ask AI about this page:ChatGPT logoChatGPTClaude logoClaudePerplexity logoPerplexity

Quick answer

Create an API key in your Lead Pattern dashboard, then POST JSON with fetch to https://public.leadpattern.com/v2/whatsapp/message/template (approved template) or https://public.leadpattern.com/whatsapp/message/text (reply within 24 hours), with the key in the Authorization header — no "Bearer" prefix. HTTP 201 with type "success" means WhatsApp accepted the message.

API reference

Step by step

Send a WhatsApp message with Node.js

1

Get an API key

In your Lead Pattern project dashboard, open API Keys and create a key. A key can call every public API endpoint your plan includes — keep it secret and on your server, never in browser or app code.

2

Set the Authorization header

Send the key as-is in the Authorization header on every request — no "Bearer" prefix — plus Content-Type: application/json. The base URL is https://public.leadpattern.com.

3

Send a template message

To start a conversation, or to message someone whose 24-hour window has closed, POST an approved template to /v2/whatsapp/message/template with named placeholders (or /whatsapp/message/template with positional ones).

4

Send a text message inside the 24-hour window

Once the customer has written to you in the last 24 hours, POST to /whatsapp/message/text with "to" and content.text. Outside the window this returns 403 — send a template instead.

5

Handle the response and errors

A successful send returns 201 with type "success" and a WhatsApp messageId. Errors come back with an HTTP status (400, 401, 402, 403, 404, 409, 422, 429, 500). On 429, wait for the Retry-After header before retrying. The Node.js code below does all of this.

1–2 · API key, Authorization header and response helperjavascript
// Node.js 18+ (built-in fetch). Save as send.mjs so top-level await works.
// Run on your server — never ship the key to a browser.
const BASE_URL = "https://public.leadpattern.com";
const API_KEY = process.env.LEADPATTERN_API_KEY; // YOUR_API_KEY

const headers = {
  Authorization: API_KEY, // the raw key, NO "Bearer " prefix
  "Content-Type": "application/json",
};

// Return data on 201, otherwise throw with the status, body and Retry-After
class LeadPatternError extends Error {
  constructor(status, body, retryAfter) {
    super(`HTTP ${status}: ${JSON.stringify(body)}`);
    Object.assign(this, { status, body, retryAfter });
  }
}

async function handle(res) {
  const body = await res.json().catch(() => null);
  if (res.status === 201) return body.data; // { messageId } or { messages: [...] }
  // e.g. { type: "error", code: 403, message: "..." }
  throw new LeadPatternError(res.status, body, Number(res.headers.get("Retry-After")) || 60);
}

// Optional: check connectivity (public endpoint, no key needed)
const health = await fetch(`${BASE_URL}/health-check`);
console.log(health.status);
3 · Send an approved template (v2, named placeholders)javascript
async function sendTemplate(to, templateName, language, placeholders) {
  // placeholders = [{ name: "..." }, { order_id: "..." }]
  const res = await fetch(`${BASE_URL}/v2/whatsapp/message/template`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      messages: [
        {
          to, // E.164 with country code, e.g. "+91XXXXXXXXXX"
          content: {
            templateName,
            language,
            templateData: { body: { placeholders } },
          },
        },
      ],
    }),
  });
  return handle(res);
}

const data = await sendTemplate("+91XXXXXXXXXX", "order_confirmation", "en", [
  { name: "Customer" },
  { order_id: "ORD-1234" },
]);
console.log(data.messages[0].messageId);
Success response (201) — templatejson
{
  "type": "success",
  "data": {
    "messages": [
      {
        "status": "SENT",
        "recipient": "91XXXXXXXXXX",
        "messageId": "wamid.abc123"
      }
    ]
  }
}
4 · Send a text message inside the 24-hour windowjavascript
// Free-form text — only works within 24 hours of the customer's last message.
async function sendText(to, text, previewUrl = false) {
  const res = await fetch(`${BASE_URL}/whatsapp/message/text`, {
    method: "POST",
    headers,
    body: JSON.stringify({ to, content: { text, previewUrl } }),
  });
  return handle(res);
}

console.log((await sendText("+91XXXXXXXXXX", "Hello! How can I help you?")).messageId);
Success response (201) — textjson
{
  "type": "success",
  "data": {
    "messageId": "wamid.abc123",
    "status": "sent",
    "recipient": "91XXXXXXXXXX"
  }
}
5 · Handle errors and rate limitsjavascript
const sleep = (s) => new Promise((r) => setTimeout(r, s * 1000));

try {
  await sendText("+91XXXXXXXXXX", "Your order has shipped.");
} catch (e) {
  if (e.status === 429) {
    await sleep(e.retryAfter); // wait for Retry-After, then retry once
    await sendText("+91XXXXXXXXXX", "Your order has shipped.");
  } else if (e.status === 403) {
    // 24-hour window closed (or IP / plan restriction) -> send a template instead
    await sendTemplate("+91XXXXXXXXXX", "order_confirmation", "en", [
      { name: "Customer" },
      { order_id: "ORD-1234" },
    ]);
  } else {
    throw e;
  }
}
Error response example (403, window closed)json
{
  "type": "error",
  "code": 403,
  "message": "Cannot send a free-form message. The 24-hour customer service window has expired. You may only send template messages after 24 hours of the last user message."
}

HTTP status codes

What each response means

StatusMeaning (from the API docs)
201Created — the message was accepted by WhatsApp. The response has type "success" and data with the messageId.
400Malformed payload, invalid JSON or a missing required field (e.g. "to" is required). Also returned when the project has not finished WhatsApp onboarding.
401Missing, revoked, inactive or malformed API key.
402Workspace subscription inactive or blocked, or plan quota exceeded.
403IP not in the key's whitelist, plan does not include the feature, project suspended — or, for a free-form message, the 24-hour window has closed.
404The entity, template or recipient does not exist.
409Conflict — the resource already exists or is in the wrong state.
422Validation failed, e.g. the phone number is not valid or the WhatsApp account is unlinked.
429Rate limit exceeded. Wait for the Retry-After header, then retry.
500Unexpected server error.

Default limits per API key: 60 requests a minute and 10 requests in flight at once, plus 300 requests a minute per IP address. The effective limit is the lower of the key's limit and your plan's maximum.

Template v1 or v2?

  • v2 — /v2/whatsapp/message/template (recommended by the docs): body placeholders are named, an array of objects like {"name": "..."}. Supports carousel templates (content.templateData.cards) and an optional scheduledAt (a future ISO date-time) per message to schedule instead of sending now.
  • v1 — /whatsapp/message/template: body placeholders are positional — a flat array of values matched in order to the template's variables. Carousel is not supported on v1.
  • Both take a messages array, so one request can send to several recipients. AUTHENTICATION (OTP) templates use their own endpoint — see the API docs.
  • For a DOCUMENT header, pass templateData.header with type, mediaUrl and filename (e.g. "invoice.pdf") so WhatsApp shows a readable file name.

When you need a template

The WhatsApp 24-hour window, which Meta calls the customer service window, starts when a customer messages your business and resets with every new message from them. While it is open you can send normal replies without approval. Once 24 hours pass with no customer message, you can only send pre-approved templates. The 24-hour window explained →

Questions developers ask

Do I need axios or an SDK for Node.js?+

No. Node.js 18 and later have fetch built in, and the API docs don't list an official SDK. axios or any other HTTP client works the same way.

How do I send a WhatsApp message with Node.js?+

Create an API key in your Lead Pattern dashboard, then POST JSON to https://public.leadpattern.com with the key in the Authorization header (no Bearer prefix). Use /v2/whatsapp/message/template for an approved template, or /whatsapp/message/text for a reply inside the 24-hour window.

Why does my text message return 403?+

Most often the customer's 24-hour window has closed — free-form messages are only allowed within 24 hours of the customer's last message. Send an approved template instead. A 403 can also mean the key's IP whitelist blocks your server, or your plan does not include the feature.

What are the API rate limits?+

By default 60 requests a minute and 10 concurrent requests per API key, plus 300 requests a minute per IP address. Going over returns 429 with a Retry-After header. Starter includes 1,000 API calls a month; Growth includes 1,00,000 a month with unlimited API keys.

Which phone number format does the API expect?+

E.164 with the country code, for example +91XXXXXXXXXX for India.

Lead Pattern

Get your API key — free 14-day trial.

Set up the official WhatsApp Business API, create a key and send your first message today.

14-day free trial · No credit card · Free WhatsApp API setup

Ask AI about this page

Get a quick summary of this page from your favourite AI assistant — based only on leadpattern.com. Be one of the first to try it.