Skip to main content
Lago

Lago's plans + metering + entitlements → the Naive customer-billing primitive

Lago is a trademark of its respective owner, used here for identification and migration comparison only. No endorsement, partnership, or affiliation is implied.
Lago is an open-source metering and usage-based billing platform: you define billable metrics, attach them to plans, subscribe customers, then ingest usage events so Lago can aggregate consumption, enforce entitlements, and turn it all into invoices. It does billing extremely well — real-time metering, subscription management, entitlements, payment orchestration across Stripe/Adyen/GoCardless, and revenue analytics. But when the customer you are billing is really an agent that also holds cards, an inbox, a phone number, and a KYC’d identity, a Lago account is a disconnected vendor account:
  • Metering lives behind a Lago API key, and each customer is a Lago customer record — an identity that exists only inside Lago, unrelated to where the same agent’s cards, email, and permissions live.
  • Whether the agent’s plan actually lets it use a capability is an entitlement you configure in Lago and then re-enforce yourself at the call site — Lago meters and can flag limits, but the primitive access it is “entitling” is governed by a different system.
  • “Which plan is this agent on, what has it used this period, and is it allowed to do this right now?” is answered in Lago for usage, and in your own permission code for access — two sources of truth for one decision.
Naive’s customer-billing primitive gives you the same control-plane — define plans, subscribe tenants, meter usage, read a usage rollup — but the plan is the same object that governs the agent:
  • A Naive plan maps a key to an Account Kit (what the agent may do) plus per-primitive quotas (how much). Subscribing a tenant assigns that kit — so plan tier → permissions → quota is one call, not a Lago config plus separate entitlement code.
  • Usage is metered automatically on the primitive call itself — no event pipeline to build, secure, and de-duplicate. When a tenant exceeds a quota, the next call is refused with 429 rate_limited at execution time.
  • Every metered call lands in the same per-user activity log as that agent’s card spend, email sends, and KYC events — one accountability trail.
Tested against: the Lago Node client lago-javascript-client v1.48.0 (June 2026), against the Lago REST API (base https://api.getlago.com/api/v1, Authorization: Bearer, /billable_metrics, /plans, /customers, /subscriptions, /events, /customers/{id}/current_usage endpoints, docs snapshot July 2026), and the Naive Node SDK @usenaive-sdk/server against the Naive API (base https://api.usenaive.ai/v1, /v1/plans + /v1/users/:id/billing/* endpoints, docs snapshot July 2026).Version notes:
  • Scope differs. Lago bills any SaaS customer. Naive customer-billing bills a tenant user — the same handle (naive.forUser(id)) that reaches the agent’s cards, email, phone, vault, and KYC.
  • Naive does NOT own the charge. Naive owns plan → permissions → quota → usage. Your app keeps its Stripe charge (Naive plans carry an optional stripePriceId to link the two). If you rely on Lago for invoice generation, payment collection, coupons, wallets, or complex pricing, that stays with Lago/Stripe — see what doesn’t map yet.
  • Metering is built-in, not custom. Lago meters any code you define with any aggregation. Naive meters a fixed, documented set of primitives (seo, aeo, email) as a per-call count — you do not define custom billable metrics. See gaps.
  • Ingestion is automatic. Lago requires you to POST /events on every billable action. Naive records one usage event per successful metered primitive call — there is no ingestion endpoint to call.

Concept map

Before / after: the core path

The path that matters for a usage-billed agent product is define a plan → subscribe a customer → meter their usage → enforce the quota. Here it is on both platforms.
The control-plane shape lines up closely. The real differences to plan for:
  • The plan carries permissions. A Naive plan references an accountKitId; subscribing assigns it. There is no separate “entitlement” object to keep in sync — the plan is the entitlement.
  • No event ingestion. Delete your events.createEvent(...) calls. Naive meters seo, aeo, and email on the primitive call itself; you never post usage.
  • Quotas are caps, not charges. Lago prices each metered unit; Naive quotas are per-period call limits that return 429 when exceeded. Pricing/collection stays in your Stripe.
  • Usage rollup is per tenant. billing.usage() returns the tenant’s quotas + consumption for the current period — the analogue of findCustomerCurrentUsage.

Minimal viable migration

The smallest swap that keeps a working, metered product running is plan → subscribe → let metering run.
1

Install the SDK and set your key

Set NAIVE_API_KEY (a server-side key from the dashboard).
2

Recreate your plans as Account Kit + quotas

For each Lago plan, create an Account Kit enabling the primitives that tier may use, then naive.plans.upsert({ key, name, accountKitId, quotas, period }). Map your Lago plan code → Naive plan key, and your metered limits → quotas (per-primitive call caps). Carry your Stripe price across via the optional stripePriceId.
3

Reflect subscriptions from your Stripe webhook

Replace subscriptions.createSubscription(...) with naive.forUser(tenantId).billing.setSubscription({ planKey, status, stripeCustomerId, stripeSubscriptionId, currentPeriodEnd }). Drive it from the same Stripe webhook you already run. assignKit defaults to true, so the plan’s Account Kit is applied in the same call.
4

Delete the event ingestion path

Remove your events.createEvent(...) calls for the metered primitives. Naive records usage automatically on each successful seo, aeo, and email call — there is no ingestion endpoint.
5

Swap the usage read

Map customers.findCustomerCurrentUsage(id, ...)naive.forUser(id).billing.usage() for the current-period rollup + quotas.
6

Keep Stripe for the charge, then ship

Your app still owns invoicing/payment through Stripe (Naive doesn’t charge). At this point the metering + entitlement layer is off Lago. Everything below is upside.

Consolidate further once you’re on Naive

This is where the migration pays for itself. In Lago, a plan meters usage and (via entitlements) can describe access — but the primitives it entitles are governed elsewhere, so “billing plan” and “permission set” are two systems you keep in sync. On Naive, the plan is the Account Kit: the object that bills the customer is the object that governs their agent.

Gain #1 — one identity across billing and capability

  • With Lago, a customer is a billing record; the agent’s cards, email, phone, and permissions live in other systems. With Naive, naive.forUser(acme.id) is a single handle to the agent’s plan and primitives and permissions and audit trail — no separate customer record to reconcile.
  • Downgrading a customer’s plan re-assigns their Account Kit, which immediately changes what their agent may do — no second write to a permission system, no drift between “what they pay for” and “what they can touch”.

Gain #2 — execution-time permission enforcement

  • A plan’s accountKitId decides which primitives the agent may use, and quotas cap how much — both enforced at the moment of the call, not reconciled later on an invoice.
  • The agent’s code path stays the same for every tier. A tenant whose plan doesn’t enable seo is refused with forbidden; a tenant over its aeo quota is refused with 429 rate_limited — with no code change on your side:
  • In Lago, exceeding a metered limit typically means an overage charge reconciled on the next invoice; on Naive the quota is a hard, execution-time gate. The two models are complementary — keep Stripe for the money, let Naive stop the call.

Gain #3 — unified accountability

  • Every metered call records the acting agent and lands in one per-user activity log — alongside that customer’s card spend, email sends, and KYC events, not in a separate Lago usage stream:
  • That is the question that is hard to answer when usage lives in Lago, permissions live in your code, and the agent’s spend lives in Stripe. Under Naive it is a single query.

What does not map yet

A migration guide that hides gaps is worse than none. The metering + entitlement + quota layer maps cleanly and fuses with the agent’s permission model — but Lago is a full billing platform, and several of its capabilities have no equivalent on Naive’s customer-billing primitive today. Naive is explicit that it owns plan → permissions → quota → usage, not the charge. Check this list against your app before you commit.
The biggest thing to weigh is scope, not method. Lago is an end-to-end billing platform: metering and invoicing and payment collection and pricing. Naive’s customer-billing primitive deliberately owns only plan → permissions → quota → usage — it does not issue invoices or collect payment, and it meters a fixed set of primitives (seo, aeo, email) as counts rather than arbitrary priced metrics. The right migration is to move the entitlement + metering + quota-enforcement layer onto Naive (so it is fused with the identity that governs the agent) while keeping Stripe for the actual charge. If Lago is load-bearing for invoicing, prepaid wallets, coupons, complex per-unit pricing, or custom billable metrics, keep that part of Lago/Stripe and adopt Naive for the permission-and-quota plane — don’t expect a full billing-stack replacement today.

Where to go next

  • Customer Billing primitive — plans → Account Kits, per-tenant subscriptions, metered usage, and quota enforcement
  • billing SDK sub-client — typed plans.upsert / setSubscription / usage
  • Account Kits — the permission object a plan assigns, enforced per user at execution time
  • Tenant users — the identity a subscription, usage, cards, email, and KYC all hang off
  • Billing & Credits — your own Naive subscription (distinct from billing your customers)
  • Logs — the unified per-user activity trail every metered call lands in