Members and moderation
The platform decides who belongs to a chat space. The relay only enforces it: a key that is not on the roster cannot connect. Every change on this page goes through a workflow, and the console's Chat → Members section (Membros) is a front end for the same workflows.
Who becomes a member
| Principal | Joins when | Leaves when |
|---|---|---|
| End user of the app | They open the chat for the first time while their account is active and seated | Their account is disabled or deleted, or they lose their seat, and reconcile runs. Or an admin removes or suspends them |
| Staff actor | You attach it with buzz.actor.attach (see Actors in chat) | You detach it, or its end-user seat stops being eligible and reconcile runs |
People are admitted lazily. An end user with a seat does not appear on the relay until they open the chat, so unused seats cost nothing on the relay.
Invite people
A person needs an account in the identity app before they can open its chat. The console's Invite tab (Convidar) takes a list of emails and, for each one, runs two workflows:
identity.end-user.createwithidentity_app_uuidandemail, skipped when the person already exists in the app;identity.end-user.invitewithidentity_app_uuidandend_user_uuid(oremail), which mails them their access to the app.
When they sign in and open the chat page, they are admitted. From the API:
start_workflow("identity.end-user.create", {"identity_app_uuid": "<app>", "email": "person@example.com"})
start_workflow("identity.end-user.invite", {"identity_app_uuid": "<app>", "end_user_uuid": "<from create>"})
Seats still apply. If the app has no free end-user seat, the person cannot sign in until you add seats. See Billing and seats.
Look at the roster
data.buzz.member.list (org members, read-only) lists every member of a space with:
| Field | Meaning |
|---|---|
status | active, suspended or removed |
relay_state | in_sync, pending_add, pending_role, pending_remove, removed or error |
moderation | The ban or timeout in effect (kind, until, reason), or null |
last_seen_at | When the person last opened the chat |
status_reason_code | Why a member left, for example end_user_not_seated, end_user_disabled, removed, admin_reason |
relay_error_code | A stable code when the last relay change failed, for example relay_unreachable |
data.buzz.member.directory is the view the chat page itself uses: active people and actors with display names and roles, and for actors how to reach them. It returns no platform ids and no emails. End users can call it only for their own app's space, and only after they have opened the chat.
Roles
buzz.member.set-role with space_uuid, member_uuid and role (admin or member) promotes or demotes a member on the relay. A relay admin can manage channels on the relay. Channel roles (owner, admin, member, guest) are set per channel; see Channels.
Display names
Other members see a person's display name. It is resolved on the server and is never derived from an email address:
- a name the person chose, or an admin set;
- otherwise a name from their profile, when one exists;
- otherwise a neutral fallback such as
Member 1a2b(in the space's locale).
A name is 1 to 64 characters, has no control characters and no @, and is not the person's email local-part.
| Who | How |
|---|---|
| The person | "Your name in the chat" on the chat page, which runs buzz.member.set-display-name with display_name |
| An organization admin | buzz.member.admin-set-display-name with space_uuid, member_uuid, display_name. The stored name wins until the person renames themselves |
| An actor | Its name is set when you attach it, not with these workflows |
Moderation
All of these are signed with the space's owner key on the relay, refuse agents, and (except suspend, remove, role and rotate) are for organization admins.
| Want | Workflow | Inputs | Effect |
|---|---|---|---|
| Silence someone for a while | buzz.member.timeout | space_uuid, member_uuid, duration_seconds (60 to 2,592,000), reason | They stay connected but cannot post until it ends. Sessions are kept |
| End a timeout early | buzz.member.clear-timeout | space_uuid, member_uuid, reason | Lifts it. Nothing to lift is not an error |
| Keep someone out | buzz.member.ban | space_uuid, member_uuid, duration_seconds (60 to 31,536,000, omit for permanent), reason | They cannot connect. Their chat sessions are revoked |
| Let them back | buzz.member.unban | space_uuid, member_uuid, reason | Lifts the ban. Revoked sessions stay revoked; they open the chat again |
| Remove one message | buzz.message.moderate-delete | space_uuid, channel_id, event_id, public_reason | Every chat client stops showing it. The optional reason is shown to the channel |
Bans and timeouts show up in data.buzz.member.list under moderation.
Removing someone
| Want | Workflow | Effect |
|---|---|---|
| Suspend, keep the record | buzz.member.set-status with status: "suspended" (and "active" to reinstate) | Off the relay at once. The member row stays |
| Remove | buzz.member.remove with space_uuid, member_uuid, reason | Off the space and the relay. Past messages stay attributed to them |
| Suspected key leak | buzz.member.rotate-key with space_uuid, member_uuid | A new key is admitted, the old one removed, sessions revoked. Old messages stay under the old key |
| Sign-out everywhere | The person signs out on the chat page (buzz.session.revoke) | Revokes the session records only. A key already in a browser keeps working until one of the rows above runs |
Disabling an end user in the identity app, or taking away their seat, does not remove them from the relay on the spot. It happens the next time the space is reconciled.
Maintenance
The console's Maintenance tab (Manutenção) holds the repair workflows.
| Workflow | Use |
|---|---|
buzz.member.reconcile with space_uuid, dry_run, prune_unknown | Suspends members whose seat, account or actor binding lapsed, then brings the relay roster in line with the platform. Safe to repeat. Run with dry_run: true first. prune_unknown (default false) also removes relay members the platform does not know, which matters only for a relay you adopted |
The console's Activity tab (Atividade) shows the actor ledger, covered in Actors in chat.
Ask your AI assistant
Invite ana@example.com and bruno@example.com to my identity app "<app name>" so they can use its chat. Show me the identity.end-user.create and identity.end-user.invite calls before starting them.
List the members of my chat space who are suspended, banned or timed out, with the reason code for each.
Time out the member named "<name>" in my chat space for one hour with the reason "spam". Find their member_uuid with data.buzz.member.list and confirm with me first.
Run buzz.member.reconcile on my chat space as a dry run and explain what it would change.
For AI agents
| Rule | Detail |
|---|---|
| Find the member | data.buzz.member.list returns member_uuid and display_name. Never guess a uuid from a name |
| Human callers | Every buzz.member.* change except the end user's own set-display-name and set-notifications refuses agents. Prepare the call for a person |
| Confirm first | Ban, timeout, remove, rotate-key and moderate-delete affect a real person. Say what will happen and wait |
| Honest revocation | Do not tell a user that signing out removed access. Removal is buzz.member.remove or buzz.member.set-status |
| Dry run | buzz.member.reconcile with dry_run: true before a real run |
Theme and customization
Customize a chat space live. The theme document carries brand, colors, layout, feature flags, attachment limits and copy, and moves through validate, draft, publish and rollback
Channels
Organization-managed channels, channels your members create and run when the space allows it, private channels, and archiving
