CRM Integration
Bidirectional ticket management and live chat — receive ticket events via webhooks and act on them from your own CRM.
Overview
The CRM Integration API lets you connect Next Cap directly to your existing CRM — Salesforce, HubSpot, Zendesk, or any custom system. With a single API key, you get access to both live chat (real-time WebSocket) and ticketing (REST API), enabling full bidirectional communication from your own dashboard.
Real-time bidirectional connection via Socket.IO WebSocket. Receive messages, typing indicators, and conversation events as they happen. Send replies instantly from your CRM. WebSocket is the only way to integrate live chat — it requires a persistent connection for real-time data.
Webhooks (recommended) push ticket events to your server in real time — new tickets, status changes, replies, and assignments. Use the REST API to take action: reply, assign, close, reopen, and update tickets. Webhooks keep your CRM in sync without polling.
How It Works
Unlike the read-only External Data API, the CRM Integration API provides per-user authentication. Every API key is tied to a seat in your organization and inherits that seat's exact permissions and widget assignments. This means:
- Every CRM key occupies a paid seat in your organization
- Each key inherits the seat's permissions (e.g.,
live-chat:join,tickets:manage) - Each key only sees data from widgets the seat is assigned to
- If the seat has live-chat permissions, the key can access live chat — if it has ticketing permissions, it can access tickets — one key, both features
- If a seat is removed or deactivated, its CRM key stops working immediately
Authentication
CRM keys use the branded nextcap_ck_ prefix. The same key works for both REST and WebSocket:
REST API (Tickets & Conversations)
X-Api-Key: nextcap_ck_YOUR_KEY# orAuthorization: Bearer nextcap_ck_YOUR_KEY
WebSocket (Live Chat)
const socket = io("wss://api.nextcap.ai/crm-chat", {auth: { apiKey: "nextcap_ck_YOUR_KEY" },transports: ["websocket"],});
Obtaining CRM Keys
CRM keys can only be created from the Next Cap Dashboard
There is no API endpoint for creating CRM keys. Only organization owners and admins can generate them through the dashboard interface. This is by design — API keys grant server-to-server access to your organization's data and must be provisioned securely.
There are two ways to create a CRM key, depending on your use case:
API-Only Seat Recommended for integrations
Create a dedicated CRM seat without inviting anyone or requiring sign-up. The seat gets a role, widget access, and an API key — all in one step. No email, no OTP, no dashboard login needed.
- Navigate to Settings → API Keys → CRM Integration.
- Click API-Only Seat.
- Enter a name for the seat (e.g., "Salesforce Integration") and a key name.
- Choose a role — this controls which permissions the key has. For live chat access, assign a role with
live-chat:joinandconversations:view. For ticketing, assigntickets:manage. - Select widget access — choose which agents/widgets the key can see data from.
- Optionally add an email — if someone later needs dashboard access, they can register with this email to activate the seat.
- Click Create Seat & Key. Copy the key immediately.
CRM Key for existing user
If a team member already has a dashboard account and you want to give them CRM API access:
- Navigate to Settings → API Keys → CRM Integration.
- Click Create Key.
- Select the team member from the dropdown and give the key a name.
- Copy the key immediately — it is shown only once.
The key inherits the user's existing role and widget access. If the user has live-chat permissions, the key automatically works for live chat too.
Live Chat Integration
The live chat integration uses WebSocket (Socket.IO) for real-time bidirectional communication. Connect your CRM to receive messages, typing indicators, and conversation events the moment they happen — and reply to visitors instantly.
live-chat:view or conversations:view to connect. To send messages and manage conversations, live-chat:join is required.WebSocket Connection
Connect to the /crm-chat namespace with your CRM API key:
wss://api.nextcap.ai/crm-chat
Quick Start — Live Chat
import { io } from "socket.io-client";const CRM_KEY = "nextcap_ck_YOUR_KEY";// Connect to the CRM chat WebSocketconst socket = io("wss://api.nextcap.ai/crm-chat", {auth: { apiKey: CRM_KEY },transports: ["websocket"],});socket.on("connect", () => {console.log("Connected to live chat");});// Join a conversation roomsocket.emit("crm:join", { conversationId: "CONV_ID" }, (res) => {console.log("Joined:", res);});// Listen for new messagessocket.on("message:new", (msg) => {console.log(`[${msg.role}] ${msg.content}`);});// Send a message as the agentsocket.emit("crm:message", {conversationId: "CONV_ID",content: "Hi, how can I help you?",});// Listen for typing indicatorssocket.on("typing", (data) => {console.log(`${data.role} is typing...`);});// Listen for conversation lifecycle eventssocket.on("agent:assigned", (data) => {console.log("Agent assigned:", data.agentUserId);});socket.on("conversation:resolved", (data) => {console.log("Conversation resolved:", data.conversationId);});
Client → Server Events
Events your CRM sends to Next Cap:
Server → Client Events
Events your CRM receives from Next Cap for conversations you've joined:
message:neweventA new message was sent in the conversation. Payload: id, conversationId, role (user/agent/assistant/system), content, createdAt, attachments.
typingeventSomeone is typing. Payload: conversationId, role (agent/visitor), identifier.
typing:contenteventVisitor's in-progress text (sneak peek). Payload: conversationId, visitorId, content.
agent:assignedeventAn agent was assigned to the conversation. Payload: conversationId, agentUserId, timestamp.
conversation:resolvedeventThe conversation was resolved. Payload: conversationId, timestamp.
ticket:createdeventA ticket was created from this conversation. Payload: conversationId, ticketNumber, subject, timestamp.
feedback:receivedeventVisitor submitted a satisfaction rating. Payload: conversationId, satisfactionRating, feedbackData.
Example event payload
{"id": "msg_abc123","conversationId": "conv_xyz789","role": "user","content": "I need help with my order","visitorId": "visitor_456","createdAt": "2026-04-07T14:30:00.000Z","attachments": null}
Conversation REST Endpoints
Use these REST endpoints alongside the WebSocket for initial data loading, listing conversations, and managing the handoff queue:
# List live chat conversationscurl -H "X-Api-Key: nextcap_ck_YOUR_KEY" \"https://api.nextcap.ai/api/v1/external/crm/conversations?status=handed_off&per_page=20"# Get a conversation with messagescurl -H "X-Api-Key: nextcap_ck_YOUR_KEY" \"https://api.nextcap.ai/api/v1/external/crm/conversations/CONV_ID"# Get the handoff queuecurl -H "X-Api-Key: nextcap_ck_YOUR_KEY" \"https://api.nextcap.ai/api/v1/external/crm/conversations/queue"# Assign an agent to a conversationcurl -X POST -H "X-Api-Key: nextcap_ck_YOUR_KEY" \-H "Content-Type: application/json" \-d '{"agent_id": "USER_UUID"}' \"https://api.nextcap.ai/api/v1/external/crm/conversations/CONV_ID/assign"# Resolve a conversationcurl -X POST -H "X-Api-Key: nextcap_ck_YOUR_KEY" \"https://api.nextcap.ai/api/v1/external/crm/conversations/CONV_ID/resolve"
Typical Integration Flow
- Connect WebSocket — establish a persistent connection to
/crm-chatwith your API key. - Fetch the queue — call
GET /external/crm/conversations/queueto see conversations waiting for an agent. - Join conversations — emit
crm:joinfor each conversation you want to monitor. - Assign agent — call
POST /external/crm/conversations/:id/assignto pick up a conversation. - Chat — send messages via
crm:message, receive visitor messages viamessage:new. - Resolve — call
POST /external/crm/conversations/:id/resolvewhen done.
Ticketing Integration
For ticketing, the recommended approach is webhooks + REST API. Webhooks push events to your server the moment something happens (ticket created, status changed, reply received), so your CRM stays in sync without polling. Then use the REST API to take action — reply, assign, close, or update tickets.
tickets:view, tickets:manage) to access ticket endpoints.Quick Start — Ticketing
# List tickets visible to your CRM usercurl -H "X-Api-Key: nextcap_ck_YOUR_KEY" \"https://api.nextcap.ai/api/v1/external/crm/tickets?status=open&per_page=20"# Reply to a ticketcurl -X POST -H "X-Api-Key: nextcap_ck_YOUR_KEY" \-H "Content-Type: application/json" \-d '{"content": "We are looking into this issue."}' \"https://api.nextcap.ai/api/v1/external/crm/tickets/TICKET_ID/reply"
Ticket Endpoints
Ticket Actions
Attachments
Two flows are available depending on your needs. Direct upload is simplest — stream the file to our API in one request. Presigned PUT is useful when you want the file to go straight to storage without passing through our server. After either flow you get an attachment_id, which you then pass as attachment_ids[] on ticket create / reply / customer-reply.
Webhook Events
Use these webhook events to keep your CRM in sync with Next Cap. Register webhooks via the POST /external/webhooks endpoint.
ticket.createdeventA new ticket was created.
ticket.updatedeventA ticket was modified (any field change).
ticket.status_changedeventTicket status changed (e.g., open → resolved).
ticket.assignedeventA ticket was assigned or reassigned.
ticket.department_changedeventA ticket was transferred to a different department.
ticket.reopenedeventA ticket was reopened.
ticket.replyeventAn agent replied to a ticket (includes content and actor).
ticket.customer_replyeventA customer replied to a ticket via widget or email.
conversation.createdeventA new conversation was started.
conversation.completedeventA conversation was completed by the AI.
conversation.handed_offeventA conversation was escalated to a human agent.
Ticket Status Transitions
Ticket status follows a strict state machine. Invalid transitions return 400 Bad Request.
new → open, in_progress, closedopen → in_progress, waiting_customer, waiting_internal, resolved, closedin_progress → waiting_customer, waiting_internal, resolved, closedwaiting_customer → open, in_progress, resolved, closedwaiting_internal → open, in_progress, resolved, closedresolved → closed, reopenedclosed → reopenedreopened → open, in_progress, waiting_customer, waiting_internal, resolved, closed
Permissions Reference
The CRM key inherits the user's role permissions. Here's what each feature requires:
WebSocket connectlive-chat:view or conversations:viewConnect to the /crm-chat WebSocket.
crm:joinconversations:view or conversations:view-ownJoin a conversation room.
crm:messagelive-chat:joinSend messages to conversations.
crm:typinglive-chat:joinSend typing indicators.
GET /conversationsconversations:view or conversations:view-ownList conversations (view-own restricts to assigned only).
GET /conversations/:idconversations:view or conversations:view-ownGet conversation details.
GET /conversations/queuelive-chat:joinView the handoff queue.
POST /conversations/:id/assignlive-chat:joinAssign an agent.
POST /conversations/:id/resolvelive-chat:joinResolve a conversation.
GET /ticketstickets:viewList tickets (widget scoping applies).
GET /tickets/:idtickets:viewView a single ticket.
GET /tickets/:id/eventstickets:viewView ticket timeline.
PATCH /tickets/:idtickets:manageUpdate ticket fields.
POST /tickets/:id/replytickets:replyReply to a ticket.
POST /tickets/:id/assigntickets:assignAssign tickets.
POST /tickets/:id/transfertickets:manageTransfer departments.
POST /tickets/:id/closetickets:closeClose tickets.
POST /tickets/:id/reopentickets:manageReopen tickets.
POST /tickets/:id/notestickets:manageAdd notes.