WhatsApp API

Developer documentation

Connect a WhatsApp number, copy your API token, and start sending. Everything below works against https://webwhatsapp.finocore.net.

API reference

Every endpoint lives under https://webwhatsapp.finocore.net and speaks JSON. Sends are accepted asynchronously: the API returns 202 Accepted with a message_id, and a background worker delivers the message. Track the outcome by polling the message, or by subscribing a webhook.

Response envelope

Successful responses are { "success": true, "data": { ... } }. Failures are { "success": false, "error": { "code", "message" } }. The only exception is the compatibility GET /api/send_sms endpoint, which returns a flat body for older integrations.

Authentication

Integrations authenticate with the account API token in the X-API-Key header. The token identifies the customer, and therefore which WhatsApp connections and message history the call can reach.

X-API-Key: YOUR_API_TOKEN

Dashboard endpoints (connecting WhatsApp, managing tokens and webhooks) use the short-lived JWT from POST /api/v1/auth/login instead. The two credentials are deliberately separate: a dashboard session can never send messages, and a leaked API token can never change account settings.

  • Send the token as a header, not a query parameter, wherever you can.
  • The token is shown once. If it is lost, regenerate it from the panel.
  • Regenerating or revoking a token stops the old one working immediately.

Send text

POST /api/v1/messages

Body

FieldTypeRequiredNotes
mobilestringYes10 digit Indian, or full international digits
messagestringYesLimited by your plan's maximum message length
session_idstringNoWhich connection sends it; defaults to your default connection
curl -X POST https://webwhatsapp.finocore.net/api/v1/messages \ -H "X-API-Key: YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-1042-confirm" \ -d '{"mobile":"919699991618","message":"Welcome to System"}'

Response 202

{ "success": true, "message": "Message queued successfully", "data": { "message_id": "MSG_202609201200000001", "status": "QUEUED", "mobile": "919699991618", "session_id": "WA_SESSION_10001" } }

Idempotency

Send an Idempotency-Key header and a retry of the same request returns the original message instead of sending twice. Keys are scoped to your account.

Send image

POST /api/v1/messages/image

{ "mobile": "919699991618", "image_url": "https://example.com/welcome.jpg", "caption": "Welcome to our system", "session_id": "WA_SESSION_10001" }

The URL must be https and reachable. Content type and size are verified before the media is fetched, and private network addresses are refused.

Send document

POST /api/v1/messages/document

{ "mobile": "919699991618", "document_url": "https://example.com/invoice.pdf", "filename": "invoice.pdf", "caption": "Your invoice" }

Send video

POST /api/v1/messages/video

{ "mobile": "919699991618", "video_url": "https://example.com/promo.mp4", "caption": "New offer" }

Send audio

POST /api/v1/messages/audio

{ "mobile": "919699991618", "audio_url": "https://example.com/note.mp3" }

Message status

GET /api/v1/messages/{message_id}

{ "success": true, "data": { "message_id": "MSG_202609201200000001", "mobile": "919699991618", "type": "text", "status": "DELIVERED", "created_at": "2026-09-20T12:00:00+00:00", "sent_at": "2026-09-20T12:00:02+00:00", "delivered_at": "2026-09-20T12:00:04+00:00", "read_at": null } }

Status moves forward only: QUEUED, PROCESSING, SENT, DELIVERED, READ, or FAILED.

Message history

GET /api/v1/messages

Filter with mobile, status, type, direction, sessionCode, from, to and search. Paginate with page and pageSize.

Compatibility GET API

GET /api/send_sms

https://webwhatsapp.finocore.net/api/send_sms?api_token=YOUR_API_TOKEN&mobile=919699991618&message=Welcome

Provided so an existing one-line integration keeps working. It runs through exactly the same validation, quota and queue as the POST API, and returns a flat body:

{"success":true,"status":"queued","message_id":"MSG_...","mobile":"919699991618","session_id":"WA_SESSION_10001"}

A token in a query string is recorded in server logs, proxy logs and browser history. Prefer the X-API-Key header for anything new.

Webhooks

Configure one endpoint per account from the panel and subscribe to the events you care about: message.sent, message.delivered, message.read, message.failed, message.received, session.connected, session.disconnected.

Payload

{ "event": "message.delivered", "message_id": "MSG_202609201200000001", "mobile": "919699991618", "status": "DELIVERED", "timestamp": "2026-09-20T12:00:04+00:00" }

Verifying the signature

Each request carries X-WhatsApp-Timestamp and X-WhatsApp-Signature. The signature is sha256= followed by the hex HMAC-SHA256 of {timestamp}.{raw body} using your webhook secret.

// Node.js const expected = 'sha256=' + crypto .createHmac('sha256', WEBHOOK_SECRET) .update(`${req.headers['x-whatsapp-timestamp']}.${rawBody}`) .digest('hex'); if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-whatsapp-signature']))) { return res.sendStatus(401); }

Reply with any 2xx status. A non-2xx response or a timeout is retried with backoff, and every attempt is recorded in the delivery log on the Webhooks page.

Errors

{ "success": false, "error": { "code": "WHATSAPP_NOT_CONNECTED", "message": "WhatsApp session is not connected" }, "request_id": "req_8fA2kd91" }
CodeHTTPMeaning
INVALID_API_TOKEN401The token is unknown or has been revoked
ACCOUNT_SUSPENDED403The account is suspended
IP_NOT_ALLOWED401The source address is not on your allow-list
WHATSAPP_NOT_CONNECTED409No connected WhatsApp session to send from
SESSION_NOT_FOUND404No such session on this account
INVALID_MOBILE422The number could not be normalised
MESSAGE_REQUIRED422The message body is missing
MESSAGE_TOO_LONG422Longer than the plan allows
MEDIA_URL_REQUIRED422The media URL is missing
INVALID_MEDIA_URL422Rejected by the media checks
MEDIA_NOT_ALLOWED403Media is not included in the plan
RATE_LIMIT_EXCEEDED429Too many requests this minute
PLAN_LIMIT_EXCEEDED429Monthly message allowance used up
MESSAGE_NOT_FOUND404No such message on this account
INTERNAL_ERROR500Quote request_id to support

Rate limits

Limits come from your plan and apply per account per minute. Every response carries the current state:

X-RateLimit-Limit: 60 X-RateLimit-Remaining: 57 X-RateLimit-Reset: 1758369600

On 429 a Retry-After header tells you how many seconds to wait. Nothing is queued and nothing counts against your message allowance.

The full machine readable schema is at /swagger/v1/swagger.json, and you can try requests interactively in Swagger UI. A ready-to-import Postman collection ships in docs/postman.