Gennissi CRMGennissi Technologies
GSigned in

Ctrl K
Gennissi CRMGennissi Technologies

Workspace

DashboardClientsLeadsTasksFollow-ups

Money

ExpensesQuotations

Messaging

WhatsAppCatalogueCampaignsAuto repliesTemplates

Admin

SubscriptionAPI & webhooksSettings
GSigned in
Gennissi CRMAPI
Sign inStart free trial
  • Getting started
  • Authentication
  • Send a message
  • Replace SMS (URL)
  • Message status
  • Webhooks
  • Protect your number
  • Errors

Developers

WhatsApp messaging API

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.

Getting started

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.

  1. Link your WhatsApp number in the CRM (WhatsApp page, scan the QR code).
  2. Open API & webhooks and make an API key. Copy it: it is shown only once.
  3. Send a POST request to /api/v1/messages with the number and the text. The message is queued and sent within seconds.

Base address: https://wa.gennissi.com/api/v1

Try it live

POST /api/v1/messages

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"}'

Authentication

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.

Send a message

POST/api/v1/messages

FieldRequiredWhat it is
toYesPhone number with country code, digits only or with +, for example 919876543210.
messageYesThe text, up to 4096 characters. WhatsApp formatting works: *bold*, _italic_, links.
referenceNoYour own id (order number, ticket), sent back in the status and webhooks.
categoryNo"notification" (default) or "marketing". Marketing messages are not sent to people who replied STOP.
ttl_minutesNoGive up if not sent within this many minutes (default 1440 = one day). Use 10 for OTP codes.
idempotency_keyNoSend 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
}

Replace SMS (URL)

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.

Message status

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.

Webhooks

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.

FieldRequiredWhat it is
message.receivedA person sent you a WhatsApp message.
message.sentA message sent through the API went out.
message.failedA message sent through the API failed or expired.
campaign.finishedA campaign finished sending.
campaign.pausedA campaign paused itself after several failed messages in a row.
contact.opted_outA 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 || ""));
}

Protect your number

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:

  • Messages go out one at a time with a random pause, never in a burst.
  • Hourly and daily limits, and a limit per number per hour (5 by default), so a bug in your system can't flood anyone.
  • People who reply STOP are kept off marketing messages and campaigns automatically.
  • Campaigns go in batches with a rest between them, only in your sending hours, and pause themselves when messages start failing.

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

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…" } }
FieldRequiredWhat it is
401 missing_api_key / invalid_api_keyNo key, a wrong key, or a key that was turned off.
422 invalid_phone / missing_messageFix the number or the text and send again.
429 rate_limited / queue_fullMore than 120 requests a minute, or too many messages waiting. Slow down and try again.
500 server_errorSomething went wrong on our side. Try again.