Skip to content

Control-plane gRPC API

The control plane’s admin API is gRPC, package moqom.admin.v1. The Go and Rust SDKs wrap it; you can also generate a client from the protos in any language gRPC supports.

Endpoint api.moqom.cloud:443 (TLS)
Auth gRPC metadata authorization: Bearer mqk_… with a backend key
Scope Every request names a tenant; it must be the key’s tenant
Services Rooms, Moderation, Tokens, Events

Relay keys are refused by every admin service (PERMISSION_DENIED).

Message / enum Key fields Notes
Identity username, device Username is your string (NFC, ≤ 4096 UTF-8 bytes). Device is derived from the client’s device-key thumbprint.
ParticipantRef username, device Empty device targets every device of the user.
Grants publish[], moderation[], is_owner Publish scopes and moderation capabilities are independent sets.
PublishScope AUDIO, VIDEO, SCREEN_SHARE, DATA Permission, never an action.
Capability chat 1–5, stage 10–16, room 20–25, meta 30–32 A set, never a level. See Concepts.
CapabilityPreset NONE, MODERATOR, SUPER_MODERATOR, OWNER Expanded server-side; the resulting set is what is stored.
MediaKind AUDIO, VIDEO, SCREEN_SHARE
Presence CONNECTED, RECONNECTING, AWAY, BACKGROUNDED, LEFT
ModerationCode ADULT_CONTENT, VIOLENCE, HARASSMENT, SELF_HARM, COPYRIGHT, AGE_VERIFICATION_REQUIRED, TENANT_POLICY, OPERATOR_ACTION Sent to the affected client.
ActorContext on_behalf_of, reason, idempotency_key The human the backend acts for. The service account is taken from credentials, never from the body.
Timecode at, uncertainty_ns, trust An instant with its error bar.
RPC Request → Response Purpose
CreateRoom CreateRoomRequest → Room Create a room. room is your own name for it. Optional owner_username, policy, starts_at (creates it SCHEDULED).
GetRoom GetRoomRequest → Room Read one room.
ListRooms ListRoomsRequest → ListRoomsResponse Page through rooms, optionally filtered by lifecycle[].
UpdateRoomPolicy UpdateRoomPolicyRequest → Room Change policy on a live room. Use update_mask; absent means all fields. Tightening applies immediately to everyone. residency_zone is immutable.
CloseRoom CloseRoomRequest → Room End the room for everyone with a ModerationCode. Idempotent.
ListParticipants ListParticipantsRequest → ListParticipantsResponse Who is in the room. include_backstage, include_departed. Paged.
GetParticipant GetParticipantRequest → Participant One device.
AttachWatchdog AttachWatchdogRequest → Watchdog Attach a recorder, safety, analytics, compliance or custom observer.
DetachWatchdog DetachWatchdogRequest → Watchdog
ListWatchdogs ListWatchdogsRequest → ListWatchdogsResponse
Field Meaning
tenant, room Identity
namespace The room’s MOQT namespace prefix, rendered slash-separated. Use it rather than constructing one.
policy RoomPolicy (below)
lifecycle SCHEDULED, LIVE, ACTING_HOST, ENDED
starts_at, created_at, ended_at Timestamps
acting_host Set while ACTING_HOST
participant_count / distinct_user_count Devices / people in the room
is_recording A recording watchdog is attached
audiences[] {via, viewers, resolution} — how the audience divides between the cohosts who brought it
Field Meaning
age_gate {minimum_age, require_verification} — with verification required, tokens must be minted with age_verified
recording ALLOWED, REQUIRED (cannot start without a recorder), PROHIBITED
residency_zone Where the room’s data (including derived data) may live. Immutable.
max_participants Device limit; 0 = unlimited
stage_slots Simultaneous video publishers (default 8)
watchdog_limit Watchdogs that may attach (default 8)

identity, grants, presence, self_muted[], forced_muted_for_everyone[], forced_muted_for_audience[], is_spotlighted, is_backstage, joined_at, relay_id, arrived_via (the cohost whose following brought them, from the token), locale (BCP 47).

id, kind (RECORDING, SAFETY, ANALYTICS, COMPLIANCE, CUSTOM), custom_kind, operated_by (PLATFORM, TENANT, THIRD_PARTY), operator_name, descriptor (shown to participants), scope (namespace prefix it may observe; empty = whole room), health (ATTACHED, DEGRADED, FAILED), attached_at, is_billable, pinned_layer.

Every RPC returns the ModerationEntry it produced. See the Moderation guide.

RPC Key request fields Purpose
Kick room, target, code, actor Remove now; may rejoin.
Ban room (for ROOM scope), scope (ROOM / TENANT), subject (username / device / ip_prefix), duration (unset = permanent), code, actor End current sessions and refuse future ones.
Unban room, scope, subject, actor Lift a ban early.
ForceMute room, target, media[] (empty = all), scope (EVERYONE / AUDIENCE), duration, actor Stop media without the client’s cooperation.
Unmute room, target, media[], scope, actor Lift a force-mute of the given scope.
SetGrants room, target, grants (complete result, not a delta), preset, actor Change rights. Demotions apply immediately.
Spotlight room, target (unset = clear), actor
ListModerationLog room (empty = tenant-wide), since, until, target, verb[], paging Audit trail. Requires CAPABILITY_READ_MODERATION_LOG.
Field Meaning
id, tenant, room, at
verb KICK, BAN, UNBAN, FORCE_MUTE, UNMUTE, SET_GRANTS, SPOTLIGHT, CLOSE_ROOM, ATTACH_WATCHDOG, DETACH_WATCHDOG, UPDATE_ROOM_POLICY
service_account Filled by the server from the credentials
on_behalf_of, reason, code From the request
target / ban_subject Who it was done to
expires_at When the effect lapses; unset for permanent or instantaneous actions
media, mute_scope, ban_scope, grants The verb’s parameters
enforced true once live sessions have been acted on; false while enforcement is still completing
decided_by_model Set when a classifier, not a person, decided

A repeated request with the same actor.idempotency_key returns the original entry unchanged.

RPC Purpose
MintClientToken Issue the short-lived token a client joins with.
ListVerificationKeys The public keys relays and clients verify tokens with. Rotated without deploys.
Field Meaning
tenant Your tenant
username The user this token speaks for
device_thumbprint Required. The client’s device-key thumbprint, as reported by the client SDK
room The room. It must exist and be open. Empty = discovery surface, no room
grants / preset What they may do. A service account can never mint more than its own ceiling
lifetime Default 10 minutes, maximum 15 minutes
backstage Join backstage; going live later needs no new token
age_verified Assert verification for age-gated rooms
arrived_via The cohost whose following brought this user (requires room, cannot be the user themselves)

token (compact form, passed by the client at session setup), expires_at, identity (with the device already derived), key_id (matches a VerificationKey.key_id).

Failure modes:

Code Cause
INVALID_ARGUMENT Missing device_thumbprint, bad username, lifetime above 15 minutes
PERMISSION_DENIED Asking for capabilities, ownership or publish rights the key cannot grant
NOT_FOUND The room does not exist
FAILED_PRECONDITION No credit (payment required / insufficient credit), room ended, or room requires age verification

keys[] — {key_id, algorithm ("EdDSA"), jwk, not_before, retires_at} — and binding, the token binding this deployment issues.

RPC Purpose
StreamRoomEvents Server stream of RoomEvent for one room (room set) or the whole tenant (room empty).

Request: tenant, room, after_sequence (resume point; 0 = now), kind[] (filter).

RoomEvent: tenant, room, sequence (contiguous per room), at, timecode, kind, and one body: participant_joined, participant_left, presence_changed, grants_changed, mute_changed, spotlight_changed, publish_changed, watchdog_changed, lifecycle_changed, moderation_applied. See Events.

gRPC code Typical cause Retry?
UNAUTHENTICATED No, malformed or unknown key No
PERMISSION_DENIED Wrong tenant, relay key, or insufficient capability/ceiling No
INVALID_ARGUMENT Malformed request; immutable policy field No
NOT_FOUND No such room/participant/watchdog (also for other tenants’ resources) No
ALREADY_EXISTS Room already exists No
FAILED_PRECONDITION Room closed; age verification required; no credit (MintClientToken) After fixing the cause
UNAVAILABLE, DEADLINE_EXCEEDED Transient Yes, with the same idempotency key
INTERNAL MOQOM bug Report it