Documentation menu
Tokens & auth
Your token server does not change. The gateway verifies real Agora tokens against the app certificate, so whatever library you already mint with keeps working — you only change which credentials it signs.
Supported formats
| Format | Prefix | Typical minter |
|---|---|---|
| AccessToken2 | 007… | agora-token (Node), agora_token_builder (Python), Agora's Go builder |
| AccessToken v1 (legacy) | 006… | agora-access-token and other older libraries |
Both are verified with the same HMAC construction Agora uses, so a token that Agora would accept is one VoxaRTC accepts, and one it would reject is rejected with the matching Agora error code.
What happens on join
- Your app calls
joinChannel(token:, channelId:, uid:). - The SDK presents the token to your VoxaRTC endpoint along with the channel name and uid.
- The endpoint reads the App ID out of the token, looks up that project's certificate, and verifies the signature, the channel name, the uid and the expiry.
- On success it issues a short-lived credential for that channel and identity.
- The SDK streams directly to the media server. The endpoint is not in the media path.
Channels are namespaced per project, so two projects using the channel name lobby never collide.
uid handling
A uid of 0 means "let the server assign", exactly as in Agora. The gateway treats it as the wildcard form, so a token minted for uid 0 is valid for any uid in that channel — which is what Agora's wildcard tokens do, and why you should mint per-user tokens in production.
Roles and publish rights
Agora's publisher privileges in a token are advisory by default: an audience-role client can still call setClientRole(broadcaster) and publish, which is what makes co-host flows work without re-minting a token. VoxaRTC matches that default — a subscriber-role token may publish.
If you want strict enforcement — the token alone decides whether a participant can send media — set enforceCoHostAuth on the project. Then only tokens carrying the publish privileges are allowed to send media.
Renewal
Token lifecycle callbacks work as they do with Agora. The SDK schedules onTokenPrivilegeWillExpire ahead of the Agora token's expiry and onRequestToken at it; you respond with renewToken() as before. The media credential underneath has its own shorter TTL and is refreshed on reconnect exchanges — you never see it.
_engine.registerEventHandler(RtcEngineEventHandler(
onTokenPrivilegeWillExpire: (connection, token) async {
final fresh = await myBackend.fetchRtcToken(
channel: connection.channelId!,
);
await _engine.renewToken(fresh);
},
onRequestToken: (connection) async {
final fresh = await myBackend.fetchRtcToken(
channel: connection.channelId!,
);
await _engine.renewToken(fresh);
},
));Minting from your own backend
Nothing VoxaRTC-specific is required — mint an Agora token the way you always have. For example, with Node's agora-token:
const { RtcTokenBuilder, RtcRole } = require('agora-token');
const token = RtcTokenBuilder.buildTokenWithUid(
process.env.VOXA_APP_ID, // from the console
process.env.VOXA_APP_CERTIFICATE, // from the console
channelName,
uid,
RtcRole.PUBLISHER,
3600, // token TTL, seconds
3600, // privilege TTL, seconds
);The console mints the same thing from its Tokens tab, which is handy before your token server is wired up.
Error codes
Verification failures come back as Agora's numeric codes, so your existing error handling still branches correctly.
| Code | Meaning | Usual cause |
|---|---|---|
| 109 | Token expired | Privilege TTL elapsed; renew and retry |
| 110 | Invalid token | Wrong certificate, wrong channel name, or a uid the token was not minted for |
| 2 | Invalid argument | Missing token or channelName in the join request |
If joins fail with "unknown App ID", the token is signed by credentials that are not registered on the gateway at https://gate.zamansheikh.com — create or import the project first.