Overview
The Lava SDK uses forward tokens for authentication. A forward token is a composite credential that combines your secret key, a connection identifier, and optional product configuration into a single authentication string.
Forward tokens enable Lava to:
- Authenticate your merchant account
- Identify which customer wallet to charge
- Apply specific pricing rules (via product configuration)
- Track usage per connection
Generating Forward Tokens
The SDK provides the generateForwardToken() method to create tokens for AI API requests.
Connection-Based Authentication
Use this pattern when billing a specific customer’s wallet with your pricing configuration:
const forwardToken = lava.generateForwardToken({
connection_secret: 'conn_secret_from_connection',
product_secret: 'prod_secret_from_dashboard'
});
// Use the token in AI requests
const response = await fetch(lava.providers.openai + '/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${forwardToken}`
},
body: JSON.stringify({ /* request body */ })
});
When to use: Production applications where each customer has their own wallet and you want to apply merchant pricing.
Parameters:
connection_secret - Obtained from connection object after checkout completes
product_secret - Your product configuration ID from dashboard (optional, uses default pricing if omitted)
Provider Key Authentication (BYOK Mode)
Use this pattern when you want to use your own AI provider API key instead of billing through Lava wallets:
const forwardToken = lava.generateForwardToken({
connection_secret: null,
product_secret: null,
provider_key: process.env.OPENAI_API_KEY!
});
// Lava tracks usage but uses your provider key
const response = await fetch(lava.providers.openai + '/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${forwardToken}`
},
body: JSON.stringify({ /* request body */ })
});
When to use: Development, testing, or when you want usage tracking without wallet billing.
Parameters:
connection_secret - Set to null
product_secret - Set to null
provider_key - Your actual API key for the AI provider (e.g., OpenAI API key)
BYOK (Bring Your Own Key) mode allows you to use Lava’s proxy for usage tracking and analytics without setting up wallet billing. The AI provider charges your provider account directly.
Token Lifecycle
When to Generate Tokens
Per-connection pattern (recommended):
// Generate once per user session or store securely
const userToken = lava.generateForwardToken({
connection_secret: userConnection.connection_secret,
product_secret: process.env.LAVA_PRODUCT_SECRET!
});
// Reuse for multiple requests
await makeAIRequest(userToken);
await makeAIRequest(userToken); // Same token
Per-request pattern (maximum security):
// Generate fresh token for each request
function makeAIRequest(connectionSecret: string) {
const token = lava.generateForwardToken({
connection_secret: connectionSecret,
product_secret: process.env.LAVA_PRODUCT_SECRET!
});
return fetch(lava.providers.openai + '/chat/completions', {
headers: { 'Authorization': `Bearer ${token}` }
// ...
});
}
Token Composition
Forward tokens are Base64-encoded JSON objects containing:
{
"secret_key": "aks_your_secret_key",
"connection_secret": "conn_secret_123",
"product_secret": "prod_secret_456",
"provider_key": null
}
Never decode or modify forward tokens manually. Always use generateForwardToken() to ensure proper encoding and security.
Security Best Practices
1. Protect Your Secret Key
// ✅ Good: Use environment variables
const lava = new Lava(process.env.LAVA_SECRET_KEY!, {
apiVersion: '2025-04-28.v1'
});
// ❌ Bad: Hardcode keys in source code
const lava = new Lava('aks_secret_123456789', {
apiVersion: '2025-04-28.v1'
});
2. Never Expose Tokens in Frontend Code
// ✅ Good: Generate tokens in backend
// Backend API route
app.post('/api/ai-request', async (req, res) => {
const token = lava.generateForwardToken({
connection_secret: req.user.connectionSecret,
product_secret: process.env.LAVA_PRODUCT_SECRET!
});
// Make AI request with token server-side
const result = await makeAIRequest(token);
res.json(result);
});
// ❌ Bad: Send token to frontend
app.get('/api/get-token', (req, res) => {
const token = lava.generateForwardToken({ /* ... */ });
res.json({ token }); // Token exposed to client
});
3. Rotate Secret Keys Periodically
Rotate your secret keys every 90 days or immediately if compromised:
- Create a new secret key in your Lava dashboard
- Update your environment variables
- Deploy the change
- Delete the old secret key after confirming the new one works
4. Use Different Keys for Environments
# Development (.env.development)
LAVA_SECRET_KEY=aks_test_dev_key_here
# Production (.env.production)
LAVA_SECRET_KEY=aks_prod_key_here
Integration Patterns
Webhook Handler Pattern
Generate tokens after receiving checkout completion webhooks:
import { Lava } from '@lavapayments/nodejs';
const lava = new Lava(process.env.LAVA_SECRET_KEY!, {
apiVersion: '2025-04-28.v1'
});
// Webhook handler for checkout completion
app.post('/webhooks/lava/checkout-complete', async (req, res) => {
const { connection_id, reference_id } = req.body;
// Retrieve full connection details
const connection = await lava.connections.retrieve(connection_id);
// Store connection_secret securely for this user
await database.users.update(reference_id, {
lavaConnectionSecret: connection.connection_secret
});
res.json({ received: true });
});
// Later, when making AI requests
app.post('/api/chat', async (req, res) => {
const user = await database.users.find(req.userId);
const token = lava.generateForwardToken({
connection_secret: user.lavaConnectionSecret,
product_secret: process.env.LAVA_PRODUCT_SECRET!
});
// Use token for AI request
});
Multi-Product Pattern
Use different product secrets for different pricing tiers:
function generateTokenForUser(user: User) {
// Select product based on user's subscription tier
const productSecret = user.isPremium
? process.env.LAVA_PREMIUM_PRODUCT_SECRET!
: process.env.LAVA_BASIC_PRODUCT_SECRET!;
return lava.generateForwardToken({
connection_secret: user.connectionSecret,
product_secret: productSecret
});
}
Troubleshooting
401 Unauthorized
Cause: Invalid secret key or forward token
Solution:
- Verify your secret key is correct in environment variables
- Ensure you’re using the
Bearer prefix in the Authorization header
- Check that connection_secret and product_secret are valid
Cause: Manually constructed token or encoding issue
Solution:
- Always use
generateForwardToken() method
- Never manually modify or decode tokens
- Ensure all parameters are strings (not objects or numbers)
Token Works in Test but Not Production
Cause: Using test mode secret key in production
Solution:
- Test keys (
aks_test_*) only work with sandbox environment
- Use production secret key for live requests
- Verify environment variables are set correctly per environment