@ai-billing/gateway. Follow the checked-out source and dependency versions rather than assuming the older OpenRouter package list still applies.
Before you start
You need Node.js compatible with the demo’s dependencies, the pnpm version specified in itspackage.json, a PostgreSQL database, an authentication secret, and a Polar sandbox access token. Local inference also requires AI_GATEWAY_API_KEY. The repository’s .env.example is the source of truth for environment variables; deployments on Vercel can use the Gateway authentication provided there.
Clone the demo into a separate app directory, install its dependencies, and copy the environment template. Keep credentials on the server and out of source control.
AUTH_SECRET, POSTGRES_URL, AI_GATEWAY_API_KEY, and POLAR_ACCESS_TOKEN, and set POLAR_SERVER=sandbox. Then initialize the database and start the application using the repository’s scripts.
http://localhost:3000 and register a test user. Successful startup verifies the application configuration, but billing still needs the checks below.
Understand the data flow
The chat route calls the billing-wrapped Gateway model. The Narev SDK middleware turns the reported generation cost and usage into an event, and the Polar destination sends it asllm_usage. The route attaches providerOptions['ai-billing-tags'], including a userId obtained from the session.
The signup flow creates a Polar customer with externalId equal to the application’s user ID. The destination uses externalCustomerIdKey: 'userId', which connects subsequent events to that customer. Inspect lib/ai/billing.ts, lib/polar-client.ts, and the chat route when adapting the demo. Together they establish the generation, destination, and customer identity contract.
This is application-side metering. The demo does not need a hosted Narev Cloud connection dashboard to send Polar events. If you later integrate a direct model provider, use the corresponding Narev SDK middleware and its price resolver requirements.
Configure the meter deliberately
KeepPOLAR_SERVER and the token in the same environment. The demo defaults to sandbox, and its billing wrapper returns the unwrapped model when no Polar destination is configured. That means a working chat without the token can produce answers while sending no billing events. Restart the server after changing this configuration because the middleware is initialized and cached.
Configure the meter to filter llm_usage and sum the event metadata field credits_consumed. In the demo’s @ai-billing/polar 0.1.5 destination, that field contains cost in base currency units, with cost_currency alongside it. For USD, a $0.002 cost is 0.002, not 2,000,000 nanos. Confirm this against the installed destination and an actual event before setting the product price. The Polar integration guide covers the customer mapping, but its nanos wording does not describe this package version.
The demo’s event destination is only one part of billing. Confirm that your test customer has the product or subscription that consumes the meter. Decide separately whether you charge raw inference cost, apply a markup, or include usage within an allowance.
Verify success
Register a new test user and find the corresponding Polar customer. ItsexternalId should equal that user’s application ID. Then send a short chat message, let the stream finish, and locate the llm_usage event in the same Polar sandbox. Compare the event’s customer identity and model context with the server-side request.
Check that the meter accepts the event and its aggregate increases by the expected cost amount. Repeat a request to confirm accumulation, then inspect the test bill or charge preview for the associated product. Seeing an event in Polar establishes delivery; seeing the expected metered charge establishes the wider billing configuration.
If events are missing, inspect the server token, selected Polar environment, and wrapped-model path. If events exist but do not map to a customer, compare the session userId, destination key, and customer externalId exactly. A user created before Polar was configured may need a corresponding customer record; verify that record instead of creating duplicate users to work around the mismatch.