osquarski OpenAI example **Since OpenAI does not include cost, I use a custom pricing map to return it** [details="Codeblock"] ```typescript import { UIMessage, convertToModelMessages, generateText, wrapLanguageModel, } from 'ai'; import { createOpenAI } from '@ai-sdk/openai'; import { createOpenAIMiddleware } from '@ai-billing/openai'; import { consoleDestination, createObjectPriceResolver, ModelPricing, } from '@ai-billing/core'; const openai = createOpenAI({ // eslint-disable-next-line turbo/no-undeclared-env-vars apiKey: process.env.OPENAI_API_KEY, }); const customPricingMap: Record<string, ModelPricing> = { 'gpt-5': { promptTokens: 1.25 / 1_000_000, completionTokens: 10.0 / 1_000_000, inputCacheReadTokens: 0.125 / 1_000_000, }, 'gpt-4o': { promptTokens: 5.0 / 1_000_000, completionTokens: 15.0 / 1_000_000, }, }; const priceResolver = createObjectPriceResolver(customPricingMap); const billingMiddleware = createOpenAIMiddleware({ destinations: [consoleDestination()], priceResolver: priceResolver, }); export async function POST() { try { const messages: UIMessage[] = [ { id: 'test-gen-1', role: 'user', parts: [{ type: 'text', text: 'What is the capital of Sweden?' }], }, ]; const model = 'gpt-5'; const wrappedModel = wrapLanguageModel({ model: openai(model), middleware: billingMiddleware, }); const result = await generateText({ model: wrappedModel, messages: await convertToModelMessages(messages), }); return Response.json(result); } catch (error) { console.error('Generate Error:', error); return Response.json({ error: (error as Error).message }, { status: 500 }); } } ``` [/details]
osquarski @ai-billing also sends usage events to Billing Providers Here is an infographic how: And a codesnippet showing how to do it for: Stripe [details="Codesnippet"] ```typescript 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(); } ``` [/details] Polar [details="Codesnippet"] ```typescript 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(); } ``` [/details] Lago [details="Codesnippet"] ```typescript 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(); } ``` [/details] OpenMeter [details="Codesnippet"] ```typescript 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(); } ``` [/details]
osquarski Installation ```bash npm install @ai-billing/core @ai-billing/openrouter # Example for OpenRouter ``` Full-stack @ai-billing examples | Name | Demo Link | Repo | Deploy | | :--- | :--- | :--- | :--- | | **Chatbot (OpenRouter + Polar)** | View Demo | GitHub | | | **Chatbot (OpenAI + Polar)** | View Demo | GitHub | | | **Chatbot (Stripe)** | View Demo | GitHub | |
system While a member of our team prepares to jump in, you might find your answer even faster in our community resources. - Official Documentation: https://v0.app/docs - The v0 expert walkthrough: https://community.vercel.com/t/become-a-v0-expert/5981 - Watch v0 in Action: https://community.vercel.com/live#v0
Amy Egan I found your support case and I'm digging in now to see what I can do to solve it faster ![]()
Amy Egan The billing system experts helped me figure out what was wrong and correct it! You should see a reply on your support case now. Please try again and us know if you still have any trouble with it. ![]()
system Thank you for bringing this to our attention. To ensure this is investigated with the necessary priority and privacy, please report all security-related concerns, potential exploits, or abuse directly to the Vercel Security Team. Please submit your report here: vercel.com/abuse Reporting via this official channel is the fastest way to reach our security engineers and ensures that sensitive information is handled in a secure environment rather than a public forum.
Kawaki Nagisa I got a response that my 2 invoices will be refunded but I only got 1 refund invoice the other is still in paid status while the whole 1500 creds were removed so can someone from support help me expedite this bcs the support ticket got auto closed after there response I did create a follow up case : 0147h2jnlrddNrGY original case id : 01d14EiL4Dls9pih
Jacob Paris Sent you a DM
Vitorhramos Um membro deixou de fazer parte do meu time e não usa mais os creditos. Foi feito um plano anual e essa conta não será mais usada. Como fazer? É possivel transferir os creditos pra minha conta? Como deixar de fazer a cobrança na conta desse colega que não será mais usada?