livqenoDocs

GuidesBuild for production

Build for production

Everything between a working demo and an app that's ready for real users.

Your demo works. This guide covers what to do before real users arrive.

A demo and a production app use the same Livqeno API. There's no "production mode" to switch on. The difference is in how you handle keys, tokens, networks, and failures. Each step below says what can go wrong with real users if you skip it.

Where this fits: the last guide. You've built at least one feature, and you're about to deploy it.

What you'll build

Not a new feature. A production-ready setup:

  • Secrets kept safe.
  • Tokens with the right lifetime.
  • Calls that connect on any network (TURN).
  • Verified webhooks.
  • Monitoring.
  • A clear view of what Livqeno hasn't proven yet.

What you need

  • A working app using at least one Livqeno product.
  • A place to deploy your backend and store secrets.

Steps

1. Use separate keys per environment

Why: if your laptop and your live server share a key, a test script can create rooms, send messages, or use up allowances in front of real users.

Use a production key in production, and a development key everywhere else. Each key is locked to its environment (development, staging, or production, each with its own separate data), so this keeps them apart for free:

Terminal
livqeno keys create --name backend-prod --environment production
livqeno keys create --name backend-dev  --environment development

Never put a production key on a developer's machine. See Environments.

2. Keep secrets out of git

Why: anything in git is eventually read by someone who shouldn't, through a public repo, a laptop, or a CI log.

Two secrets matter. Store them in your host's secret manager or environment settings, not in your code:

SecretHeld by
LIVQENO_API_KEYYour backend only
LIVQENO_WEBHOOK_SECRETYour webhook receiver only

3. Use short tokens, and refresh them

Why: a token is a pass to one room. If one leaks (a screenshot, a log, a shared link), it's only useful until it expires.

On your backend:

const credentials = await livqeno.tokens.create({
  room: room.id,
  identity: user.id,
  permissions: { join: true, subscribe: true, publish: true },
  expiresIn: 600,                 // not 21600 because you can
});

Keep the lifetime short. Ten minutes is plenty:

  • The token is checked when someone connects. A call in progress stays connected after the token expires.
  • If the network drops later, reconnecting needs a valid token. On web and React Native, rejoin with a fresh one from your endpoint (see Handle reconnection). Flutter can fetch one for you with refreshToken.
  • You can revoke a token (DELETE /v1/rooms/{roomId}/rtc-tokens/{tokenId}). New connections with it are refused, and a session already running on it is ended (TOKEN_REVOKED). It's best-effort, though, and a session that has refreshed onto a newer token isn't on the old one any more. So a short lifetime is still what limits the damage if a token leaks.

See RTC authentication.

For chat, set the refresh callback in your app. Forgetting it is the most common production chat bug: chat stops working when the token expires:

createChatClient({
  token,
  apiUrl,
  onTokenExpiring: async () => (await fetch('/chat-token').then((r) => r.json())).token,
});

4. HTTPS, WSS, and Allowed Origins

Why: without encryption, anyone on the same Wi-Fi can read what your app sends, including tokens.

  • https:// for the API, and for your own site.
  • wss:// (encrypted WebSocket) for both WebSockets. The Livqeno addresses in each grant already use it; just don't replace them.

The chat token travels in the WebSocket URL (browsers can't send headers on a WebSocket), so encryption is what keeps it private.

Then list the sites allowed to use your project. An origin is a site's address: scheme, host, and port, like https://app.example.com. Add every origin you serve from under Settings → Security → Allowed Origins:

https://app.example.com
https://admin.example.com
  • No wildcards (*). List each site.
  • While the list is empty, any origin is allowed. Add all your sites at once.
  • A page from a site that isn't listed is refused with ORIGIN_NOT_ALLOWED (chat close code 4403).

See Browser security & CORS.

5. Forward TURN credentials unchanged, then test

Why: many office, hotel, and mobile networks block direct connections between devices. A TURN relay passes the media along instead. Livqeno runs TURN for you, but it only helps if your app uses it:

  • Send iceServers from the token response to the app unchanged. The credentials are made per token and expire with it.
  • Never hard-code or cache an old iceServers list. A stale one fails quietly, on exactly the networks that need TURN most.

Then check that it really works. On your computer:

Terminal
livqeno diagnostics

6. Verify webhooks, and watch deliveries

Why: an unverified webhook endpoint accepts fake events from anyone. A silently failing one loses data.

How to verify is in Handle webhooks. You also need to watch that deliveries succeed:

Terminal
curl "$LIVQENO_API_URL/v1/projects/$PROJECT_ID/webhooks/$WEBHOOK_ID/deliveries" \
  -H "Authorization: Bearer $LIVQENO_TOKEN"

After 50 failures in a row, an endpoint is turned off automatically. If you don't check, you lose data without noticing.

7. Set up monitoring

Why: users rarely report "the call didn't connect". They just leave. You need to see failures yourself.

These CLI commands show real data from your project:

Terminal
livqeno status                    # dependency health
livqeno connections list          # what connected, and how it ended
livqeno errors list               # classified failures with likely causes
livqeno diagnostics               # is the whole stack reachable

Alert on the rate of FAILED connections, not on single failures. One failed call is someone's bad network. A rising rate is a real problem.

8. Know the limits

Why: hitting a limit in production shows up as errors for users. Know them before you design around them, for example cap your text box at 4000 characters.

LimitDefault
People per room50
Message length4000 characters
Attachment size25 MB
Token mints60 per window
Chat sends30 per 10 seconds per user

Full list: Limits & quotas.

How it works

  • The API is the same in production. There's no "production mode" to turn on, so there's no switch to forget. Everything above is a key, a setting, or a check.
  • Keep the credentials straight, and you're mostly safe. Short-lived, narrow tokens in the browser. A permanent key only on the server. Most real-time production incidents come from getting this wrong.

What isn't ready yet

Know these before you launch:

AreaStatus
Congestion controlTWCC feedback is collected; nothing consumes it to drive layer selection
Server-side quality verdictgetConnectionQuality() returns 'unknown' rather than guessing
Simulcast on AV1 / H.265Keyframe detection covers VP8, VP9, H.264 only
Browser coverageOnly Chromium exercised with real media
Measured capacity100 participants on loopback with synthetic media — not a capacity figure
RecordingDoes not exist
Billing / paid plansDoes not exist. Each account gets fixed free grants; see Usage

The full list, and what each means for you, is in Known limitations. Read it before you pick a launch date.

Next steps