Connecting your software to WhatsApp
API Management is how another system — your billing software, your online store, your own backend — sends WhatsApp messages through WizMessage. You issue it an API key here, tell it which approved WhatsApp template it may send, and point it at a webhook so replies come back. This guide walks the whole path, then documents the REST API your developers will call.
The dashboard
Where your API keys live
Every key you have issued appears here under API Keys. Each row shows the key's status, its public prefix (the only part of the key ever shown again after you create it), and how many calls it has made today. "Manage" opens the key; the toggle pauses it without deleting it.


A key that is not finished yet
A key with no template mapped to it carries a "Needs setup" badge. It can authenticate, but it cannot send a template message until you map one — so a half-configured integration is visible at a glance rather than failing later at send time.

Guided setup
Step 1 — What are you connecting?
"Connect your software" opens a four-step wizard. Start by naming the key after the system that will use it, and pick the preset that matches. The presets only suggest sensible field names — billing suggests invoice_number, amount, due_date, customer_name — they do not restrict what you can map.


Step 2 — Choose the message
Pick one of your approved WhatsApp templates. Templates live at Meta, not here — this list is fetched live from your connected WhatsApp account, and only APPROVED templates can be sent. This step is optional: skip it if the key only ever sends plain text, and map a template later.

Step 3 — Where should replies go?
When someone replies to your message, or a delivery report comes back, we POST it to a URL you control. Leave this off if your system only ever sends. You can add it later from the key's Webhooks tab — nothing here is permanent.

Step 4 — Go live
Pressing Next on the previous step is what actually creates the key — this screen is the confirmation. Copy all three values now: the API key, the API secret, and the webhook signing secret are shown exactly once and cannot be recovered afterwards. If you lose the secret you must rotate it, which means updating whatever system was using it.

Managing a key
Overview
Opening a key lands on Overview: what it is allowed to do, how heavily it is being used, and the endpoints it can call. Usage counts are read from the request log, so they reflect real traffic.


A key that still needs setting up
A key with no template mapped opens on a Setup tab instead — the same wizard, inline. Note the tabs differ from the previous screenshot: until a key has at least one template, Templates and Webhooks are not offered, because there is nothing yet for them to act on.


Templates
Each card is one template this key may send. The body text is the message as approved at Meta, and the chips beneath it are the variable names your system will supply. "Try it" sends a test, "Get code" produces a ready-made request in eight languages.


Reading the variables — the one thing worth slowing down for
Look at the payment_failed_alert card. Its message runs {{1}}, then {{3}}, then {{2}} — the placeholders are NOT in numeric order in the text. The chips below are listed in numeric order: customer_name fills {{1}}, renewal_date fills {{2}}, plan_name fills {{3}}. Match your values to the NUMBER, never to the order the placeholders happen to appear in the sentence. This trips people up most often on translated templates, where word order moves the placeholders around.

The three buttons on every template
"Try it" — send a test without writing any code
The paper-plane button opens this template pre-loaded with sample values, one per variable name. It is the fastest way to check a mapping is right before your developers touch it. Note the toggle: "Simulate Only" is the default and nothing leaves the building — the server echoes your payload straight back. Flip it to "Send Real Message" and a genuine WhatsApp message goes to the number in the box, and is charged for.


What a simulated send gives you back
Pressing Send Request in Simulate Only mode returns the request as the platform received it, with the HTTP status and round-trip time. Read this as a shape check, not a rehearsal: it confirms your JSON parsed and the key was accepted, but it does NOT validate your variable names against the mapping and never contacts Meta. A payload that simulates cleanly can still be rejected for real. To prove the mapping itself, turn on Send Real Message and send to your own phone.

"Get code" — the request, written for you
The angle-brackets button generates a working request for this exact template in eight languages: cURL, JavaScript, Node.js, Python, PHP, Ruby, Go and C#. Hand it to whoever is writing the integration. The variables appear as {{placeholders}} named after your mapping, and the credentials are left as YOUR_API_KEY / YOUR_API_SECRET — this dialog never prints your real key, so the snippet is safe to paste into a ticket.

Switching language
Click any language along the top and the snippet is rewritten for it — the same call, the same headers, that language's HTTP client. Each one is generated the first time you click it, so a brief spinner on first switch is normal. Copy takes the snippet as shown: it is complete, imports and all, and if this key requires signatures the signing step is already in it.

"Edit" — change what your system sends
The pencil opens the mapping itself. The phone mock-up at the top is the real message with your current names substituted in, so you can see what the customer will read. Beneath it, each detected variable can be renamed, reordered, marked required, or given a type — a date, a currency amount, an image. Renaming a variable renames it in your API payload too, so a system already sending the old name will start failing: change both together.

Test API — the same tool, any endpoint
"Test API" at the top of the tab is "Try it" without the template lock: pick any of the three send endpoints, switch templates from the second dropdown, and edit the body freely. Use it to check the raw /messages/text or /messages/template calls, which have no card of their own because they are not tied to a mapping. Note this template's variables are still called body_1, body_2, body_3 — that is what an unmapped template looks like, and it is what "Edit" exists to fix.

Replies
Webhooks — getting replies back
Sending is only half an integration. Point a webhook at your own HTTPS endpoint and WizMessage will POST inbound messages and delivery updates to it. The signing secret is shown once when generated; verify it on every delivery so you know the call really came from us.

REST API reference
Everything above configures the key. This is what your developers actually call. Base URL is /api/v1/external. Every endpoint requires the auth headers below and is subject to the key's rate limits.
Authenticating
Send your API key on every request. If the key has "Require signature" enabled, you must also sign the request — and a key with it enabled will reject any unsigned call outright.
| Header | Value |
|---|---|
X-API-Key | Your API key — the full pk_live_… value, not just the prefix shown in the dashboard. |
X-Timestamp | Unix timestamp in seconds. Rejected if more than 5 minutes from server time. |
X-API-Signature | HMAC-SHA256 of timestamp + "." + rawBody, keyed on SHA256(api_secret), lowercase hex. |
Note: Signatures are only enforced when the key has
require_signatureenabled. If it does not, the API key alone is sufficient to send messages — which is exactly why the key is a secret, not an identifier. Treat it like a password: it is shown once, and anyone holding it can message your customers.
const crypto = require('node:crypto')
const body = JSON.stringify({ to: '+919876543210', message: 'Hello' })
const timestamp = Math.floor(Date.now() / 1000).toString()
const secretHash = crypto.createHash('sha256').update(API_SECRET).digest('hex')
const signature = crypto.createHmac('sha256', secretHash)
.update(timestamp + '.' + body)
.digest('hex')
await fetch('https://your-host/api/v1/external/messages/text', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY,
'X-Timestamp': timestamp,
'X-API-Signature': signature,
},
body,
})
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /messages/text | Send a plain text message. Only works inside an open 24-hour conversation window. |
POST | /messages/template | Send a template, supplying Meta's raw components array yourself. |
POST | /messages/template/send | Send a template using the mapping you configured in the dashboard. This is the one to use. |
POST | /messages/interactive/list | Send an interactive list message. |
POST | /messages/interactive/button | Send up to three reply buttons. |
GET | /messages/:uuid | Look up the delivery status of a message you sent. |
POST | /media/upload | Upload a PDF/image/video to Meta and get a media_id for use in a template header. |
GET | /templates | List the templates available to this key. |
GET | /templates/:name | Fetch one template's details. |
Sending a mapped template
This is the payload that pairs with the Templates tab. You send variables by NAME — the names shown as chips on the template card — and the platform assembles Meta's components for you. That is the whole point of mapping a template: your billing system does not need to know anything about Meta's component format.
POST /api/v1/external/messages/template/send
{
"to": "+919876543210",
"template_name": "payment_failed_alert",
"language": "en",
"variables": {
"customer_name": "Priya Sharma",
"renewal_date": "12 Aug 2026",
"plan_name": "Wiz Pro"
},
"reference": "invoice-2026-0817"
}
Note: The variable names must match the mapping exactly — an unrecognised name is ignored, and a missing required one is rejected with a
400.referenceis yours: it is echoed back on webhooks so you can tie a delivery report to a record in your own system.
Rate limits
Each key carries its own limits, visible on the Overview tab — 60 requests/minute and 1,000 messages/day by default. Responses include the remaining allowance, so back off when it runs low rather than retrying into a wall.