Skip to Content
API ReferenceEndpoints

Endpoints

Every endpoint lives under https://YOUR_DOMAIN/api/v1 and requires an API key (Authorization: Bearer lw_live_... or X-API-Key: lw_live_...). See Authentication for keys and scopes, and Rate Limits for the per-key budget.

List endpoints wrap results in a data array. Errors return { "error": "...", "message": "..." }. Reads work even on an expired license; writes return 402 when the license is locked in enforce mode.

Conventions

ThingDetail
Base URLhttps://YOUR_DOMAIN/api/v1
AuthAuthorization: Bearer lw_live_... or X-API-Key: lw_live_...
Scope checkA key with * passes everything; otherwise the exact scope must be present (403 insufficient_scope).
Content typeapplication/json for request bodies.

System

Liveness check

GET /healthscope: any valid key (no scope needed).

Returns ok plus the API version.

Request
curl https://YOUR_DOMAIN/api/v1/health \ -H "Authorization: Bearer lw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
200 OK
{ "ok": true, "version": "1.0.0" }

Messages

Send a WhatsApp message

POST /messagesscope: messages:write

Sends a text message from a connected, ready number. If from is omitted, the first ready number is used. Returns 202 once the message is handed to the WhatsApp engine.

Body fieldTypeRequiredDescription
tostringYesPhone number (digits, with country code) or a full WhatsApp jid.
textstringYesMessage body.
fromstringNoId of the connected number to send from. Defaults to the first ready number.
Request
curl -X POST https://YOUR_DOMAIN/api/v1/messages \ -H "Authorization: Bearer lw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "to": "919812345678", "text": "Hi! Thanks for reaching out." }'
202 Accepted
{ "status": "sent", "to": "919812345678@s.whatsapp.net", "from": "n1a2b3c" }

Possible errors: 400 invalid_input, 401 invalid_api_key, 402 license_locked, 403 insufficient_scope, 409 no_ready_session, 429 rate_limited.


Chats

List chats

GET /chatsscope: chats:read

Returns chats for a number, newest activity first. If sessionId is omitted, the first connected number is used.

Query paramTypeRequiredDescription
sessionIdstringNoNumber/session id. Defaults to the first connected number.
limitintegerNoMax chats to return (default 200).
Request
curl "https://YOUR_DOMAIN/api/v1/chats?limit=50" \ -H "X-API-Key: lw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
200 OK
{ "data": [ { "sessionId": "n1a2b3c", "chatId": "919812345678@s.whatsapp.net", "name": "Asha Rao", "isGroup": false, "status": "open", "assignee": null, "assigneeName": null, "unreadCount": 2, "lastMessage": "Refund please", "lastTimestamp": 1751193300 } ] }

List messages in a chat

GET /chats/{chatId}/messagesscope: chats:read

Returns messages for a chat, newest first.

ParamInTypeRequiredDescription
chatIdpathstringYesThe chat id (a WhatsApp jid, e.g. 919812345678@s.whatsapp.net).
sessionIdquerystringNoNumber/session id.
limitqueryintegerNoMax messages to return (default 50).
Request
curl "https://YOUR_DOMAIN/api/v1/chats/919812345678@s.whatsapp.net/messages?limit=20" \ -H "X-API-Key: lw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
200 OK
{ "data": [ { "messageId": "ABCD1234", "sessionId": "n1a2b3c", "chatId": "919812345678@s.whatsapp.net", "body": "Refund please", "type": "chat", "author": "919812345678@s.whatsapp.net", "fromMe": false, "hasMedia": false, "timestamp": 1751193300 } ] }

Contacts

List contacts

GET /contactsscope: contacts:read

Returns contacts, optionally filtered by a free-text query and lifecycle stage.

Query paramTypeRequiredDescription
qstringNoSearch across name, email, and phone.
stagestringNoOne of lead, active, customer, vip, churned.
Request
curl "https://YOUR_DOMAIN/api/v1/contacts?stage=vip" \ -H "X-API-Key: lw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
200 OK
{ "data": [ { "id": "c_1a2b3c", "name": "Asha Rao", "phone": "919812345678", "email": "asha@example.com", "company": "Acme", "location": "Bengaluru", "owner": "agent-7", "tags": ["website", "wholesale"], "lifecycle": "vip", "lifetimeValue": "₹12,000", "notes": "Prefers WhatsApp over email." } ] }

Create a contact

POST /contactsscope: contacts:write

Creates a CRM contact. name and phone are required.

Body fieldTypeRequiredDescription
namestringYesContact name.
phonestringYesPhone number (with country code).
emailstringNoEmail address.
companystringNoCompany name.
locationstringNoLocation.
ownerstringNoAgent/rep responsible.
tagsstring[]NoFree-form labels.
lifecyclestringNolead, active, customer, vip, or churned.
lifetimeValuestringNoFree-text value (e.g. ₹12,000).
notesstringNoInternal notes.
Request
curl -X POST https://YOUR_DOMAIN/api/v1/contacts \ -H "X-API-Key: lw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "name": "Asha Rao", "phone": "919812345678", "email": "asha@example.com", "lifecycle": "lead", "tags": ["website"] }'
201 Created
{ "data": { "id": "c_1a2b3c", "name": "Asha Rao", "phone": "919812345678", "email": "asha@example.com", "tags": ["website"], "lifecycle": "lead" } }

Tickets

List tickets

GET /ticketsscope: tickets:read

Returns tickets, optionally filtered by status and assignee.

Query paramTypeRequiredDescription
statusstringNoOne of open, in_progress, waiting, resolved, closed.
assigneestringNoFilter by assigned agent.
Request
curl "https://YOUR_DOMAIN/api/v1/tickets?status=open" \ -H "X-API-Key: lw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
200 OK
{ "data": [ { "id": "t_9f8e7d", "ref": "TKT-2041", "subject": "Refund not received", "customerName": "Asha Rao", "customerPhone": "919812345678", "status": "open", "priority": "high", "assignee": "agent-7", "tag": "chat" } ] }

Create a ticket

POST /ticketsscope: tickets:write

Creates a support ticket. Only subject is required.

Body fieldTypeRequiredDescription
subjectstringYesShort description of the issue.
customerNamestringNoWho the ticket is for.
customerPhonestringNoCustomer phone number.
statusstringNoopen, in_progress, waiting, resolved, closed.
prioritystringNourgent, high, med, low (defaults to med).
assigneestringNoAgent responsible.
tagstringNoSource/category tag.
Request
curl -X POST https://YOUR_DOMAIN/api/v1/tickets \ -H "X-API-Key: lw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "subject": "Refund not received", "customerName": "Asha Rao", "customerPhone": "919812345678", "priority": "high" }'
201 Created
{ "data": { "id": "t_9f8e7d", "ref": "TKT-2041", "subject": "Refund not received", "customerName": "Asha Rao", "customerPhone": "919812345678", "status": "open", "priority": "high", "tag": "api" } }

Broadcasts

Create and queue a broadcast

POST /broadcastsscope: broadcasts:write

Creates a broadcast and immediately queues it. The paced worker sends one recipient per tick with randomized delays and a daily cap — see Anti-Ban Pacing. Up to 5000 recipients per broadcast.

broadcasts:write is deliberately a separate scope because broadcasts carry the highest ban risk. Grant it only to keys that truly need to send bulk messages, and only broadcast to opted-in recipients.

Body fieldTypeRequiredDescription
namestringYesA name for the campaign.
sessionIdstringYesId of the connected number to send from.
messagestringYesMessage body. Supports the {{name}} token.
recipientsarrayYes1–5000 items of { "chatId": "...", "name": "..." } (chatId required).
Request
curl -X POST https://YOUR_DOMAIN/api/v1/broadcasts \ -H "X-API-Key: lw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "name": "October offer", "sessionId": "n1a2b3c", "message": "Hi {{name}}, here is 10% off this week.", "recipients": [ { "chatId": "919812345678@s.whatsapp.net", "name": "Asha" } ] }'
201 Created
{ "data": { "id": "b_5e4d3c", "name": "October offer", "sessionId": "n1a2b3c", "message": "Hi {{name}}, here is 10% off this week.", "status": "queued", "total": 1, "sentCount": 0, "failedCount": 0 } }

Error responses

Every endpoint can return these shared errors:

StatuserrorMeaning
400invalid_inputThe request body or parameters are invalid.
401invalid_api_keyMissing, malformed, or disabled key.
402license_lockedLicense expired/revoked in enforce mode (writes only).
403insufficient_scopeThe key lacks the scope for this endpoint.
409no_ready_sessionNo connected WhatsApp number is ready (send only).
429rate_limitedPer-key rate limit exceeded (120/min).
Back to the API overview

Base URL, scopes, and how the API fits together.