Support
Customer-side chat conversation. The server resolves the active conversation from the widget identity on every request; there is no `conversation_id` to thread through. Open, send, and poll.
Open (or resume) a conversation
Idempotent. Calling it twice in the same session returns the same conversation.
const r = await client.support.openConversation()
if (!r.ok) {
console.error(r.error.code, r.error.message)
return
}
const { conversation, starters, messages } = r.value
console.log(conversation.id, conversation.status)
console.log(starters) // ChatWidgetStarter[] of quick-reply prompts
console.log(messages.length) // most recent messagesSend a message
Just text content; the SDK adds an idempotency key so retries are safe.
const r = await client.support.sendMessage('Hi, is the cherry tee in stock?', {
idempotencyKey: 'msg-2026-05-07-stock-check', // optional, auto-generated otherwise
})
if (!r.ok) {
console.error(r.error.code, r.error.message)
return
}
const message = r.value
console.log(message.id, message.created_at)
console.log(message.sender_type, message.content)Poll for new messages
let cursor: string | undefined
async function poll() {
const r = await client.support.getMessages({ after: cursor, limit: 50 })
if (!r.ok) return
for (const message of r.value) {
console.log(message.sender_type, message.content)
cursor = message.id
}
}
const intervalId = setInterval(poll, 3000)React: a chat widget
'use client'
import { useEffect, useState } from 'react'
import { useCimplifyClient } from '@cimplify/sdk/react'
import type { ChatMessage } from '@cimplify/sdk'
export function ChatPanel() {
const client = useCimplifyClient()
const [messages, setMessages] = useState<ChatMessage[]>([])
const [input, setInput] = useState('')
useEffect(() => {
let active = true
client.support.openConversation().then((r) => {
if (active && r.ok) setMessages(r.value.messages)
})
return () => { active = false }
}, [client])
async function send() {
const text = input.trim()
if (!text) return
setInput('')
const r = await client.support.sendMessage(text)
if (r.ok) setMessages((prev) => [...prev, r.value])
}
return (
<>
<ul>{messages.map((m) => <li key={m.id}>{m.sender_type}: {m.content}</li>)}</ul>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button onClick={send}>Send</button>
</>
)
}getMessages options
| Field | Type | Notes |
|---|---|---|
after | string | Last seen message id; return only newer |
limit | number | Page size |
Method reference
| Method | Returns |
|---|---|
openConversation(opts?) | Result<ChatConversationResponse> |
sendMessage(content, opts?) | Result<ChatMessage> |
getMessages({ after?, limit? }) | Result<ChatMessage[]> |
Related
-
Activity Session signals that surface contextual messages
-
Uploads Attach images / files to a chat message
-
Auth Logged-in users get conversation continuity
-
Error handling RATE_LIMITED is the most common code here
Uploads
Three-step direct-to-storage upload: `init` hands you a presigned URL, ` PUT` the bytes, then `confirm` commits the upload. `upload(file)` is the one-call sugar that runs the whole sequence for you.
Link
client.link covers saved addresses, saved mobile money, link preferences, and sessions. Customer-scoped surface routed through the SDK's separate linkApiUrl transport, not the per-business storefront API.