Join tokens
Your backend mints a join token for each person, and your app joins with it. The token decides who they are and what they may do. Our servers enforce it, so a modified app can't give itself more.
Minting a token
curl -X POST https://api.appsocom.dev/tokens \
-H "Authorization: Bearer $APPSOCOM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"roomId":"class-1","userId":"u_7","name":"Ada","role":"student"}'
# → {"token":"eyJ…","expiresAt":1763000000}| Field | |
|---|---|
roomId, userId |
Required. Letters, numbers and . _ : @ -, up to 128 characters. |
name |
Display name. Defaults to userId. |
role |
"teacher", "student", "observer", or your own label. See below. |
permissions |
Override single permissions: { "mic", "camera", "screen", "moderate" } as booleans. |
canPublish |
false makes a view-only participant, such as a webinar audience member. Ignored for the built-in roles. |
metadata |
App data everyone in the room can see but the participant can't change. Up to 4 KB as JSON. |
ttlSeconds |
How long the token works. Default 7200 (2 hours), from 60 to 86400. |
maxParticipants |
Room size limit when this token joins, from 2 to 10000. Use 2 for one-on-one calls. |
e2ee |
true means this person must use end-to-end encryption (web only). |
Mint tokens only after you've checked who the user is and that they may join that room. Mint a fresh token for each join, rather than storing them.
Call types
All calls use the same room API. Pick the call type with the token and the joinRoom options.
| Call | Token | Frontend |
|---|---|---|
| One-on-one video | maxParticipants: 2 |
joinRoom({ url, token }) |
| One-on-one voice | maxParticipants: 2 |
joinRoom({ url, token, mode: "audio" }) |
| Group video | optionally a limit, e.g. maxParticipants: 12 |
joinRoom({ url, token }) |
| Group voice | joinRoom({ url, token, mode: "audio" }) |
|
| Webinar | speakers as usual; audience with canPublish: false |
the same joinRoom |
Roles and permissions
| Role | mic | camera | screen | moderate |
|---|---|---|---|---|
teacher |
✓ | ✓ | ✓ | ✓ |
student |
✓ | ✓ | – | – |
observer |
– | – | – | – |
permissionsoverrides single values. For example,{"role":"student","permissions":{"screen":true}}is a student who may present.- Any other role name (up to 32 letters, numbers and
. _ -) is a label your UI can show. Its permissions come fromcanPublish. - Without a role, everyone may publish (
participant), or nobody may ifcanPublishisfalse(viewer). moderatelets someone mute others, remove people, admit from the waiting room, run polls and start recordings. See Classrooms.
You can change a role or permissions during the call from your backend:
curl -X PATCH https://api.appsocom.dev/rooms/class-1/participants/u_7 \
-H "Authorization: Bearer $APPSOCOM_API_KEY" -H "Content-Type: application/json" \
-d '{"role":"teacher"}'Long calls
Tokens expire after ttlSeconds, and a participant whose token has expired is removed from the room (the left event's reason is unauthorized, and webhooks report token-expired). Pick a ttlSeconds longer than your calls, or refresh the token. The client fires token-expiring a minute before expiry. Fetch a new token and hand it over:
room.on("token-expiring", async () => room.updateToken(await fetchNewToken()));Limits
Your project's plan sets a room size limit and monthly allowances. When a limit is reached, new tokens are refused with 429, and people already in a call aren't cut off. See Pricing.