Appearance
Live Chat & Widgets
How chat renders depends on the operator's provider (see Support Overview): an injected vendor widget, or your own UI on the platform's REST chat.
Provider widgets (Zendesk / Intercom)
If the operator configured a provider, the SDK injects the vendor's <script> tag once and passes the player's display context (name, email, VIP tier) to the widget; support agents know who they are talking to the moment a chat starts.
javascript
const config = await sdk.support.getConfig();
if (config.liveChatEnabled && config.provider !== 'CUSTOM' && config.provider !== 'NONE') {
await sdk.support.injectWidget(config, {
userId: user.id,
username: user.username,
email: user.email,
vipTier: 'Gold' // optional; surfaces VIP status for priority support
});
}Rules:
text
GIVEN a provider widget is configured
WHEN injectWidget(config, userContext) is called
THEN the vendor script is injected exactly once (repeated calls are no-ops),
and the widget receives only the display context; never tokens or JWTs.
GIVEN a provider is configured WITHOUT a providerKeyId
WHEN injectWidget is called
THEN nothing is injected and the call reports the misconfiguration;
check the operator's widget key.Security: the injected script runs on the operator's origin. Content-Security-Policy is the operator's responsibility; the SDK never passes tokens or JWTs to any widget.
Custom chat (REST)
With provider: 'CUSTOM', you build the UI on the platform's chat. The player's context is derived server-side from the authenticated session; user id, username, email, and VIP tier are never client-supplied, so identity and priority cannot be spoofed.
javascript
const sessionId = await sdk.support.initializeChat();
await sdk.support.sendMessage("My deposit hasn't reflected yet.");Agent replies: real-time, with polling fallback
Agent replies arrive on the platform's shared real-time channel:
typescript
interface ChatMessageReceivedPayload {
eventId: string; // dedupe key; see Real-Time Channel
sessionId: string;
message: ChatMessage; // the full typed message (id, sender, body, timestamp)
}text
GIVEN an active chat session
WHEN the agent sends a message
THEN a CHAT_MESSAGE_RECEIVED event fires on the real-time channel,
carrying the full message.
GIVEN a client that was disconnected when the agent replied
WHEN it reconnects within the 24-hour replay window
THEN the missed reply is redelivered (dedupe by eventId);
otherwise, and always as a fallback, getHistory(sessionId) returns
the full transcript.javascript
sdk.support.onChatMessage((event) => {
appendToTranscript(event.message);
});Session lifecycle
| State | Entry trigger | Allowed transitions | Side effects |
|---|---|---|---|
ACTIVE | initializeChat() succeeds | → ENDED (player or agent ends the chat, or inactivity timeout) | Messages exchangeable; events fire |
ENDED | End/timeout | terminal | Transcript retained; see Chat History |
endChat() ends the current session locally and server-side. Sessions never vanish: ended transcripts remain reviewable via getSessions() / getHistory().
Error contract
| Code | Error class | HTTP status | Trigger condition | Retryable |
|---|---|---|---|---|
SUPPORT_SESSION_NOT_FOUND | ChatSessionNotFoundError | 404 | Unknown sessionId (history/send) | No |
SUPPORT_NO_ACTIVE_SESSION | NoActiveChatSessionError | 409 | History/send with no active session and no sessionId | No (initializeChat first) |
SUPPORT_WIDGET_MISCONFIGURED | WidgetMisconfiguredError | 400 | injectWidget with a provider configured but no providerKeyId | No (check the operator's widget key) |
Common errors are defined once in the Error Reference.
Sandbox
The sandbox provider is CUSTOM with live chat enabled; the REST chat flow with seeded greeting messages.
Divergence flag: sandbox agent auto-replies are fixed English text delivered ~1.5 seconds after each player message; good enough for UI testing, not for content testing.