Chat & conversations
LeadX has one unified inbox. WhatsApp, Instagram and website webchat all write to the same UnifiedMessage collection, so a conversation reads the same regardless of channel.
Everything chat-related except bulk campaigns is owned by leadx-ms-unified-chat. You reach it through leadx-api.
Read conversations
# Contacts with conversation history
curl https://api.leadx.in/api/key/protected/unified-chat -H "apikey: $KEY"
# Messages for one contact
curl -G https://api.leadx.in/api/key/protected/unified-chat/messages \
-H "apikey: $KEY" \
--data-urlencode 'contact=665f...' \
--data-urlencode 'p=1' --data-urlencode 'n=50'
Send a message
curl -X POST https://api.leadx.in/api/key/protected/unified-chat/messages \
-H "apikey: $KEY" -H "Content-Type: application/json" \
-d '{
"contact": "665f...",
"mode": "whatsapp",
"messageType": "message",
"content": { "text": "Thanks for getting in touch — are you free Thursday?" },
"leadId": "6660..."
}'
| Field | Notes |
|---|---|
mode | whatsapp, instagram, smtp / email, or webchat. An unknown mode is a 400. |
contact | Contact id. Required for every mode except instagram, which uses recipientId. |
recipientId | Instagram-scoped user id. |
content | Object, or a plain string for Instagram. Pass a template as content.template (id or document — only the id is stored). |
messageType | Default message. |
app / appType | Which connected app sends, when you have several. |
senderId | Specific sending identity. |
leadId | Attach the message to a lead's timeline. |
scheduled + scheduledAt | Send later. |
contactSpecificMessage | Per-contact override text. |
:::note webchat is delivered inline
mode: "webchat" is written directly rather than dispatched to the send service, because there is no external provider behind it. It requires contact.
:::
Human handover
Bots hold a conversation until a person takes over. Intercept is that takeover, and it is channel-agnostic:
# Current handover state
curl -G https://api.leadx.in/api/key/protected/conversation/handover \
-H "apikey: $KEY" \
--data-urlencode 'channel=whatsapp' \
--data-urlencode 'app=665f...' \
--data-urlencode 'externalThreadId=919876543210'
# Take over
curl -X PUT https://api.leadx.in/api/key/protected/conversation/intercept \
-H "apikey: $KEY" -H "Content-Type: application/json" \
-d '{ "channel": "whatsapp", "app": "665f...", "externalThreadId": "919876543210", "intercept": true }'
A conversation is keyed by { channel, app, account?, externalThreadId } — the same key the bot session uses. One pair of endpoints serves every channel.
The older PUT /chat-widget/session/:sessionId/intercept still exists for the widget client specifically.
Chat widgets
The embeddable website widget.
| Method | Endpoint | Purpose |
|---|---|---|
GET / POST / DELETE | /chat-widget | List / create / bulk delete. |
GET / PUT | /chat-widget/:id | Read / update. |
PUT | /chat-widget/:id/status | Enable or disable. |
PUT | /chat-widget/:id/rotate-token | Rotate the public embed token. |
GET | /chat-widget/:id/sessions | Visitor sessions. |
PUT | /chat-widget/session/:sessionId/intercept | Widget-specific takeover. |
Rotate the token if a widget's embed snippet leaks — it is a public credential by design.
Chatbots
Rule-based bots that answer before a human is involved.
| Method | Endpoint | Purpose |
|---|---|---|
GET / POST / DELETE | /manual-chatbot | Manage bots. |
GET / PUT / DELETE | /manual-chatbot/:id | One bot. |
POST | /manual-chatbot/keywords | Extract trigger keywords from text. |
POST | /manual-chatbot/ai-generate | Draft a whole bot with AI. |
Routing
Which bot answers on which channel:
| Method | Endpoint | Purpose |
|---|---|---|
GET / PUT | /manual-chatbot/chatbot-router | Router configuration. |
GET | /manual-chatbot/chatbot-router/bots | Bots eligible for routing. |
GET | /manual-chatbot/chatbot-router/apps | Apps usable as entry points. |
POST | /manual-chatbot/chatbot-router/entry-points | Create an entry point. |
PUT / DELETE | /manual-chatbot/chatbot-router/entry-points/:entryPointId | Update / remove. |
POST | /manual-chatbot/chatbot-router/entry-points/:entryPointId/test | Test routing without a real message. |
POST | /manual-chatbot/chatbot-router/rebuild | Rebuild keyword indexes. |
GET / PUT | /manual-chatbot/chatbot-router/session/stop | Read or end a live bot session. |
Run /rebuild after bulk keyword changes, or routing will keep using the stale index.
AI CRM chat
A conversational agent over your own CRM data — ask it questions, and approve any write it proposes.
| Method | Endpoint | Purpose |
|---|---|---|
GET / POST | /ai-crm-chat | List / start a chat. |
GET / PUT / DELETE | /ai-crm-chat/:id | Read / rename / soft-delete. |
POST | /ai-crm-chat/:id/message | Send a message. |
POST | /ai-crm-chat/:id/tool/:messageId/:callId | Approve or reject a proposed write. |
GET | /ai-crm-chat/usage | Usage against your plan. |
Approving a tool call is the moment the write actually happens — it is a POST for that reason. Chats are never hard-deleted.
Hitting the plan's AI ceiling returns AIUsageLimitReached_429.