Getting Started
Core Concepts
Session Tokens
Understand how session tokens work and manage them effectively
Token Lifecycle
Understanding the token lifecycle helps you build more robust applications:
Token Properties
Temporary
Tokens expire after 1 hour by default
Single-use
Each token is tied to one conversation session
Persona-specific
Tokens are bound to the persona configuration used to create them
Secure
Tokens cannot be used to access your account or create new tokens
Creating Session Tokens
Basic Token Creation
async function createSessionToken() {
const response = await fetch("https://api.anam.ai/v1/auth/session-token", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.ANAM_API_KEY}`,
},
body: JSON.stringify({
personaConfig: {
name: "Alex",
avatarId: "30fa96d0-26c4-4e55-94a0-517025942e18",
voiceId: "6bfbe25a-979d-40f3-a92b-5394170af54b",
brainType: "ANAM_GPT_4O_MINI_V1",
systemPrompt: "You are a helpful customer service representative.",
},
}),
});
const { sessionToken } = await response.json();
return sessionToken;
}
Advanced Token Management
For production applications, implement proper token management:
class SessionTokenManager {
constructor(apiKey) {
this.apiKey = apiKey;
this.tokenCache = new Map();
}
async createToken(personaConfig, userId = null) {
// Create cache key from persona config
const cacheKey = this.createCacheKey(personaConfig, userId);
// Check if we have a valid cached token
if (this.tokenCache.has(cacheKey)) {
const cached = this.tokenCache.get(cacheKey);
if (cached.expiresAt > Date.now()) {
return cached.token;
}
this.tokenCache.delete(cacheKey);
}
try {
const response = await fetch("https://api.anam.ai/v1/auth/session-token", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${this.apiKey}`,
},
body: JSON.stringify({ personaConfig }),
});
if (!response.ok) {
throw new Error(`Token creation failed: ${response.status}`);
}
const { sessionToken } = await response.json();
// Cache token for 50 minutes (10 minutes before expiry)
this.tokenCache.set(cacheKey, {
token: sessionToken,
expiresAt: Date.now() + (50 * 60 * 1000)
});
return sessionToken;
} catch (error) {
console.error('Failed to create session token:', error);
throw error;
}
}
createCacheKey(personaConfig, userId) {
const key = JSON.stringify({ personaConfig, userId });
return btoa(key); // Base64 encode for safe map key
}
clearCache() {
this.tokenCache.clear();
}
}
Token Security Best Practices
Server-side Token Creation
Never create session tokens on the client side. Your API key should only exist in your server environment.
// ✅ Correct: Server-side endpoint
app.post('/api/session-token', authenticateUser, async (req, res) => {
const { personaType } = req.body;
const personaConfig = getPersonaConfig(personaType, req.user);
const sessionToken = await sessionTokenManager.createToken(personaConfig, req.user.id);
res.json({ sessionToken });
});
// ❌ Wrong: Client-side token creation
const sessionToken = await fetch("https://api.anam.ai/v1/auth/session-token", {
headers: { Authorization: `Bearer ${window.ANAM_API_KEY}` } // NEVER DO THIS
});
User Authentication
Always validate user permissions before creating tokens:
app.post('/api/session-token', async (req, res) => {
// Validate user session/JWT
const user = await authenticateUser(req);
if (!user) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Check user permissions
if (!user.canAccessPersonas) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
// Rate limiting
const rateLimitKey = `session_tokens:${user.id}`;
const tokenCount = await redis.incr(rateLimitKey);
if (tokenCount === 1) {
await redis.expire(rateLimitKey, 3600); // 1 hour window
}
if (tokenCount > 10) { // Max 10 tokens per hour per user
return res.status(429).json({ error: 'Rate limit exceeded' });
}
// Create token
const sessionToken = await createSessionToken(getPersonaConfig(user));
res.json({ sessionToken });
});
Error Handling
Implement robust error handling for token operations:
async function createSessionTokenWithRetry(personaConfig, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch("https://api.anam.ai/v1/auth/session-token", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.ANAM_API_KEY}`,
},
body: JSON.stringify({ personaConfig }),
});
if (!response.ok) {
const errorData = await response.json();
// Handle specific error codes
switch (response.status) {
case 401:
throw new Error('Invalid API key');
case 429:
// Rate limited - wait before retry
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
continue;
}
throw new Error('Rate limit exceeded');
case 400:
throw new Error(`Invalid persona configuration: ${JSON.stringify(errorData)}`);
default:
throw new Error(`Token creation failed: ${response.status}`);
}
}
const { sessionToken } = await response.json();
return sessionToken;
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
Token Validation
Validate tokens before using them in client applications:
async function validateSessionToken(token) {
try {
// Attempt to create client with token
const testClient = createClient(token);
// Set up timeout for connection test
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Connection timeout')), 5000)
);
// Test connection
await Promise.race([
testClient.connect(),
timeoutPromise
]);
return true;
} catch (error) {
console.error('Token validation failed:', error);
return false;
}
}
Monitoring and Debugging
Track token usage for debugging and optimization:
class TokenAnalytics {
constructor() {
this.metrics = {
tokensCreated: 0,
tokensUsed: 0,
tokensFailed: 0,
averageSessionDuration: 0
};
}
trackTokenCreation(userId, personaConfig) {
this.metrics.tokensCreated++;
// Log for debugging
console.log('Token created', {
userId,
personaName: personaConfig.name,
timestamp: new Date().toISOString()
});
}
trackTokenUsage(sessionId, duration) {
this.metrics.tokensUsed++;
// Update average session duration
this.metrics.averageSessionDuration =
(this.metrics.averageSessionDuration + duration) / 2;
console.log('Session completed', {
sessionId,
duration,
timestamp: new Date().toISOString()
});
}
trackTokenFailure(error, context) {
this.metrics.tokensFailed++;
console.error('Token operation failed', {
error: error.message,
context,
timestamp: new Date().toISOString()
});
}
getMetrics() {
return this.metrics;
}
}
Common Issues and Solutions
Symptoms: Frequent disconnections, users having to refresh
Solutions:
- Implement token refresh logic in your client
- Cache tokens server-side to avoid recreating them
- Monitor token usage patterns to optimize expiry times
// Implement token refreshing
anamClient.on('token_expired', async () => {
const newToken = await refreshSessionToken();
await anamClient.updateToken(newToken);
});
Symptoms: 429 errors when creating tokens
Solutions:
- Implement exponential backoff for retries
- Cache tokens to reduce API calls
- Distribute token creation across time
const backoffDelay = Math.min(1000 * Math.pow(2, attempt), 30000);
await new Promise(resolve => setTimeout(resolve, backoffDelay));
Symptoms: 400 errors with configuration details
Solutions:
- Validate persona config before sending
- Use known good avatar and voice IDs
- Check system prompt length and content
function validatePersonaConfig(config) {
if (!config.name || typeof config.name !== 'string') {
throw new Error('Persona name is required and must be a string');
}
if (!config.avatarId || !isValidUUID(config.avatarId)) {
throw new Error('Valid avatar ID is required');
}
if (config.systemPrompt && config.systemPrompt.length > 2000) {
throw new Error('System prompt too long (max 2000 characters)');
}
return true;
}
Next Steps
Was this page helpful?