Security

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

  1. The browser calls your backend.
  2. Your backend forwards requests to https://api.zeroweight.ai/api/v1/*.
  3. Your backend injects X-ZW-Api-Key.
  4. 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.