Realtime WebSocket messaging

Real-time messaging API for the Freeday webchat, powered by Centrifugo. The typed message payloads are in the Message schemas reference; this page covers how the protocol works.

Quick Start

Service URL
REST API https://api.freeday.ai
WebSocket wss://realtime.freeday.ai

All REST calls require X-Session-Id (client-generated UUID) and X-Company (from Settings → Company in the Freeday platform) headers.

Pass X-Channel-Id as well to keep each channel's conversation history separate: when it is set, listing conversations and reading their messages is limited to the digital employees routed from that channel, so a conversation started in one widget or app does not surface in another.

  1. Create a conversationPOST /sdk/v1/conversations
  2. Get a connection tokenPOST /sdk/v1/auth/connect-token
  3. Connect — Open a WebSocket to wss://realtime.freeday.ai/connection/websocket with the token. See the Centrifugo client SDK docs.
  4. Get a subscription tokenPOST /sdk/v1/auth/subscribe-token
  5. Subscribe — Subscribe to the channel from step 4 with a getToken callback.

Example (centrifuge-js):

const sub = client.newSubscription(channel, {
  getToken: async () => {
    const res = await fetch("https://api.freeday.ai/sdk/v1/auth/subscribe-token", {
      method: "POST",
      headers: { "Content-Type": "application/json", "X-Session-Id": sessionId, "X-Company": companyId },
      body: JSON.stringify({ conversation_id: conversationId }),
    });
    return (await res.json()).subscription_token;
  },
});
sub.on("publication", (ctx) => handleEnvelope(ctx.data));
sub.subscribe();

Authentication

JWT tokens are used for both connection and channel subscription authentication:

  • Connection token — obtained via POST /sdk/v1/auth/connect-token (15 min expiry). See the REST API reference.
  • Subscription token — obtained via POST /sdk/v1/auth/subscribe-token (24 hour expiry). See the REST API reference.

Conversation channel

Channel address: messages:companies.{companyId}.conversations.{conversationId}

  • companyId — UUID of the company
  • conversationId — UUID of the conversation

Clients subscribe to receive messages from the digital employee, agents, and system, and publish to send user messages.

History: 100 messages are retained for 1 hour. See the Centrifugo history documentation for retrieval details.

Message flow

Sending: Publish a SendMessage via subscription.publish().

Receiving: The server streams MessageEnvelope objects. Each envelope carries a sequence number — discard out-of-order envelopes, and ignore envelopes with an unrecognized proto version. A message streams in via replace → … → complete.

Streaming example:

{ "operation": "replace",  "sequence": 1, "message_snapshot": { "status": "partial",  "blocks": [{ "id": "b1", "type": "typing",   "typing": true }] } }
{ "operation": "replace",  "sequence": 2, "message_snapshot": { "status": "partial",  "blocks": [{ "id": "b2", "type": "markdown", "content": "Hello! I can" }] } }
{ "operation": "complete", "sequence": 3, "message_snapshot": { "status": "complete", "blocks": [{ "id": "b2", "type": "markdown", "content": "Hello! I can help you with that." }] } }

Message schemas

The full typed payloads (MessageEnvelope, Message, SendMessage, block types, …) are in the Message schemas reference nested under this section. The machine-readable AsyncAPI 3.1 spec is available in the repo at docs/async-api/webchat-sdk-async.yaml.