Keep your API secret off the device
Keep the API secret on your backend and out of the shipped app.
Your API secret can create agents, start sessions, spend your credits and create more secrets. Anything you ship to a user’s device can be read by that user: a string in the app bundle, a value in the app’s preferences, or a header the app sends. Keep the secret on a server you run, and give devices the least they need.
Why it matters
- A secret in a shipped app is shared by every install. Revoking it stops every copy of the app at once.
- Whoever extracts it is billed as you: their sessions land on your account next to your users’ sessions.
- A new build does not remove a leaked secret from old builds. Only revoking it does.
Pick a pattern
| Your app | Where the avatar renders | What the device holds |
|---|---|---|
| A web page or a web view | the browser | nothing for a public agent; an embed token for a private one |
| Any app with a LiveKit client | bitHuman cloud or your servers | a LiveKit room token from your server (Build a LiveKit agent) |
| The Swift package or the Android SDK | the device | the API secret, in memory, fetched from your backend (below) |
The first two patterns keep the secret on your backend: your server mints a short-lived token with the secret and the device presents only that token.
On-device rendering
The Swift package and the Android SDK report usage with the credential you pass to Expression2Credential.set or Essence2Credential.set. That credential is your API secret, so the device needs it in memory while it renders. You can still keep it out of the binary and limit what a leak costs.
Treat that secret as exposed in any build you ship: what a shipped app holds has the rules. In short, fetch it from your backend at launch, give each app its own secret, and watch your balance.
Serve the secret from your backend
Your backend checks your own user’s sign-in, then returns the secret. The app calls it at launch and passes the answer to the setter.
# Your backend: returns the secret only to a signed-in user of your app.
import os
def bithuman_credential(request):
require_signed_in_user(request) # your own auth check
return {"secret": os.environ["BITHUMAN_API_SECRET"]}
// Your app: fetch at launch, keep in memory, never write it to disk.
struct Credential: Decodable { let secret: String }
let (data, _) = try await URLSession.shared.data(for: yourSignedInRequest)
Expression2Credential.set(try JSONDecoder().decode(Credential.self, from: data).secret)
On Android, call Expression2Credential.set(secret) (or Essence2Credential.set(secret)) with the value before fetch() and create().
This keeps the secret out of the app bundle and source control, and lets you rotate it without shipping a build. It does not stop a signed-in user from reading it from memory, so pair it with the steps below.
Limit what a leak costs
- Give this app its own API secret, used by nothing else, so you can revoke it alone.
- Rotate it on a schedule: create the new secret, point your backend at it, then revoke the old one (Rotate a secret).
- Watch your balance and revoke at once if usage looks wrong.
Next
- Your API secret: where each platform reads it.
- Build a LiveKit agent: render in the cloud, with the app holding only a room token.
- Embedding: mint embed tokens on your backend.