Chat SDK reference
The API surface for @getuserfeedback/chat and @getuserfeedback/chat-react.
- Last reviewed
Chat SDK reference
The Chat SDK is a headless client for user-facing conversation UIs. Use it when your app should show conversation history and let a signed-in user reply from your own UI.
If you're setting up for the first time, start with the React Chat SDK guide or JavaScript Chat SDK guide.
Packages
| Package | Use it when |
|---|---|
@getuserfeedback/chat | You want a TypeScript client for the Chat API. Works in browsers, React Native, and server-side proxy code with fetch. |
@getuserfeedback/chat-react | You want React or React Native hooks. |
Authentication
Every request needs a JWT bearer token created by your app. Configure your backend or auth provider to issue a token for the signed-in user with this audience:
aud: https://getuserfeedback.com/v1/apps/{appId}sub: the stable identity for this user in your appThe default setup uses sub as the user's stable app identity. If your
getuserfeedback.com app uses a custom JWT claim mapping, configure one primary
signed claim for chat, such as the per-user id from your app.
Pass that token to the SDK. The SDK sends it as a bearer token:
Authorization: Bearer <token>getuserfeedback.com verifies the token and uses the signed app identity to decide which conversations the user can read or reply to.
createChatClient(options)
import { createChatClient } from "@getuserfeedback/chat";const chat = createChatClient({auth: {jwt: {token,},},});Options
| Option | Type | Default | Description |
|---|---|---|---|
auth.jwt.token | string | required | JWT bearer token for the current user session. |
baseUrl | string | https://api.getuserfeedback.com/v1 | Absolute API base URL. Custom values are used exactly. |
fetch | typeof fetch | globalThis.fetch | Custom fetch implementation. Required only in runtimes without global fetch. |
baseUrl must be absolute. If you proxy through your backend, include the
full path your proxy expects:
createChatClient({baseUrl: "https://app.example.com/api/chat/v1",auth: { jwt: { token: appSessionToken } },});In proxy mode, auth.jwt.token is the bearer token your backend expects. Your
backend should authenticate that token, then call getuserfeedback.com with a JWT
minted for your getuserfeedback.com app and that user.
chat.configure(updates)
Update auth after the client has been created.
await chat.configure({auth: {jwt: {token: refreshedToken,},},});Clear auth on logout:
await chat.configure({auth: {jwt: null,},});After auth is cleared, SDK requests fail with missing_auth_jwt until a new
token is configured.
chat.conversations.list(input?)
Lists conversations visible to the signed app identity.
const page = await chat.conversations.list({limit: 25,});Input
| Field | Type | Description |
|---|---|---|
limit | number | Page size. Defaults to the API default. |
cursor | string | Cursor returned by a previous page. |
signal | AbortSignal | Optional cancellation signal. |
Response
type ChatConversationListResponse = {conversations: ChatConversation[];limit: number;nextCursor: string | null;};chat.conversations.markRead(input)
Marks a conversation as read through a specific message. Call this after your UI has shown that message to the user.
const { readState, unreadCount } = await chat.conversations.markRead({conversationId: "conv_123",messageId: "msg_123",});Input
| Field | Type | Description |
|---|---|---|
conversationId | string | Conversation containing the visible message. |
messageId | string | Latest message the user has seen. |
signal | AbortSignal | Optional cancellation signal. |
Response
type ChatConversationMarkReadResponse = {readState: {lastReadMessageId: string;lastReadMessageTimestamp: string;readAt: string;};unreadCount: number;};Read state only moves forward. Marking an older message does not make newer messages unread again. The response includes the conversation's authoritative remaining unread count after the update.
Use compareChatConversationReadPositions(left, right) when reconciling read
state from concurrent requests. It returns -1, 0, or 1 and follows the
same message ordering as the API.
chat.messages.list(input)
Lists messages in one conversation.
const page = await chat.messages.list({conversationId: "conv_123",limit: 50,});Input
| Field | Type | Description |
|---|---|---|
conversationId | string | Conversation to read. |
limit | number | Page size. Defaults to the API default. |
cursor | string | Cursor returned by a previous page. |
signal | AbortSignal | Optional cancellation signal. |
Response
type ChatMessageListResponse = {conversationId: string;limit: number;messages: ChatMessage[];nextCursor: string | null;};chat.messages.send(input)
Sends a message from the signed app identity into an existing conversation.
const { message } = await chat.messages.send({conversationId: "conv_123",text: "Thanks — that helps.",});Input
| Field | Type | Description |
|---|---|---|
conversationId | string | Conversation to send into. |
text | string | Message text, from 1 to 10,000 characters. |
clientMessageId | string | Optional idempotency key for this message. Set this only when you manage send retries yourself, such as in a proxy or non-React client. Reuse the same value when retrying the same send after a timeout or lost response. |
signal | AbortSignal | Optional cancellation signal. |
Response
type ChatMessageSendResponse = {message: ChatMessage;};Message shape
type ChatMessage = {author: "system" | "team" | "unknown" | "user";content:| { type: "text"; text: string }| { type: "flow_response"; responseId: string }| { type: "unsupported" };conversationId: string;id: string;receivedAt: string;sentAt: string;timestamp: string;};Render unsupported with a quiet fallback. It means the conversation contains
a message type this SDK version doesn't know how to display yet.
React provider
import { GetUserFeedbackChatProvider } from "@getuserfeedback/chat-react";<GetUserFeedbackChatProviderclientOptions={{auth: {jwt: {token,},},}}><App /></GetUserFeedbackChatProvider>The provider creates the chat client and owns an in-memory store for the hooks
below it. When clientOptions.auth.jwt.token changes, the provider updates auth
and resets the store so the next user doesn't see the previous user's cached
conversations.
Props
| Prop | Type | Description |
|---|---|---|
clientOptions | ChatClientOptions | Recommended. Provider-owned client options. |
client | ChatClient | Advanced. Pass an explicitly created client instead of clientOptions. |
Use clientOptions.baseUrl when proxying through your backend:
<GetUserFeedbackChatProviderclientOptions={{baseUrl: "https://app.example.com/api/chat/v1",auth: { jwt: { token: appSessionToken } },}}><App /></GetUserFeedbackChatProvider>useConversations(options?)
Loads conversations on mount by default.
const {conversations,error,hasMore,loadMore,refresh,status,} = useConversations({ limit: 25 });Options
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Set to false to disable the first load. |
limit | number | 25 | Page size for the initial load and pagination. |
Return value
| Field | Description |
|---|---|
conversations | Loaded conversations. |
status | Load state for the list. |
error | Last load error, if any. |
hasMore | true when another page is available. |
loadMore() | Loads the next page. |
refresh() | Reloads the first page. |
useCreateConversation()
Stores a draft for a new conversation and creates the conversation with the first message.
const {create,error,reset,setText,status,text,} = useCreateConversation();Return value
| Field | Description |
|---|---|
text | Current draft text. |
setText(text) | Updates the draft text. |
reset() | Clears the draft and resets create state. |
create({ text? }) | Creates a conversation with the draft or provided text and returns the created conversation and message. |
status | Create state: idle, pending, success, or error. |
error | Last create error, if any. |
useConversation(options)
Loads one conversation's messages on mount by default.
const {error,hasMore,loadMore,markRead,messages,refresh,send,sendState,status,} = useConversation({ conversationId: "conv_123" });Options
| Field | Type | Default | Description |
|---|---|---|---|
conversationId | string | required | Conversation to read and send into. |
enabled | boolean | true | Set to false to disable the first load. |
limit | number | 50 | Page size for the initial load and pagination. |
Return value
| Field | Description |
|---|---|
messages | Loaded messages. |
status | Load state for messages. |
error | Last message load error, if any. |
hasMore | true when older messages are available. |
loadMore() | Loads older messages. |
markRead({ messageId }) | Marks the conversation read through a message your UI has confirmed is visible and returns the updated read state. |
refresh() | Reloads the first page. |
send({ text }) | Sends a message and returns the saved message. |
sendState.status | Current send state. |
sendState.error | Last send error, if any. |
Errors
Failed API responses throw ChatApiError.
import { ChatApiError } from "@getuserfeedback/chat";try {await chat.conversations.list();} catch (error) {if (error instanceof ChatApiError) {console.log(error.status, error.code, error.message);}}Common codes:
| Code | Meaning |
|---|---|
missing_auth_jwt | The client has no configured JWT. |
identity_missing | The verified JWT did not include a signed app identity. |
identity_ambiguous | The verified JWT mapped to more than one equally preferred signed app identity. |
invalid_cursor | The pagination cursor is malformed or belongs to a different request. |
not_found | The conversation or referenced message does not exist, is inaccessible, or the message does not belong to that conversation. |
Current limits
- You can start and list conversations, list and send messages, and advance the signed user's read cursor.
- Realtime updates, typing indicators, automatic visibility tracking, and peer-visible read receipts are not exposed in the SDK yet.