Documentation
Sign inGet started

Client events

A client event travels directly from one subscribed client to the others on the same channel. Your backend receives a copy only if you subscribe it to the client-events webhook group.
Use client events for short-lived signals such as typing indicators, cursor positions, or activity heartbeats. They avoid a round trip through your API.
Do not use client events as authoritative state. The Realtime edge does not validate their payloads, so recipients cannot trust their contents. Send stored chat messages, state changes, and permission-sensitive actions through your server with Publishing events.

Enable client events

Enable Client Events for the app on the Realtime apps page. You can also set client_events to true through the Realtime API. The setting applies to the whole app. Until you enable it, the Realtime edge rejects client events.

Client event requirements

The Realtime edge enforces three rules:
  • The name starts with client-. This reserved prefix identifies client events. Clients reject event names that omit it.
  • The channel is private or presence. A public channel is refused, and that is the point: the app key ships in your page, so anyone could subscribe to a public channel and start writing to it. Authorization is what makes a client trustworthy enough to broadcast, and only private and presence channels have it.
  • The sender is subscribed. The connection has to already be on the channel it triggers into, so a client cannot write to a room it was never admitted to.
If an event breaks one of these rules, the edge returns a connection-level error. Bind the error while developing:
bird.connection.bind("error", (e) => console.warn("edge refused:", e.message));

Sending

import { BirdRealtime } from "@messagebird/realtime";

const bird = new BirdRealtime({
  appKey: "your-app-key",
  region: "us1",
  authEndpoint: "/bird/auth",
});

const room = bird.subscribe("presence-room-1");

input.addEventListener("input", () => {
  room.trigger("client-typing", { at: Date.now() });
});
The optional payload can be a string, object, or array. trigger returns true after sending the frame and false if the channel is not subscribed. To send immediately after joining, wait for bird:subscription_succeeded.
Swift and Kotlin take the payload as their language's JSON value: Any? encoded with JSONSerialization in Swift, JsonElement in Kotlin.

Receiving

Bind the name you sent, on the same channel, exactly like a server-published event:
room.bind("client-typing", (data) => showTypingIndicator(data));
The sending connection does not receive its event. Other connections that belong to the same person do receive it, so filter those copies when needed.

Rate limits

Each connection can send up to 10 client events per second. The same limit applies to every connection on the app.
When a connection exceeds the limit, the edge drops the event and reports an error without closing the connection. Bind the connection's error event and throttle high-frequency inputs such as cursor movement.

Receive client events on your server

To receive client events on your server, subscribe an endpoint to the realtime.client_events group. Each webhook's type adds the realtime. prefix to the client event name, so client-typing becomes realtime.client-typing:
Codevoorbeeld
{
  "data": {
    "channel_name": "presence-room-1",
    "event": "client-typing",
    "data": "{\"at\":1785495600000}",
    "connection_id": "26896.319537",
    "member_id": "u_42"
  },
  "timestamp": "2026-07-31T09:00:00Z",
  "type": "realtime.client-typing"
}
member_id appears for presence channels and is absent for private channels. High-volume signals such as cursor positions produce one webhook per client event. See Realtime webhooks for the envelope, signature, and other groups.

Next steps

  • Realtime webhooks covers receiving client events, and channel activity, on your own endpoint.
  • Presence channels give client events a member identity, and the member list to render them against.
  • Publishing events is the server-side path, for everything a client should not be trusted with.