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).
Credit and the admin API
Only calls that admit media are gated on credit. A tenant without credit gets
FAILED_PRECONDITION from MintClientToken (and 402 from the
Join endpoint ). Moderation, rooms and events always keep
working , so you can still remove someone from a room when you are out of credit.
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