Header Logo

How to Store and Rotate Realtime Avatar API Keys Safely in a Flask Application

How to Store and Rotate Realtime Avatar API Keys Safely in a Flask Application

Store and rotate realtime avatar API keys in Flask with env vars, server-side config, and zero-downtime secret rotation.

Introduction


When you integrate a realtime avatar service into a Flask app, the hardest part is usually not the avatar stream itself. It’s handling API keys safely while the system is creating sessions, rendering tokens, and occasionally rotating credentials without breaking active traffic.


The core problem is simple: a server-side API key can create and manage avatar sessions, but it should never be exposed to browsers, logs, or client-side code. If you store it poorly, you end up with one of three failure modes:


  • the key leaks into frontend bundles or browser devtools,

  • the key ends up in plaintext config that gets copied around too freely, or

  • rotation is so disruptive that nobody does it until after an incident.


By the end of this article, you should be able to store a realtime avatar API key in a Flask application using environment-based configuration, keep it out of source control, load it safely in production, and rotate it with minimal downtime. I’ll also show where a managed integration changes the threat model and reduces how often you need to touch the key at all.


Start with the right trust boundary


For a service like Protoface, the API key belongs only on trusted server-side components. That means Flask routes, background workers, or internal jobs that call the REST API or Python SDK. It does not belong in JavaScript, mobile apps, or any browser-exposed code path.


In practice, this means the browser should talk to your Flask backend, and your backend should talk to the avatar API. If a frontend needs to start a session, it should request that from your server, which then signs the request using the stored key. That keeps the credential in one place and lets you enforce your own authorization rules before you ever create a realtime session.


For a Flask app, the simplest safe storage pattern is:


  1. store the key in an environment variable or secret manager,

  2. load it into Flask configuration on process start,

  3. never print it, serialize it, or ship it to templates, and

  4. allow rotation by reading the current value from config rather than hardcoding it.


Store secrets outside the codebase


The practical baseline is environment variables. They’re not perfect, but they’re the right default because they keep secrets out of Git and let you vary credentials by environment.


import os

return app
import os

return app
import os

return app


That single line forces you to inject the key from the deployment environment. In local development, use a shell export or a local secret file that is never committed. In production, use your platform’s secret store, container secret injection, or a vault system.


Do not put the key in:


  • Git-tracked config files,

  • frontend environment variables that get bundled into JavaScript,

  • request logs, or

  • Flask SECRET_KEY by accident. That value is for session signing and CSRF protection, not for third-party API credentials.


Use a dedicated config object and keep the key server-side


One clean pattern is to isolate all avatar API access in a small service object. That makes rotation, testing, and auditing easier because your app has exactly one place that knows how to authenticate outbound requests.


import os

return resp.json()
import os

return resp.json()
import os

return resp.json()


This is intentionally minimal. The exact endpoints and request shapes depend on the API docs, but the pattern is what matters: the key stays in memory on the server, outbound calls are authenticated with a bearer header, and the browser only receives the result you choose to expose.


If you’re using the Python SDK, the same idea applies: initialize the client from environment-backed config inside Flask, not in client-visible code. The key point is that credential handling should be centralized and testable.


Rotation without downtime


Rotation is where most teams get sloppy. If you hardcode a single secret into one process, rotation means redeploying everything at once. That’s avoidable.


A safer strategy is to support overlapping validity: create a new key, deploy it to the app, verify traffic, then revoke the old one. For most Flask deployments, that can be done without downtime if you treat keys as runtime configuration rather than baked-in constants.


A simple rotation workflow looks like this:


  1. Generate a new API key in the dashboard.

  2. Add it to your deployment environment as the next active secret.

  3. Deploy or restart workers so they pick up the new value.

  4. Verify calls to the REST API or SDK are succeeding.

  5. Revoke the old key once traffic has fully moved over.


If you run multiple Flask workers or pods, be careful about caching config too aggressively. A process that reads the key once at import time may keep using the old value long after you think rotation is complete. That’s fine if you plan for it, but then your rollout needs to restart all workers explicitly.


For systems that need faster rotation, you can read from an external secret store on startup and refresh on a controlled interval. The trade-off is added complexity and more moving parts. For most applications, startup-time loading plus rolling restarts is enough.


Common gotchas in Flask deployments


A few failure modes come up repeatedly:


  • Logging request headers. If you dump outbound headers for debugging, redact Authorization immediately.

  • Template leakage. Never pass the key into Jinja templates “just for testing.” That pattern tends to survive longer than intended.

  • Celery or background jobs. If those workers also call the avatar API, they need the same secret injected independently. Don’t assume Flask web workers and task workers share config.

  • Local notebooks and ad hoc scripts. Treat them as production code when they touch secrets. They should read from the same environment variable, not a pasted token.

  • Accidental client-side use. If the browser directly calls the avatar API with your bearer token, the key is already compromised.


Also remember that API key safety is only one side of the problem. Session endpoints, user authorization, and rate limits still matter. If a user can trigger session creation through your Flask app, your backend should enforce limits before it forwards the request to the avatar service.


Where Protoface changes the equation


The useful design choice here is that your Flask app only needs the server-side API key when it is actually managing avatars or sessions. For example, you might create a session from Flask, then hand a short-lived, purpose-built session result to the browser. That keeps the long-lived credential off the client.


If you’re using a voice agent stack, the LiveKit plugin path is especially clean: your agent process remains the trusted server-side component, and the avatar integration happens inside that backend workflow rather than in the browser. See the examples in the plugin repository or the documentation for the exact integration points and payload fields.


For teams that want a narrower surface area, the managed iframe embed model removes the API key from the browser entirely. That’s a different architecture, but it’s worth noting because it often eliminates the class of frontend secret-handling mistakes you’d otherwise have to guard against in Flask.


Practical rotation example


Here’s a minimal Flask route that uses the server-side key to create a session. The exact request body is illustrative; check the docs for the current schema.


from flask import Blueprint, current_app, jsonify, request

return jsonify(session)
from flask import Blueprint, current_app, jsonify, request

return jsonify(session)
from flask import Blueprint, current_app, jsonify, request

return jsonify(session)


To rotate the key safely, you don’t change this code. You change the deployed secret value. That separation is what makes rotation boring, which is exactly what you want.


If you need to verify a new key before revoking the old one, create a short smoke test in CI or in a staging job that exercises the same Flask route and checks that the avatar session call succeeds. That gives you a clean validation path without exposing credentials anywhere outside the server.


Conclusion


The safe pattern for realtime avatar API keys in Flask is straightforward: keep the key server-side, load it from environment or a secret manager, centralize outbound API access, and rotate by changing deployment configuration rather than editing code. If you do that, you get predictable security properties and a rotation process that won’t interrupt active users.


For implementation details, field names, and current examples, start with the docs. If you’re integrating through a LiveKit agent stack, use the plugin repository examples as your reference point. And if your use case is browser-facing, consider whether an iframe embed can remove the key from your frontend entirely.

Add a face to your AI.

No credit card needed.

Add a face to your AI.

No credit card needed.

Add a face to your AI.

No credit card needed.