Developers
Send notifications from your own systems through your WhatsApp number, replace SMS alerts, and receive messages with webhooks — with limits that keep your number safe.
Send WhatsApp messages from your own software — order updates, OTP codes, appointment reminders, GPS alerts — from your company's WhatsApp number, in place of SMS. Get new messages and delivery results back with webhooks.
Base address: https://wa.gennissi.com/api/v1
The key stays in this page only. The message is sent from the WhatsApp number linked to the key's company.
curl -X POST https://wa.gennissi.com/api/v1/messages \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "919876543210", "message": "Your order #1001 is out for delivery. Track it: https://example.com/t/1001", "reference": "order-1001"}'Every request needs your API key. Send it in the Authorization header. Keep keys on your server; never put them in a website or mobile app that others can open.
Authorization: Bearer gk_…
# or
X-API-Key: gk_…Systems that can only call a web address may put the key in the address as ?api_key=… (see Replace SMS). Turn a key off at any time on the API & webhooks page; it stops working at once.
POST/api/v1/messages
| Field | Required | What it is |
|---|---|---|
| to | Yes | Phone number with country code, digits only or with +, for example 919876543210. |
| message | Yes | The text, up to 4096 characters. WhatsApp formatting works: *bold*, _italic_, links. |
| reference | No | Your own id (order number, ticket), sent back in the status and webhooks. |
| category | No | "notification" (default) or "marketing". Marketing messages are not sent to people who replied STOP. |
| ttl_minutes | No | Give up if not sent within this many minutes (default 1440 = one day). Use 10 for OTP codes. |
| idempotency_key | No | Send the same value again (for example after a timeout) and the message is not sent twice. |
Answer: 202 Accepted with the message id. Sending happens in the background; check the status or listen for the message.sent webhook.
{
"id": "6f1c2a9e-4b7d-4c1e-9a55-2f0d1b3c4e5f",
"to": "919876543210",
"status": "queued",
"category": "notification",
"reference": "order-1001",
"error": null,
"whatsapp_message_id": null,
"created_at": "2026-11-10T09:30:00.000Z",
"sent_at": null
}Many systems — GPS tracking platforms, alarm panels, billing and school software — send SMS by calling a web address with the number and the text. Point them to this address instead and the same alerts arrive on WhatsApp.
GET / POST/api/v1/sms?api_key=…&to=…&text=…
https://wa.gennissi.com/api/v1/sms?api_key=YOUR_API_KEY&to={phone}&text={message}The number can be in to, phone, mobile or number, and the text in text, message, msg or body, as JSON, a form or the address. Numbers without a country code get the default country code set in API settings.
GET/api/v1/messages/{id}
queued — waiting in the sending queue.sent — handed to WhatsApp from your number.failed — not sent; error says why (for example, not a WhatsApp number, or the hourly limit for this number).expired — not sent within ttl_minutes, for example because WhatsApp was disconnected.GET/api/v1/messages?status=failed&limit=50
GET/api/v1/status
/status tells you whether WhatsApp is connected, how many messages are waiting, and how much of the hourly and daily limit is used.
Add a webhook address on the API & webhooks page and the CRM posts each event to it as JSON. Answer with any 2xx status within 10 seconds. Failed deliveries are tried again after 1, 5 and 30 minutes, 2 and 6 hours.
| Field | Required | What it is |
|---|---|---|
| message.received | A person sent you a WhatsApp message. | |
| message.sent | A message sent through the API went out. | |
| message.failed | A message sent through the API failed or expired. | |
| campaign.finished | A campaign finished sending. | |
| campaign.paused | A campaign paused itself after several failed messages in a row. | |
| contact.opted_out | A person replied STOP and won't get campaigns any more. |
POST https://your-server.com/webhooks/whatsapp
Content-Type: application/json
X-Gennissi-Event: message.received
X-Gennissi-Signature: t=1762767000,v1=5f2b…
{
"id": "0b8e…",
"event": "message.received",
"created_at": "2026-11-10T09:30:00.000Z",
"data": {
"from": "919876543210",
"name": "Ravi",
"text": "Is my order ready?",
"has_media": false,
"client_id": "…",
"received_at": "2026-11-10T09:29:58.000Z"
}
}Check the signature to be sure the call came from your CRM: HMAC-SHA256 of the time, a dot and the raw body, with your webhook's secret.
import crypto from "node:crypto";
// rawBody = the request body exactly as received (a string)
function isFromGennissi(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 || ""));
}This API sends from a normal WhatsApp number, not the paid WhatsApp Business API. WhatsApp blocks numbers that send too much, too fast or to people who don't want the messages. The CRM protects your number for you:
Good habits: send only to people who gave you their number and expect the message; put your business name in it; avoid only links; and for a new number start slowly (a few dozen messages a day) for the first week or two.
Errors come back as JSON with a code and a message:
{ "error": { "code": "invalid_phone", "message": "Phone number must have 8 to 15 digits with the country code…" } }| Field | Required | What it is |
|---|---|---|
| 401 missing_api_key / invalid_api_key | No key, a wrong key, or a key that was turned off. | |
| 422 invalid_phone / missing_message | Fix the number or the text and send again. | |
| 429 rate_limited / queue_full | More than 120 requests a minute, or too many messages waiting. Slow down and try again. | |
| 500 server_error | Something went wrong on our side. Try again. |