When you need to hide your API key from client-side code or route requests through your backend, use the baseUrl option to point to your proxy server.

Why Use a Proxy?

We strongly recommend using a proxy server for production deployments. Key benefits:
  • API Key Protection: Keep your Sarvam API key secure on your server instead of exposing it in client-side code where it can be easily extracted
  • Access Control: Enforce your own authentication (e.g., only logged-in users can start conversations)
  • Rate Limiting: Implement custom rate limits per user to prevent abuse
  • Usage Tracking: Log and monitor API usage per user for billing or analytics
  • Request Validation: Validate and sanitize requests before forwarding to Sarvam

┌─────────────┐      JWT Auth      ┌──────────────┐    API Key     ┌─────────────┐
│   Client    │ ──────────────────▶│ Your Proxy   │ ──────────────▶│   Sarvam    │
│    App      │                    │   Server     │                │   API       │
└─────────────┘                    └──────────────┘                └─────────────┘
Use JWT-based authentication between your client and proxy—the same auth mechanism you use for the rest of your application. This ensures:
  • Only authenticated users can access the conversational AI
  • You can tie conversations to specific user accounts
  • No additional auth system to maintain

Server Setup (Python / FastAPI)

import os
import httpx
from fastapi import FastAPI, Request, Response, Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt

app = FastAPI()
security = HTTPBearer()

SARVAM_API_KEY = os.environ["SARVAM_API_KEY"]
JWT_SECRET = os.environ["JWT_SECRET"]
SARVAM_BASE = "https://apps.sarvam.ai/api/app-runtime/"

def verify_jwt(credentials: HTTPAuthorizationCredentials = Depends(security)):
    try:
        payload = jwt.decode(credentials.credentials, JWT_SECRET, algorithms=["HS256"])
        return payload
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid token")

@app.api_route("/api/sarvam/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def proxy(path: str, request: Request, user: dict = Depends(verify_jwt)):
    async with httpx.AsyncClient() as client:
        resp = await client.request(
            method=request.method,
            url=f"{SARVAM_BASE}{path}",
            headers={"Authorization": f"Bearer {SARVAM_API_KEY}"},
            content=await request.body(),
        )
        return Response(content=resp.content, status_code=resp.status_code, headers=dict(resp.headers))

Client Setup

How you pass authentication credentials to your proxy depends on your platform and deployment scenario.

Web (Same Domain)

When your proxy server and frontend are on the same domain, cookies are sent automatically with requests:
import { ConversationAgent } from "sarvam-conv-ai-sdk/browser";

const agent = new ConversationAgent({
  apiKey: "", // Empty - proxy handles Sarvam auth
  baseUrl: "/api/sarvam/", // Same-origin request
  config: {
    // ... your config
  },
});

Web (Cross-Domain) and React Native

For cross-domain requests, React Native, or any scenario where cookies aren’t automatically included, use customHeaders to pass authentication tokens:
const agent = new ConversationAgent({
  apiKey: "", // Empty - proxy handles Sarvam auth
  baseUrl: "https://api.yourapp.com/sarvam/",
  customHeaders: {
    Authorization: "Bearer eyJhbGciOiJIUzI1NiIs...",
    "X-User-Id": "user-123",
    "X-Session-Id": "session-456",
  },
  config: {
    // ... your config
  },
});
The SDK includes these headers in all HTTP requests to the API (e.g., when fetching the signed WebSocket URL).

Flutter

import 'package:sarvamconv_ai_sdk/sarvamconv_ai_sdk.dart';

final config = InteractionConfig(
  orgId: 'your_org_id',
  workspaceId: 'your_workspace_id',
  appId: 'your_app_id',
  userIdentifier: 'user@example.com',
  userIdentifierType: UserIdentifierType.email,
  interactionType: InteractionType.call,
  sampleRate: 16000,
);

// No API key passed - proxy server will add it
final agent = SamvaadAgent(
  config: config,
  baseUrl: 'https://your-proxy-server.com/sarvam-proxy/',
  headers: {
    'Authorization': 'Bearer user_session_token', // Your app's auth
  },
  audioInterface: DefaultAudioInterface(),
);

await agent.start();

Summary

ScenarioAuthentication Method
Browser (same domain)Cookies sent automatically
Browser (cross-domain)Use customHeaders
React NativeUse customHeaders
FlutterUse headers

Security Best Practices

Always Authenticate Users

app.use('/sarvam-proxy', async (req, res, next) => {
  const token = req.headers['authorization']?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  const user = await verifyUserToken(token);
  if (!user) {
    return res.status(401).json({ error: 'Invalid token' });
  }
  
  req.user = user;
  next();
});

Rate Limiting

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 60, // 60 requests per minute per IP
  message: { error: 'Too many requests' },
});

app.use('/sarvam-proxy', limiter);

Request Validation

app.use('/sarvam-proxy', (req, res, next) => {
  const allowedPaths = ['/orgs/', '/workspaces/', '/apps/'];
  const isAllowed = allowedPaths.some(p => req.path.includes(p));
  
  if (!isAllowed) {
    return res.status(403).json({ error: 'Path not allowed' });
  }
  
  next();
});

Environment Variables

Store your API key securely:
# .env (never commit this file!)
SARVAM_API_KEY=sk_samvaad_your_actual_api_key