The RWI in Practice: One Socket for Events and Control

M
Miuda Team
Building conversational AI tooling

The RWI (Real-time WebSocket Interface) is RustPBX’s control plane for external software: a command/event protocol over a single WebSocket that a CTI panel, CRM, or AI agent can hold open for the life of a shift. This is the practical guide — what the protocol looks like, what arrives on it, and the details that matter in production.

Addons console — where RWI-powered integrations plug in

Why One Socket

The alternative architecture — REST polling + a separate webhook receiver + a database for state — has three failure modes: latency, duplication, and drift. A single duplex socket solves all three: commands go out, events come back on the same ordered channel, and there’s no polling loop to tune.

Events: What Arrives

The event bus mirrors the platform. Broad categories:

CategoryExamples
Call lifecyclecall_created, call_ringing, call_answered, call_hangup, call_error
Call detailDTMF, hold/resume, transfer, recording state, ivr step trace
Queue / ACDenqueue, agent_connected, overflow_joined, dequeue
Agentstate transitions, wrap-up start/end, break changes
Session datauser-data changes, session var updates

Two protocol qualities make these usable:

  • Root call identity on every call-scoped event. Every event carries the root session id, so a panel can group child legs (queue dispatch, transfer) into one logical call without guessing.
  • Attribution on every event. src_ip / client_ip are stamped, which is what lets a multi-tenant CTI panel decide what a given operator is allowed to see.

Commands: What You Can Do

Commands mirror the platform’s capabilities:

  • Call control — originate, answer, hangup, bridge, transfer (blind/attended), hold/resume, mute
  • Media — play prompts (including side_only), send DTMF (rfc4733 or SIP INFO), stop an app
  • Queue operations — enqueue/dequeue, agent assignment, priority updates
  • Conference control — create rooms, add/remove participants
  • Session data — set/get user data and call variables

A call-control command’s REST twin exists for scripted use: POST /api/calls/active/{id}/commands.

Cluster Behavior: Owner-Routed

In a cluster, the socket you’re connected to may not own the call. That’s handled for you: commands are forwarded to the owning node (/cluster/session_op under the hood), and the terminal node applies them locally. From the client’s perspective, “transfer that call” works from any node, and the response/events come back on the same socket.

Session Data That Survives

Session user data set over RWI replicates to peers and is inherited by transfer sub-sessions — so the CRM id your panel attached at answer is still there after two transfers and a node failover.

Delivery Guarantees for Webhooks

Not every consumer wants a socket. The webhook runtime bridges the same events over HTTP with:

  • Dedicated worker threads (a slow receiver never stalls calls)
  • Retries with exponential backoff for 5xx/429/transport errors
  • An idempotency key that stays identical across retry attempts
  • Optional queue-latency metrics and hot-reload of the webhook config

Socket for interactive control, webhook for fire-and-forget consumers — same events, appropriate transport.

Practical Integration Pattern

  1. Connect the socket, subscribe to the event classes you need
  2. Keep a local map keyed by root session id
  3. Attach CRM ids via user-data on answer
  4. Drive control from the same socket; use REST for out-of-band scripting
  5. Route non-interactive consumers to webhooks

Guides: RWI Events & Webhooks and Extensibility.

Get new posts by email

Deep dives on Rust telephony, contact centers and wholesale voice. No spam.

Thanks — you're on the list.

We use cookies for anonymous analytics to improve the site. Nothing is loaded until you accept. Privacy Policy