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

Next Steps