Going to production
Everything below has caused a real incident for someone. Half an hour with this list is cheaper than finding out from your users.
Keys and secrets#
- The API secret exists only in your server environment. Search your client bundles for
as_to be sure. - Separate projects for development and production, so a test never spends production minutes or joins a real channel.
- Know how to rotate: create a new key pair, deploy it, then disable the old one. Rotation is not an emergency if you have done it once.
If a secret ever reaches a client build
Treat it as public from that moment. Rotate immediately in the console and disable the old pair — a shipped binary cannot be recalled.
Token endpoint#
- It authenticates your user first. An open token endpoint is an open door to your quota.
- Your server chooses the identity. Never accept one from the client, or anyone can impersonate anyone.
- Your server chooses the role. A client that asks for
hostand gets it can take over a stream. - Tokens last six hours. Issue one per session rather than one per screen.
The shape that keeps you safe
app.post('/api/rtc-token', async (req, res) => {const user = await authenticate(req); // 1. your authif (!user) return res.status(401).json({ error: 'Sign in first' });const channel = req.body.channel;if (!(await userMayJoin(user, channel))) { // 2. your rulesreturn res.status(403).json({ error: 'Not allowed in this channel' });}const { token, wsUrl } = await applooma.createToken({roomName: channel,identity: user.id, // 3. YOUR id, not theirsrole: await roleFor(user, channel), // 4. YOUR decision});res.json({ token, wsUrl });});
Permissions and devices#
- Ask for camera and microphone at a moment the user understands, not on app start.
- Handle refusal: let them join muted rather than showing a dead screen.
- iOS needs
NSCameraUsageDescriptionandNSMicrophoneUsageDescription, or the app terminates on first use. - Android needs runtime permission requests; declaring them in the manifest is not enough.
- Browsers block autoplay with sound until the user interacts. Attach media in a click handler or offer an unmute control.
User interface#
| Case | What users should see |
|---|---|
| Joining | A connecting state, not a frozen button. |
| Reconnecting | A visible banner. Connections drop on mobile networks; silence looks like a crash. |
| Someone joins without video | Their name or avatar, not a black rectangle. |
| Quota exceeded (429) or plan inactive (402) | A clear message. This is a billing problem, not a network problem. |
| Token expired | Fetch a new token and rejoin, without making them restart the app. |
Before a large event#
- Use the
audiencerole for viewers. An audience token cannot publish, which is what keeps a large stream cheap and stable. - A channel holds up to 500 participants, but the practical limit depends on how many publish video. For big audiences, few publishers and many subscribers.
- Rehearse at roughly the size you expect. Streams fail at scale in ways they never do with four people.
- Tell us in advance if you expect an unusual peak, so we can raise limits and watch capacity: support@applooma.dev.
Usage and billing#
- Know your plan's minutes per period and watch usage in the console. Sandbox minutes are a one-time grant that lapses after 30 days.
- At 80% we email the account owner once. Make sure that address is one somebody reads.
- At 100% new tokens are refused with
QUOTA_EXCEEDED; outside an active period withSUBSCRIPTION_INACTIVE. Calls in progress continue. - Decide in advance what your app does when that happens — a queue, a waiting message, or an upgrade prompt.
Moving an existing app over#
- Put every real-time call behind one class of your own first. The move is then hundreds of lines, not thousands, and you can keep two implementations side by side.
- Choose the implementation per room, when the room is created, and store the choice on the room. A session that started one way finishes that way; only new rooms move, and rolling back is a flag, not a rebuild.
- Move video live first — join, picture, comments, gifts — and run it for a week. Then voice rooms, then two-room battles (a second
AppEngine) and private calls. - Wait for
onJoinedbefore sending anything, attach video views inonUserJoined, and readonStatswhen a call looks wrong before assuming the network.
Know when something is wrong#
- Log the error
codefrom every failed token request. A spike inQUOTA_EXCEEDEDis a business event, not a bug. - Subscribe to webhooks for joins and leaves if session records matter to you.
- Track how often clients reach the reconnecting state. A rise usually means a network or capacity problem worth reporting.
Privacy and consent#
AppLooma does not store your users' audio or video. Media is relayed, not recorded. What you are responsible for: telling participants when a session is being captured on your side, and meeting the consent rules that apply where your users are — they vary by country, and in some places every participant must agree.