Server-side chat API
Create chat sessions, post customer and agent messages, show typing indicators, hand off bot conversations, and record CSAT from your backend with an API key, using the v2.1 chat endpoints.
The chat SDKs act as a customer, from a browser or device. The v2.1 chat API does the same jobs from your server with a regular API key, which is what you need when chat runs through something you host: your own bot, a messaging bridge, or an AI agent that answers before a human.
Every route takes the customer in the request (customerId) rather than a chat SDK token, and the messages show up in the customer's chat on every device, like any other chat message.
Roles
Use a server-side API key with these roles, never a chat key. See API keys and roles.
Answer chats with your own agent
A common pattern: your service (a bot or an AI agent) answers customers in chat, and hands off to Kustomer routing when it can't help.
Diagram source
sequenceDiagram autonumber participant C as Customer (chat SDK) participant K as Kustomer participant S as Your service C->>K: Sends a message K-->>S: Webhook or workflow: kustomer.message.create S->>K: POST /v2.1/chat/outbound/typing (typing: true) S->>S: Work out the answer S->>K: POST /v2.1/chat/outbound/messages (auto: true) S->>K: POST /v2.1/chat/outbound/typing (typing: false) K-->>C: Reply and typing state, in realtime
Hear about new messages
Subscribe to
kustomer.message.createwith an outbound webhook, or call your service from a workflow. Only act on inbound chat messages: check the message's channel ischatand its direction isin.Show that you're typing
cURLcurl -X POST "https://acme.api.kustomerapp.com/v2.1/chat/outbound/typing" \ -H "Authorization: Bearer $KUSTOMER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"customerId": "6716a3a9c4e5f60012ab34b0", "session": "6716a3a9c4e5f60012ab34c1", "from": "5f1b2c3d4e5f600012ab0001", "typing": true, "ai": true}'ai: trueshows the AI typing indicator instead of the agent one. You own the indicator's lifecycle: sending a message doesn't hide it, so sendtyping: falsewhen you're done, and repeattyping: trueabout every three seconds for long answers.Reply
cURLcurl -X POST "https://acme.api.kustomerapp.com/v2.1/chat/outbound/messages" \ -H "Authorization: Bearer $KUSTOMER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customerId": "6716a3a9c4e5f60012ab34b0", "session": "6716a3a9c4e5f60012ab34c1", "from": "5f1b2c3d4e5f600012ab0001", "body": "Your order shipped this morning. Tracking: 1Z999AA10123456784", "auto": true }'fromis the Kustomer user the message is attributed to; use a dedicated user for your bot.auto: truemarks the message as automated, so it doesn't count as the first human response in your reporting.Hand off
When a human should take over, stop replying and let routing assign the conversation, for example by updating a conversation attribute that your queue rules match on. A workflow can make that update for you.
Create a session
post/v2.1/chat/sessions
Opens a new chat conversation for an existing customer, for example to start a proactive chat:
{
"customerId": "6716a3a9c4e5f60012ab34b0",
"title": "Your order is delayed",
"brand": "5f1b2c3d4e5f600012ab0099"
}trackingId links the session to a chat SDK identity. Leave it out and Kustomer reuses the one from the customer's latest chat session, if any. When you pass assistant, Kustomer also starts that assistant's opening dialog in the new session.
Import a bot conversation
post/v2.1/chat/handoff
If a conversation started in your own bot, import the whole thing so agents see the history:
{
"customerId": "6716a3a9c4e5f60012ab34b0",
"active": true,
"from": "Acme Bot",
"externalId": "bot-thread-88123",
"messages": [
{ "date": "2026-10-21T15:00:02Z", "direction": "in", "body": "Where's my order?" },
{ "date": "2026-10-21T15:00:04Z", "direction": "out", "body": "I can help. What's your order number?" },
{ "date": "2026-10-21T15:00:31Z", "direction": "in", "body": "4417, and I need to talk to a person" }
]
}The chat SDKs have the same feature as handoff(), for imports that happen in the customer's browser or app.
Customer messages from your server
post/v2.1/chat/messages
Posts a message as the customer, for bridges that receive customer messages somewhere else. customerId must match the session's customer. Send body, attachments, or both.
Read chats
curl "https://acme.api.kustomerapp.com/v2.1/chat/sessions?customerId=6716a3a9c4e5f60012ab34b0&active=true" \
-H "Authorization: Bearer $KUSTOMER_API_KEY"Pass customerId, trackingId, or both (sessions matching either are returned); with neither you get a 400. Results are newest first by lastMessageAt. Session metadata doesn't include messages: fetch those with GET /v2.1/chat/sessions/{id}/messages, using fields to limit the attributes returned.
CSAT
put/v2.1/chat/satisfaction-responses/{id}
Records a rating, a comment, or both on a CSAT response that Kustomer already created for the chat:
rating is 0 to 10, and answers takes up to three answers. Add ?include=satisfaction to get the form back, with the follow-up questions for that rating.