API · Webhooks · SDK

The WhatsApp API developers actually enjoy.

One request to send. One webhook to listen. Built on the official WhatsApp Business Cloud API, with a typed SDK, signed webhooks and retries you never have to think about.

import { Notizy } from "notizy";

const notizy = new Notizy(process.env.NOTIZY_API_KEY);

const { data, error } = await notizy.whatsapp.sendTemplate({
  account_id: "YOUR_ACCOUNT_ID",
  to: "6281234567890",
  template: {
    name: "order_confirmation",
    language: "en",
    body: ["John", "#1234"],
  },
});

Press Run to send a test message. Nothing leaves your browser.

A

Acme Store

Business account

Official
Meta Tech Partner
600 req/min
per API key
8 retries
on every webhook
30 days
of delivery logs
Send

Every message type, one endpoint.

POST /v1/whatsapp/messages with a type. That's the whole mental model.

Templates

Approved templates with variables, media headers and buttons. Send any time.

"template": {
  "name": "order_confirmation",
  "language": "en",
  "body": ["John", "#1234"],
  "buttons": [{ "url": "1234" }]
}

Text

Up to 4,096 characters, inside the 24h service window.

"text": { "body": "Your order has shipped 🚚" }

Images & video

Pass a public URL or a media_id from /v1/media.

"image": {
  "url": "https://cdn.acme.com/receipt.png",
  "caption": "Your receipt"
}

Documents

Invoices, tickets, PDFs, with a custom filename.

"document": {
  "url": "https://cdn.acme.com/inv-1234.pdf",
  "filename": "invoice.pdf"
}

Carousels

Swipeable cards, each with its own media and buttons.

"cards": [
  { "header": { "type": "image", "url": "…" },
    "body": ["Type 45"], "buttons": [{ "payload": "t45" }] }
]

Broadcasts

Target contacts by tag, personalise per contact and schedule ahead.

await notizy.broadcasts.create({
  account_id, template: { name: "promo_october", language: "en" },
  audience: { tags_any: ["customer"] },
  scheduled_at: "2026-10-12T09:00:00+07:00",
});
Listen

Know the moment it's read.

Subscribe an HTTPS endpoint and get a signed POST for every status change and every reply. Same object as the API, no polling.

  1. message.sent

    Accepted by WhatsApp

    10:00:00.312
  2. message.delivered

    Reached the phone

    10:00:01.104
  3. message.read

    Opened by John

    10:00:03.020
  4. message.received

    “Can I change the address?”

    10:00:41.877
  5. broadcast.completed

    12,480 sent · 97.2% delivered

    10:05:12.009
POST /webhooks/notizy
{
  "id": "0199d0c4-7e2a-7c1b-9f3e-5a1d2c3b4e5f",
  "event": "message.read",
  "created_at": "2026-10-11T10:00:03.000Z",
  "data": {
    "id": "0199d0c4-6a11-7b02-8e4f-1c2d3e4f5a6b",
    "channel": "whatsapp",
    "direction": "outbound",
    "status": "READ",
    "to": "6281234567890",
    "delivered_at": "2026-10-11T10:00:01.000Z",
    "read_at": "2026-10-11T10:00:03.000Z"
  }
}
import { verifyWebhook } from "notizy";

app.post("/webhooks/notizy", express.raw({ type: "application/json" }),
  async (req, res) => {
    try {
      const event = await verifyWebhook({
        payload: req.body.toString(),
        signature: req.header("X-Notizy-Signature"),
        secret: process.env.NOTIZY_WEBHOOK_SECRET,
      });

      if (event.event === "message.received") {
        await reply(event.data.from, event.data.text);
      }
      res.sendStatus(200);
    } catch {
      res.sendStatus(400); // bad signature
    }
  },
);
  • X-Notizy-Signature: t=…,v1=…
  • At-least-once, dedupe on data.id
  • Send test events from the dashboard
  • Public HTTPS URLs only, SSRF-checked
Ship

The boring parts, already done.

Everything you'd otherwise build yourself before going live.

Idempotent by design

Send an Idempotency-Key and retry freely. Replays return the original message, never a duplicate.

Signed webhooks

Every delivery carries an HMAC-SHA256 signature with a timestamp, so you can drop forged or replayed calls.

Automatic retries

Failed deliveries retry 8 times with exponential backoff. Inspect every attempt for 30 days.

Predictable limits

600 requests per minute per key, with X-RateLimit headers and Retry-After when you hit the ceiling.

Errors you can act on

Field-level validation errors and a request_id on every failure. The SDK returns { data, error } and never throws.

Cursor pagination

Stable, cursor-based lists for messages, contacts and broadcasts. No skipped or repeated rows.

24h window, handled

Templates go out any time. Free-form text and media inside the service window, with a clear 422 outside it.

OpenAPI spec

Generate a client in any language from /openapi.json, or explore it in the interactive reference.

Start

From zero to delivered in three steps.

  1. 1

    Create an API key in your dashboard.

    .env
    NOTIZY_API_KEY=nzy_3f9a…
  2. 2

    Install the SDK, or use plain HTTP.

    terminal
    npm install notizy
  3. 3

    Send your first message.

    send.ts
    await notizy.whatsapp.send({
      account_id, to: "6281234567890",
      type: "text", text: { body: "Hello from my app 👋" },
    });

Ship your first message in 5 minutes.

Start free, then pay as you grow. See pricing.

https://api.notizy.id