Developers · Developer API
One endpoint to send a template.
Order updates and one-time passcodes, sent from your own systems on your WhatsApp number. One HTTPS request with a JSON body; delivery comes back to you by webhook.
The Developer API is on Professional (₹3,499 a month + GST, 14-day trial) and above. Each message has one price for its category, the same on every paid plan.
# PINNAKI_API = Pinnaki's API base URLcurl -X POST "$PINNAKI_API/mass-messaging/api/v1/messages" \ -H "Authorization: Bearer $PINNAKI_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-4821-shipped" \ -d '{ "to": "919800000000", "template_name": "order_shipped", "language_code": "en", "variables": ["Asha", "#4821", "Thursday"] }'The Developer API is on Professional (₹3,499 a month + GST, 14-day trial) and above. Each message has one price for its category, the same on every paid plan.
01The request
What goes in the body.
POST /mass-messaging/api/v1/messages with your key as a Bearer token. The template must be approved by Meta and be a utility or authentication template.
| Field | Type | Notes |
|---|---|---|
to | string | The recipient in full international format, with the country code. Up to 32 characters. |
template_name | string | An approved utility or authentication template in your workspace. |
variables | string[] | Values for {{1}}, {{2}} … in order. Up to 50, each up to 1,024 characters. |
named_variables | object | Values for named parameters such as {{first_name}}. Up to 50 keys in lowercase letters, digits and underscores. |
media_url | string | A link to the header image, video or document, if the template has one. |
language_code | string | The template language to use, such as en. Optional. |
disable_logging | boolean | Leave this message out of the stored API logs. Optional. |
An accepted request returns a message_id and the status ACCEPTED: the message is queued for sending. What happens next arrives by webhook.
{ "success": true, "message_id": "msg_3f6c2a9e-8d1b-4c57-9a0e-5b7d1c2e4f60", "status": "ACCEPTED"}02Errors
When it says no, it says why.
Errors come back with a standard HTTP status and a message that names the problem.
| Status | When |
|---|---|
| 400 | The body is invalid, the template isn’t approved in that language, or the number isn’t in international format. |
| 401 | The key is missing, wrong or expired, or the signature is missing, stale, wrong or already used. |
| 402 | The wallet balance doesn’t cover the message. |
| 403 | The IP address or the template isn’t allowed for this key, the template is a marketing template, or the workspace’s plan or WhatsApp channel is inactive. |
| 409 | The WhatsApp number is disconnected, or a request with the same Idempotency-Key is still in progress. |
| 422 | An Idempotency-Key was reused with a different body. |
| 429 | The key’s requests-per-second limit was exceeded. |
03Signing
Sign the request, if you want proof it came from you.
Turn on “Enforce Cryptographic Signature” for a key, and every request must carry a timestamp and an HMAC of its body.
Send X-SaaS-Timestamp (seconds or milliseconds) and X-SaaS-Signature: the hex HMAC-SHA256 of the timestamp, a dot and the raw body, keyed with your API key. A sha256= prefix is accepted.
Requests older than five minutes are refused, and each signature is accepted once. A request that carries an Idempotency-Key may be retried with the same signature, inside the same five minutes.
// Node.js: sign the exact bytes you sendimport { createHmac } from "node:crypto"; const body = JSON.stringify(payload);const timestamp = Math.floor(Date.now() / 1000).toString();const signature = createHmac("sha256", process.env.PINNAKI_KEY) .update(timestamp + "." + body) .digest("hex"); // headers: "X-SaaS-Timestamp": timestamp// "X-SaaS-Signature": "sha256=" + signature04Retries
Retry safely with an Idempotency-Key.
Networks fail. Send the same Idempotency-Key with a retry and the message goes out once.
Put any string up to 128 characters in Idempotency-Key (or X-Idempotency-Key), such as your order ID and the event. For 24 hours, the same key with the same body returns the original response instead of sending again.
The same key with a different body is refused with 422. While the first request is still being handled, a second one gets 409.
05Keys
Each key carries its own limits.
Set the rules once on the key, and every request made with it follows them.
- IP allowlist
- Accept requests only from the addresses or ranges you list, IPv4 or IPv6.
- Rate limit
- A ceiling in requests per second for the key: 10 unless you set another.
- Allowed templates
- Limit the key to the templates it should send; anything else is refused with 403.
- Expiry
- Give a key an end date, and it stops working on its own.
- Message logs
- Keep a log of API messages with their delivery status, or turn storage off.
- Stored hashed
- Pinnaki keeps a hash of the key, not the key. You see the key once, when it is created or regenerated.
06Webhooks
Delivery comes back to you.
Add a webhook URL to the key, and Pinnaki posts each status change of your API messages to it, signed with the webhook secret.
Events are message.sent, message.delivered, message.read and message.failed; a failed event adds a reason.
Check X-Signature-256: the HMAC-SHA256 of X-Timestamp, a dot and the body, keyed with your webhook secret. X-Webhook-Id stays the same across retries, so you can drop duplicates.
POST https://your-server.example/pinnaki-eventsX-Timestamp: 1791936600X-Webhook-Id: 0b9f6d0e-2f53-4c1e-9b7a-3d1f0c8e5a21X-Signature-256: sha256=<hex HMAC-SHA256 of "<X-Timestamp>.<body>"> { "workspace_id": "…", "message_id": "msg_3f6c2a9e-8d1b-4c57-9a0e-5b7d1c2e4f60", "event": "message.delivered", "to": "919800000000"}07Questions
Questions, answered.
What developers ask before they wire Pinnaki into an order system.
Can I send marketing templates through the API?
Is there an SDK?
What happens if my wallet runs low?
Which plan includes the API?
Create an API key.
Start the 14-day Professional trial, connect your WhatsApp number, then open WhatsApp, Integrations, Developer API to create the key and its webhook.
Free: ₹0 a month, without the API. Professional: ₹3,499 a month + GST.