Web Core API
Build a custom chat UI on KustomerCore, the headless web chat SDK. Loading core.js, init, conversations, messages, typing, read state, knowledge base, CSAT, identity, and every event.
core.js is the chat SDK without a UI. It handles identity, conversations, messages, realtime delivery and the knowledge base, and you render everything yourself. The widget is built on the same Core, so anything the widget does, your UI can do too.
Load Core
<script
src="https://cdn.kustomerapp.com/chat-web/core.js"
data-kustomer-api-key="YOUR_CHAT_KEY"
></script>
<script>
window.addEventListener('kustomerCoreLoaded', function () {
KustomerCore.init({ brandId: 'YOUR_BRAND_ID' }, function (res, error) {
if (error) return console.error(error)
console.log('Chat settings', res.settings)
})
})
</script>Use a chat key with only the org.tracking role, as for the widget. Core runs in a hidden iframe, defines window.KustomerCore, and fires kustomerCoreLoaded when it's ready. init calls made before that are queued.
Every method takes an optional callback, called as callback(response, error). Check error first: it's an Error with optional status, code, title and source from the API.
init
brandIdstring- Brand whose chat settings to load. Defaults to the default brand.
assistantIdstring- Assistant to use for new conversations.
scheduleIdstring- Business schedule for availability.
langstring- Language for assistant and knowledge base content, such as
en_usorfr. enableUrlUnfurlingboolean- Deliver link previews with messages.
The response's settings is what admins configured for the brand in Kustomer. Use it to render your UI:
Call getChatSettings() later to read them again, and isChatAvailable() for the current availability: { availability: 'online' | 'offline' | 'hidden' | 'disabled' }.
Build a minimal chat
let conversationId
KustomerCore.addListener('onMessageReceived', function (message) {
if (message.conversationId !== conversationId) return
render(message)
KustomerCore.markRead({ conversationId })
})
KustomerCore.addListener('onAgentTypingActivity', function (activity) {
showTyping(activity)
})
function startChat() {
KustomerCore.createConversation({ title: 'Help with my order' }, function (conversation, error) {
if (error) return showError(error)
conversationId = conversation.conversationId
})
}
function send(body) {
KustomerCore.sendMessage({ conversationId, body }, function (message, error) {
if (error) return showError(error)
render(message)
})
}
let typingTimer
function onInput() {
KustomerCore.sendTypingActivity({ conversationId, typing: true })
clearTimeout(typingTimer)
typingTimer = setTimeout(function () {
KustomerCore.sendTypingActivity({ conversationId, typing: false })
}, 3000)
}A message object looks like this:
{
"messageId": "6716a3b2c4e5f60012ab34cd",
"conversationId": "6716a3a9c4e5f60012ab34c1",
"body": "Your order shipped this morning.",
"direction": "out",
"createdAt": "2026-10-21T15:04:12.511Z",
"attachments": [],
"sentBy": { "type": "user", "id": "5f1b…", "displayName": "Ada", "avatarUrl": "https://…" },
"isInAssistantMode": false
}direction is in for the customer's messages and out for agents, assistants and automations.
Methods
Conversations
A conversation has conversationId, createdAt, lastMessageAt, preview, lastResponder (displayName, avatarUrl), ended, isInAssistantMode and custom.
Messages
Attachments are browser File objects, for example from <input type="file">. Respect settings.disableAttachments.
Identity
See Customer identity for generating the JWT.
Knowledge base
Articles have articleId, title, description, htmlBody, slug, lang, updatedAt and kbUrl. htmlBody is HTML from your knowledge base: sanitize it before inserting it into your page.
Satisfaction (CSAT)
When Kustomer sends a CSAT survey for a chat conversation, Core fires onSatisfactionReceived with it. getMessages also returns it as satisfaction.
Other
Events
Subscribe with KustomerCore.addListener(event, callback) and unsubscribe with removeListener(event, callback), passing the same function.