Public chat and embed
These routes support anonymous public chat and website embed sessions. They do not use the workspace API token — a browser or your own backend calls them directly against your Radioso host (https://api.radioso.ai, https://api-us.radioso.ai, or your self-hosted origin) using the public launch token embedded in your site, not a workspace credential.
How it works
- Enable public chat or website embed in general settings.
- Create a public chat session from the launch token.
- Send the returned
publicSessionTokenasx-radioso-public-sessionon chat and history requests. - Store the returned
resumeTokenif the visitor should resume the same history after the public session expires. - Preserve the server-set anonymous session cookie across requests when available.
The launch token identifies the public surface, and the public session identifies the visitor. The resume token is the only supported way to request the same visitor session in a later session exchange.
Create a public chat session
Use this route before sending chat messages:
POST /api/v1/public/chat/{token}/sessionsFor a public link:
{
"channel": "anonymous_link"
}For an Enterprise website embed, call the route from an origin admitted by the website embed origin setting:
{
"channel": "website_embed"
}Browser clients must keep using that same origin on later chat, history, and
feedback requests. Radioso checks the signed public session against the current
website embed origin setting each time. Removing an origin from the list stops
existing browser sessions from sending new messages. If the list contains *,
any origin is admitted. If the list is empty, no origin is admitted.
To resume an existing visitor session, include the previous response’s resumeToken:
{
"channel": "website_embed",
"resumeToken": "previous-resume-token"
}The response includes publicChatToken, publicSessionId, publicSessionToken, resumeToken, workspaceName, assistantBootstrapActive, expiresAt, and resumeExpiresAt. Internally, publicSessionId is the chat session boundary for conversation continuity.
chatSessionId and anonymousSessionId are accepted as client hints but are not trusted as resume credentials. Use resumeToken to resume and the returned publicSessionToken for chat requests.
Send a public chat message
{
"message": "What can this workspace answer?",
"stream": false,
"conversationId": "optional-conversation-id",
"bootstrapGreetingId": "optional-bootstrap-greeting-id"
}The public chat request also supports startConversation: true for bootstrap greeting requests, userExpectedLocale, and inputMetadata.
Bootstrap greetings are not saved as conversations by themselves. A startConversation: true response can omit conversationId and include bootstrapGreetingId. If the visitor replies, send that bootstrapGreetingId with the first normal message and no conversationId; Radioso will save the displayed greeting before the user’s message. Use the conversationId returned by that first normal message for history and follow-up continuity.
Sending a newer message while a turn is running
If a newer message arrives for the same conversation before the current assistant
reply starts, Radioso cancels the current reply and answers from the latest
history. A non-streaming superseded request returns HTTP 409 with error code
chat_turn_superseded.
For a streaming request, the superseded stream ends with this terminal event:
event: cancelled
data: {"conversationId":"...","reason":"superseded","stage":"rendering"}Treat cancelled as terminal and clear the pending reply state. It contains no
assistant copy. Before an answer or cancellation, the stream can emit status
with stage interpreting, searching, or composing. Status is transient UI
state and does not prevent cancellation. If the first assistant chunk has already streamed, the first turn
finishes and the newer message runs next.
Answer chunks are incremental delivery. Guarded or durability-sensitive replies may be validated or committed before their first chunk, then replayed in bounded chunks. Clients should not assume every chunk is a live provider token.
If the assistant cannot produce a public response body, the route returns a
JSON error instead of an empty 204 response:
{
"error": {
"code": "service_unavailable",
"message": "Public chat response is unavailable.",
"details": {
"code": "public_chat_empty_response"
}
}
}Chat suggestions
A chat response can include suggestions. Each suggestion has text, a
provider-defined kind string, and an optional action.
Two kinds of chips can appear together:
- Follow-up question chips have no
action(oraction.kindis"ask_followup"). They are pure text prompts: rendersuggestion.textand, when the visitor selects one, send it as a new user message withinputMetadata.method = "suggestion_click"andsuggestionSourceMessageId = <assistant message id>. - Action chips carry
action.kind === "start_intent"with anintent.skillName(and an optionalintent.intentName). They route to a specific assistant skill instead of asking a follow-up question. When the visitor activates one, sendinputMetadata.method = "intent_click"together with theintentobject verbatim, plussuggestionSourceMessageId. Sendingsuggestion_clickfor an action chip will work as a plain text turn but will bypass the structured skill handoff the chip was meant to trigger.
At most one action chip is returned per assistant turn. Action chips are in addition to the up-to-two follow-up question chips.
Example response fragment:
{
"suggestions": [
{
"text": "Contact us",
"kind": "contact_human",
"action": {
"kind": "start_intent",
"intent": {
"skillName": "human_contact.request",
"intentName": "no_context_refusal"
}
}
},
{ "text": "What's covered here?", "kind": "deeper" }
]
}The set of action kind values is open: new modules can register additional
providers, so clients should treat kind as an opaque string and decide
behavior from action rather than from kind.
History in the anonymous flow
Use GET /api/v1/public/chat/{token} to list conversations for the current anonymous session.
Use GET /api/v1/public/chat/{token}/history/{conversationId} to fetch one conversation from that same anonymous session.
Use GET /api/v1/public/chat/{token}/tail/{conversationId} to read messages created after a cursor. A conversation detail response includes tailCursor; send that cursor to receive only later visitor, AI, or human-agent messages. If cursor is omitted, tail returns the newest bounded page and a cursor for the newest returned message. Public tail responses do not include operator ownership metadata.
Use GET /api/v1/public/chat/{token}/events/{conversationId} to open a server-sent event stream for the same anonymous session. The stream emits ready after subscription and message.created when a new message is available. Browser clients that store the public session in local storage should consume this stream with fetch, not native EventSource, so they can send x-radioso-public-session.
Endpoint reference
Session, message, and embed-config payloads are typed in full in the API Reference.
POST /api/v1/public/chat/{token}/sessions
GET /api/v1/public/chat/{token}
POST /api/v1/public/chat/{token}
GET /api/v1/public/chat/{token}/history/{conversationId}
GET /api/v1/public/chat/{token}/tail/{conversationId}
GET /api/v1/public/chat/{token}/events/{conversationId}
GET /api/v1/public/chat/{token}/embed-config
GET /api/v1/public/chat/{token}/assistant-logoCommon failure modes
403on a website embed session usually means the origin is not admitted by the website embed origin setting.404usually means the public token is unknown or not active.409withchat_turn_supersededmeans a newer message replaced this turn before its reply started.503on a public chat message means Radioso accepted the public session but could not produce a response body.429on public chat means the optional server-side public chat rate limit has been reached.- If history appears empty, the browser or cookie jar is probably not preserving the HttpOnly anonymous session cookie.