Security
For production, do not ship your ZeroWeight API key to the browser. The recommended approach is to keep the key on your server and expose your own backend endpoints to the frontend.
Recommended architecture
- The browser calls your backend.
- Your backend forwards requests to
https://api.zeroweight.ai/api/v1/*. - Your backend injects
X-ZW-Api-Key. - The browser only receives the bundle payload or LiveKit token.
Frontend example
import { LiveKitAvatarSession } from "@zeroweight/react";
export default function SecureAvatar() {
// setting "anonymuous" for user name will disable character's memory
const userName = "anonymuous";
return (
<LiveKitAvatarSession
avatarId="your-avatar-id"
livekitUrl="wss://your-livekit-server.example.com"
api={{
getBundle: (avatarId) =>
fetch(`/api/avatars/bundle/${avatarId}`).then((r) => r.json()),
getLiveKitToken: (avatarId) =>
fetch(
`/api/livekit/token?avatar_id=${avatarId}&name=${userName}`
).then((r) => r.json())
}}
/>
);
}Backend implementation examples
Use these examples to build your own secure proxy. Your frontend should call these endpoints instead of the ZeroWeight API directly.
Bundle Proxy
pages/api/avatars/bundle/[avatarId].ts
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { avatarId } = req.query;
const response = await fetch(
`https://api.zeroweight.ai/api/v1/avatars/bundle/${avatarId}`,
{
headers: {
"X-ZW-Api-Key": process.env.ZEROWEIGHT_API_KEY || ""
}
}
);
const data = await response.json();
res.status(response.status).json(data);
}LiveKit Token Proxy
pages/api/livekit/token.ts
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { avatar_id, name } = req.query;
const response = await fetch(
`https://api.zeroweight.ai/api/v1/livekit/getToken?avatar_id=${avatar_id}&name=${name}`,
{
headers: {
"X-ZW-Api-Key": process.env.ZEROWEIGHT_API_KEY || ""
}
}
);
const data = await response.json();
res.status(response.status).json(data);
}Server security checklist
- Store the API key in environment variables.
- Never expose the key in client-side bundles.
- Add your own auth and tenant checks before proxying requests.
- Log and monitor bundle and token requests.
- Rate-limit your own public endpoints as well.
Why this matters
If the browser holds your private API key, anyone can inspect it and use it outside your app. A server-side proxy keeps credential control with you.