Build Your OmniagentTools

WebSocket Tools

The session-long connection between the platform and your functions server — initialize, metadata, the live conversation feed, and pushing messages back

An explicit tool with a wss:// URL gets more than fast tool calls: the platform opens a WebSocket to your functions server when the session starts and keeps it open for the whole session. This page covers everything that connection can do beyond answering tool calls.

The initialize message

When the WebSocket connection opens at the start of a session, the platform sends an initialize message. It identifies the session, lists the WebSocket-based tools attached to it, and carries the session's metadata — so your server can set up any per-session state before the first tool call arrives.

{
  "type": "initialize",
  "data": {
    "session_id": "sess_xyz789",
    "functions": [
      {
        "name": "get_order_status",
        "flow": "explicit"
      }
    ],
    "metadata": {
      "tags": {
        "key": "value"
      },
      "external_client_id": "client_id",
      "external_client_profile": {
        "key": "value"
      }
    }
  }
}

Session metadata

The metadata object is always included in the initialize message. It carries the context you passed when you created the connection — so your server can tie the session to your own user, tenant, or profile without a separate lookup.

The three values come straight from the connection-creation request. Note the WebSocket message uses snake_case, while the connection request uses camelCase:

metadata field (WebSocket)Connection request field
tagstags
external_client_idexternalClientId
external_client_profileexternalClientProfile

Fields you did not set at connection creation are simply absent from metadata — no per-tool opt-in is required.

Connection behavior

The platform opens the socket to your server at session start — and your server might be slow to accept, or down. The tool's connectionBehavior setting controls what the session does in each case. Set it when you create the tool (POST /public/functions), or update an existing tool with PUT /public/functions/{functionId}:

{
  "connectionBehavior": {
    "waitBeforeStart": false,
    "abortOnFailure": false
  }
}
Flagtruefalse (default)
waitBeforeStartThe session waits for your socket to be fully connected before starting.The session starts after a short wait and lets your tool join late.
abortOnFailureThe whole session is aborted if your socket fails to connect.The session starts anyway and the client is notified of the failure.

Use waitBeforeStart: true when the agent's first responses depend on your tool being reachable; use abortOnFailure: true when a session without your tool is worthless. For everything else, the defaults favor fast session starts.

The defaults changed. Previously the platform always behaved as if both flags were true — every session waited for your socket and died if it failed to connect. Sessions now start without waiting and survive a failed tool connection unless you opt back in with { "waitBeforeStart": true, "abortOnFailure": true }.

To see how each tool's connection actually went — connected when, failed why, canceled by the user leaving — read the session's functionMetrics.

receiveMessages

Set receiveMessages to true on a tool and the conversation between the user and the agent is streamed to your WebSocket endpoint in real time as it happens — not just when the tool is called.

Because the WebSocket connection is open from the start of the session, enabling receiveMessages gives your server a live feed of the full exchange. By the time the agent invokes your tool, your server already has the complete conversation context and can provide a more informed response.

This is particularly useful for tools that need to understand the broader conversation — not just the specific parameters the agent populated for the tool call.

Pushing UI state to the client (ui_update)

Sometimes the conversation changes something the page should reflect, and the change happens on your backend — where the client can't see it. For example:

  • The user books a table through the agent — your tool handler completes the booking, and the page should show a confirmation card with the details, not just have the agent say them out loud.
  • A tool kicks off a long-running job — a report, an export, a search — and you want the page to show live progress while the agent keeps talking.
  • The agent updates the user's cart, profile, or dashboard through a tool — the on-screen state should update the moment it happens.

ui_update is the channel for exactly this: your server pushes a message to the end user's client through the tool connection, and the platform relays it over whichever session channel is in use (the WebRTC data channel, or the client WebSocket). The payload is not validated or modified — it arrives exactly as you sent it, and your client renders it however it wants.

Send from your functions server — for example from an explicit tool handler after completing an action:

{
  "type": "ui_update",
  "data": { "view": "booking-confirmation", "bookingId": "bk_123" }
}

The client receives it as a server event:

{
  "event": "ui_update",
  "data": { "view": "booking-confirmation", "bookingId": "bk_123" }
}

data can be any JSON, and the message is not tied to a tool call — send it whenever you want while your WebSocket is connected.

ui_update is one-way and fire-and-forget: no acknowledgement, and updates are not stored — a client that connects mid-session won't receive earlier updates.

Updating the conversation context (context_update)

context_update lets your functions server add information to the conversation itself. Where ui_update targets the page, context_update is injected into the session's context — the agent reads it as if it were part of the conversation, and can act on it or respond to it. It's the server-side counterpart of the client's send_message command, and like everything on this page, it works regardless of the session channel — WebRTC included.

The canonical use case is the deferred tool result. Tool calls have a 10-second timeout — but some tools do real work that takes longer: generating a report, calling a slow third-party API, waiting on a human approval. You can't keep the agent (and the user) hanging while it runs:

  1. The agent calls your tool. Your handler returns an interim result right away — for example "Started. Tell the user the result will be ready shortly." — so the conversation isn't blocked.
  2. Your server does the slow work.
  3. When it completes, your server sends context_update with the outcome — the agent picks it up and tells the user.
{
  "type": "context_update",
  "data": {
    "previous_item_id": null,
    "role": "system",
    "trigger_response": false,
    "content": "The export the user asked for has finished. 1,204 rows, download link sent by email."
  }
}
FieldTypeDescription
contentstringThe text to inject into the conversation context.
rolestringWho the injected message is from, e.g. "system".
trigger_responsebooleantrue — the agent responds to the injection immediately. false — the context is added silently and informs the agent from its next turn.
previous_item_idstring | nullThread position for the injected message; null appends at the end of the conversation.

For a deferred result you'll usually want trigger_response: true, so the agent announces the outcome the moment it lands; with false the agent simply knows, and works it into the conversation naturally when the user next speaks.

context_update isn't limited to tool results, though — it can be sent at any time while the WebSocket is connected and doesn't have to follow a tool call. Use it whenever your backend knows something the agent should too: fresh instructions, changed account state, an event that happened outside the conversation.

Next steps

On this page