@ai-billing also sends usage events to Billing Providers
Here is an infographic how:
And a codesnippet showing how to do it for:
Stripe
Codesnippet
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import {
streamText,
convertToModelMessages,
UIMessage,
wrapLanguageModel,
} from 'ai';
import { createOpenRouterV3Middleware } from '@ai-billing/openrouter';
import { consoleDestination } from '@ai-billing/core';
import { createStripeDestination } from '@ai-billing/stripe';
type BillingTags = {
org_name?: string;
stripe_customer_id?: string;
};
const stripeDestination = createStripeDestination<BillingTags>({
apiKey: `${process.env.STRIPE_SECRET_KEY}`, // Make sure this is in your .env
meterName: 'llm_usage', // The slug from your Stripe dashboard
});
const openrouter = createOpenRouter({
// eslint-disable-next-line turbo/no-undeclared-env-vars
apiKey: process.env.OPENROUTER_API_KEY,
});
//const consoleLogger = new ConsoleDestination();
const billingMiddleware = createOpenRouterV3Middleware<BillingTags>({
destinations: [consoleDestination(), stripeDestination],
});
export async function POST() {
const messages: UIMessage[] = [
{
id: 'test-message-123',
role: 'user',
parts: [
{
type: 'text',
text: 'What is the capital of Sweden?',
},
],
},
];
const model = 'google/gemini-2.0-flash-001';
const wrappedModel = wrapLanguageModel({
model: openrouter(model),
middleware: billingMiddleware,
});
const result = streamText({
model: wrappedModel,
messages: await convertToModelMessages(messages),
providerOptions: {
'ai-billing-tags': {
stripe_customer_id: 'cus_UIMLD4AuBpC8Ux', // This has to be defined as customer_mapping in your Stripe meter
org_name: 'acme corp', // This will end up in Stripe metadata
} as BillingTags,
},
});
return result.toUIMessageStreamResponse();
}
Polar
Codesnippet
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import {
streamText,
convertToModelMessages,
UIMessage,
wrapLanguageModel,
} from 'ai';
import { createOpenRouterV3Middleware } from '@ai-billing/openrouter';
import { consoleDestination } from '@ai-billing/core';
import { createPolarDestination } from '@ai-billing/polar';
type BillingTags = {
customer_id?: string;
org_name?: string;
};
const polarDestination = createPolarDestination<BillingTags>({
accessToken: process.env.POLAR_ACCESS_TOKEN, // Make sure this is in your .env
eventName: 'llm_usage', // The slug from your Polar dashboard
server: 'sandbox', // Good for testing!
});
const openrouter = createOpenRouter({
// eslint-disable-next-line turbo/no-undeclared-env-vars
apiKey: process.env.OPENROUTER_API_KEY,
});
//const consoleLogger = new ConsoleDestination();
const billingMiddleware = createOpenRouterV3Middleware<BillingTags>({
destinations: [consoleDestination(), polarDestination],
});
export async function POST() {
const messages: UIMessage[] = [
{
id: 'test-message-123',
role: 'user',
parts: [
{
type: 'text',
text: 'What is the capital of Sweden?',
},
],
},
];
const model = 'google/gemini-2.0-flash-001';
const wrappedModel = wrapLanguageModel({
model: openrouter(model),
middleware: billingMiddleware,
});
const result = streamText({
model: wrappedModel,
messages: await convertToModelMessages(messages),
providerOptions: {
'ai-billing-tags': {
customer_id: '4a874ea3-53ec-432d-9d55-c55bf957e18f', // This triggers internalCustomerId in Polar
org_name: 'Acme Corp', // This will end up in Polar metadata
} as BillingTags,
},
});
return result.toUIMessageStreamResponse();
}
Lago
Codesnippet
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import {
streamText,
convertToModelMessages,
UIMessage,
wrapLanguageModel,
} from 'ai';
import { createOpenRouterV3Middleware } from '@ai-billing/openrouter';
import { consoleDestination } from '@ai-billing/core';
import { createLagoDestination } from '@ai-billing/lago';
type BillingTags = {
userId?: string;
};
const lagoDestination = createLagoDestination<BillingTags>({
apiKey: `${process.env.LAGO_API_KEY}`,
apiUrl: process.env.LAGO_API_URL,
meterCode: 'llm_usage',
});
const openrouter = createOpenRouter({
// eslint-disable-next-line turbo/no-undeclared-env-vars
apiKey: process.env.OPENROUTER_API_KEY,
});
const billingMiddleware = createOpenRouterV3Middleware<BillingTags>({
destinations: [consoleDestination(), lagoDestination],
});
export async function POST() {
const messages: UIMessage[] = [
{
id: 'test-message-123',
role: 'user',
parts: [
{
type: 'text',
text: 'What is the capital of Sweden?',
},
],
},
];
const model = 'openai/gpt-4o';
const wrappedModel = wrapLanguageModel({
model: openrouter(model),
middleware: billingMiddleware,
});
const result = streamText({
model: wrappedModel,
messages: await convertToModelMessages(messages),
providerOptions: {
'ai-billing-tags': {
userId: 'user_lago_test',
} as BillingTags,
},
});
return result.toUIMessageStreamResponse();
}
OpenMeter
Codesnippet
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import {
streamText,
convertToModelMessages,
UIMessage,
wrapLanguageModel,
} from 'ai';
import { createOpenRouterV3Middleware } from '@ai-billing/openrouter';
import { consoleDestination } from '@ai-billing/core';
import { createOpenMeterDestination } from '@ai-billing/openmeter';
type BillingTags = {
userId?: string;
org_name?: string;
};
const openMeterDestination = createOpenMeterDestination<BillingTags>({
apiKey: `${process.env.OPENMETER_API_KEY}`,
});
const openrouter = createOpenRouter({
// eslint-disable-next-line turbo/no-undeclared-env-vars
apiKey: process.env.OPENROUTER_API_KEY,
});
const billingMiddleware = createOpenRouterV3Middleware<BillingTags>({
destinations: [consoleDestination(), openMeterDestination],
});
export async function POST() {
const messages: UIMessage[] = [
{
id: 'test-message-123',
role: 'user',
parts: [{ type: 'text', text: 'What is the capital of Sweden?' }],
},
];
const model = 'openai/gpt-4o';
const wrappedModel = wrapLanguageModel({
model: openrouter(model),
middleware: billingMiddleware,
});
const result = streamText({
model: wrappedModel,
messages: await convertToModelMessages(messages),
providerOptions: {
'ai-billing-tags': {
userId: 'user_openmeter_test',
org_name: 'Acme Corp',
} as BillingTags,
},
});
return result.toUIMessageStreamResponse();
}