Skip to content

Join HTTP endpoint

POST /join lets a device obtain a participant token directly, for deployments that do not (yet) have a backend of their own. A real application usually mints tokens from its own server with MintClientToken instead — that is the only way to tie the token to a signed-in user.

Method / path POST /join
Host Your deployment’s join host, e.g. https://join.example.moqom.cloud
Auth Authorization: Bearer <device credential>
Body JSON, at most 4 KiB
Rate limit 30 requests per minute per credential
Token lifetime 15 minutes

The endpoint deliberately narrows what a device can get:

  • No moderation capabilities and no ownership, whatever the credential could otherwise do.
  • Publish rights are the credential’s own, never anything named in the request.
  • A credential with no publish rights is refused (403) rather than issued a token that can publish nothing. A tenant’s owner-level backend key is in this category — do not ship it to devices.
  • Relay keys are refused.
{
"tenant": "t_3f9a0c1d2e4b5a69",
"username": "alice",
"room": "demo",
"key": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" }
}
Field Required Meaning
tenant no Defaults to the credential’s tenant
username yes The name to join as (normalised server-side)
room no The room to join
key one of key / device The device’s public key as a JWK. The SDK sends this for you (TokenService).
device one of key / device Legacy: a device thumbprint. If both are sent they must agree, or the request is refused.

Depending on deployment configuration, a room that does not exist yet is either created (LIVE, default policy) or refused with 404.

Terminal window
curl -sS https://join.example.moqom.cloud/join \
-H "Authorization: Bearer $DEVICE_CREDENTIAL" \
-H 'Content-Type: application/json' \
-d '{"username":"alice","room":"demo","key":{"kty":"EC","crv":"P-256","x":"…","y":"…"}}'

200 OK (Cache-Control: no-store)

{
"token": "eyJhbGciOiJFZERTQSIs…",
"expiresAt": "2026-10-06T09:35:00Z",
"room": "demo",
"username": "alice",
"keyId": "k-2026-10",
"device": "Xq3v…full-thumbprint…"
}
Field Meaning
token Pass to the client SDK as the session credential
expiresAt When it stops being accepted — schedule renewal from this, never by decoding the token
username The name actually minted, after normalisation. Display this one.
keyId Signing key ID
device The device thumbprint the token was bound to. Compare with the device’s own.

Note the camelCase field names: this endpoint is consumed directly by devices.

Error bodies are plain text explaining the cause.

Status Cause Client should
400 Unreadable body; missing key/device; mismatched key and device; invalid username Fix the request
401 Missing or unknown credential, or a relay credential Re-provision the credential
402 Tenant has no credit (never paid, or balance spent) Tell the user the service is unavailable; the tenant must top up
403 Credential has no publish rights, or lacks permission Stop — the credential will not do
404 Room not found Show “room not found”
405 Not a POST —
409 Room has ended, or another precondition failed Do not retry blindly
429 Rate limited Retry shortly
503 / 504 Temporarily unavailable / timed out Retry with backoff
500 Internal error Retry later; report if persistent