Skip to content

Metering and invoicing

Read this to know where your bill comes from and where to find it. There is no billing screen of ours; there is a pipeline, and Stripe’s hosted portal at the end of it.

the metering agent → the time-series store → the rating service → metering Worker → the ledger database ledger → Stripe
meters stores rates carries records invoices
what runs the series per flavour the total it you

Every step is an the cloud platform component doing its own job. Our own service computes nothing money-related — it reads the rating service’s rated total per project, writes it to a ledger, and tells Stripe. That is deliberate: the moment we start arithmetic on money, we own a class of bug we do not have to own.

StepWhat it does
the metering agentMeters what runs — instance existence, by flavour, over time
the time-series storeStores the time series
the rating serviceRates the series against per-flavour prices from the catalog. Reachable at rating.shelfcs.com
metering WorkerCron, hourly. Reads the rating service’s rated total per project
the ledger database ledgerRecords every period, whether or not it billed
StripeHolds the subscription, sums the meter, sends the invoice

Hourly. A edge service on a cron trigger, not a daemon — nothing this team runs lives on its own machines.

Each tick reads the rating service’s /v2/summary for every project it meters, writes each period to the ledger, and emits the unemitted ones to Stripe. A period that fails to emit is retried on the next tick, because the ledger records it as unemitted rather than losing it.

At most a handful of periods are emitted per tick, so a long backlog drains over several hours rather than in one.01 EUR per unit, monthly, metered | | Customer mapping | By stripe_customer_id in the event payload | | Collection | send_invoice, 30 days to pay |

The value is sent in cents because the Stripe meter payload takes an integer string. A rated amount under half a cent for the period rounds down to 0; that period is still reported — a zero value marks it seen — it simply bills for nothing.

Card-on-file collection is a later phase. Today you receive an invoice.

A the identity service project and its Stripe customer share a name, carried in the Stripe customer’s metadata["tenant"]:

cust-<your user id>

The metering service reads that mapping and never writes it. It is written once, at signup, by the storefront. This is the whole of the identity plumbing between your cloud resources and your bill — if that metadata is wrong, usage bills to the wrong customer, which is why nothing else is allowed to touch it.

Stripe’s hosted Customer Portal. Ask the storefront for the URL:

GET /v1/billing/portal → { "url": "https://billing.stripe.com/..." }

It requires a session and an onboarded account, and the link it returns is short-lived. The portal shows usage and invoices, and it is where a payment method is managed.

There is no billing UI of ours, and no billing API of ours beyond that one call. There is no cost-explorer, no budget alert, no per-resource cost breakdown, and no way to see today’s accrued charge before the hour ticks.

A subscription on the metered price. Without one there is nothing for a meter event to attach to and the usage is recorded but never invoiced. POST /v1/onboard creates it, and finds an existing one before creating — “existing” being judged over every subscription status except canceled and incomplete_expired, so a past_due invoice is still your one subscription and not grounds for a second.

[!warning]

Billing is in Stripe TEST mode. The meter, product and price created on 2026-09-03 are test-account objects, and the live ids replace them when billing goes live. Nothing invoiced today is a real charge. Do not treat a test invoice as evidence that live billing works.

Compute, by flavour, per hour. Nothing else — see How prices are set for the full table of what is rated and what is not. Storage, snapshots, egress and addresses are all currently unbilled.