Billing
Provenance has two deliberately different billing models, one per product — because the two buyers pay in completely different ways.
- Authorize is self-serve SaaS: sign up, get a free Developer key, and upgrade to metered Growth with a card when you outgrow it.
- Attest is enterprise: carriers are on annual contracts or usage-based InsurTech pricing, billed by invoice — never self-serve checkout.
Billing runs through Stripe in either test or live mode — a deployment switches between them with one setting, and the dashboard says which one you're in (a Test mode banner with card 4242 4242 4242 4242 means no real money moves). Billing is also fully optional: when Stripe isn't configured on a deployment, signup still works and every Stripe-dependent path returns billing_not_configured (503) instead of failing silently.
Authorize metering
Every POST /v1/intent/verify writes one row to a persistent usage ledger — independent of the rate limiter. That ledger is the source of truth for both metering and enforcement.
| Plan | Price | Included | Metered to Stripe | | --- | --- | --- | --- | | Developer | Free | 50 verifications / calendar month | No | | Growth | metered (below) | — | Yes | | Enterprise | Custom | — | No (reconciled manually) |
For Growth keys, each billable event is reported to a Stripe Billing Meter asynchronously — we never block your API response on Stripe. Every report carries a stable identifier derived from its ledger row, so a retry can never bill you twice; anything Stripe didn't accept is re-sent by a reconcile worker every few minutes, so nothing is lost either. Developer usage is never sent to Stripe, and Enterprise keys write ledger rows for internal reconciliation but are never metered.
How Growth is billed
Your Growth subscription has one metered price per event below (and per Trace event further down). There's no base fee: at the end of each monthly billing period Stripe invoices the sum of each event's count × its unit price, charged to the card on file. Trace events with an included allowance only bill the overage.
Metered units (Growth)
| Event | Unit price |
| --- | --- |
| intent_verify | $0.004 |
| approval_resolved (step-up) | $0.05 |
| network_approval_resolved (approver network) | $0.15 |
| media_screen | $0.02 |
| agreement_completed | $0.25 |
| order_authorized (clinical order) | $0.25 |
| evidence_export | $5.00 |
Never metered, on every tier: JWKS / offline verification and revocation + /v1/intent/status — the trust primitives are always free.
Approver network: a network_approval_resolved is billed when an approval you route through the approver network is decided — a premium over in-house approval_resolved for the shared, always-enrolled approver base. The integrator pays; the approver never does.
Plan gating
Enterprise-only surfaces (portfolio analytics, batch verification, evidence exports, SSO/teams, Policy v2 constructs, custom case types, Trace registry export) return HTTP 402 plan_required with an upgrade pointer below Enterprise. Trust primitives are never gated.
Developer allowances
Growth features are not locked on the Developer plan — each one has a free monthly allowance. Only the action counts (creating, deciding, minting); every read, list, revoke, disconnect and decline is free on every plan. Growth and Enterprise are unlimited and never touch the counter.
| Feature | What counts | Developer / month | Growth+ |
|---|---|---|---|
| Webhook endpoints webhook_endpoint | Create an endpoint | 3 | Unlimited |
| Step-up approvals approval | Approve or deny in the inbox | 25 | Unlimited |
| Delegation mandates mandate | Grant a mandate | 10 | Unlimited |
| Decision receipts receipt | Fetch a signed receipt | 25 | Unlimited |
| Shadow policies shadow_policy | Start a shadow run | 5 | Unlimited |
| Canary policy rules canary_rule | Save a policy with canary conditions | 5 | Unlimited |
| Slack delivery slack | Connect or test Slack | 5 | Unlimited |
| Two-sided agreements agreement | Create an agreement | 10 | Unlimited |
| Approver network approver_link | Invite an approver or route an approval | 5 | Unlimited |
| Clinical orders order | Create or authorize an order | 10 | Unlimited |
| Prescriber verification prescriber | Register or re-verify a prescriber | 5 | Unlimited |
| Embeds (capture / sign / order) embed_token | Mint an embed token | 25 | Unlimited |
| Neuron disclosure grants trace_disclosure_grant | Grant a disclosure request (auto or approved) | 10 | Unlimited |
| Account canary sets canary_set | Create an account canary set | 1 | Unlimited |
When an allowance is used up, the next action returns HTTP 402 usage_limit_reached, the same code as the verification cap, with the feature, count and reset date in the message and a structured error.trial object:
{
"error": {
"type": "billing_error",
"code": "usage_limit_reached",
"message": "You've used 3 of 3 free webhook_endpoint actions this month on the Developer plan. The allowance resets 2026-11-01; upgrade to Growth for unlimited use.",
"trial": { "feature": "webhook_endpoint", "used": 3, "limit": 3, "resets_at": "2026-11-01T00:00:00.000Z" },
"doc_url": "https://provenance.io/docs/billing#upgrade"
}
}
GET /v1/account/usage returns a trial array (feature, used, limit, resets_at; limit is null on Growth+), and the dashboard's Account page shows it as usage bars. Allowances reset on the 1st of the month (UTC), are counted per account (dashboard sessions and API keys share one counter), are never sent to Stripe, and are env-tunable per feature (TRIAL_LIMIT_WEBHOOK_ENDPOINT, TRIAL_LIMIT_APPROVAL, … — 0 turns a trial off). Metered events behind an allowance (approval_resolved, agreement_completed, order_authorized) are still recorded in the usage ledger as usual.
Spend limits (Growth)
Set a monthly cap in cents with PUT /v1/account/spend-limit (or the account portal). Spend is estimated from the usage ledger (price map × counts) — no Stripe round-trip. At 80% we log a warning; at 100%, metered calls are rejected with HTTP 402 spend_limit_exceeded until you raise the limit or the month resets. GET /v1/account/usage returns the per-type breakdown, estimate, and current limit.
The Developer cap and upgrading
When a Developer key reaches its 50 included verifications for the current calendar month, further POST /v1/intent/verify calls are rejected:
{
"error": {
"type": "billing_error",
"code": "usage_limit_reached",
"message": "Monthly included usage exceeded. Upgrade to Growth to continue.",
"doc_url": "https://provenance.io/docs/billing#upgrade"
}
}
That's HTTP 402. Growth and Enterprise keys are uncapped at the API level — volume is absorbed by metering and invoicing. The cap resets at the first instant of the next calendar month. (The default free-tier allowance is env-tunable via DEVELOPER_INCLUDED_UNITS; existing keys keep the allowance they were minted with.)
To upgrade, open the dashboard's Account → Plan & billing card and choose Upgrade to Growth (owners and admins only). That calls POST /v1/account/upgrade-session, which returns a Stripe Checkout URL subscribing your account to every metered Growth price. (With an account API key you can call POST /v1/accounts/upgrade-session instead.) When Stripe confirms the payment it calls our webhook (checkout.session.completed), which flips every key on your account to growth. In test mode pay with card 4242 4242 4242 4242.
Managing your subscription
Manage billing on the same card (POST /v1/account/billing-portal) opens Stripe's Customer Portal: update the card, download invoices, or cancel. GET /v1/account/billing returns the plan, the active Stripe mode, the subscription status and whether a payment is past due.
| What happens in Stripe | What happens to your account |
| --- | --- |
| Subscription active / trialing | Growth |
| Payment fails (past_due) | Stays Growth while Stripe retries; the account owner gets an email and the dashboard shows a warning |
| Invoice paid after a failure | The past-due warning clears |
| Cancelled, unpaid, or incomplete_expired | Every key drops to Developer, with the Developer included units restored |
Cancelling takes effect when Stripe ends the subscription (immediately or at period end, per your portal choice); usage up to that point is invoiced as normal.
Trace metering
Trace events carry a monthly included allowance per plan. A Developer key is hard-capped at its allowance (the next call returns usage_limit_reached, 402); a Growth key gets the allowance free and pays the metered price only for the overage — Stripe is only ever sent the units beyond the allowance. Enterprise is contract-invoiced.
| Event | Unit | Developer (included, capped) | Growth (included, then) | Price |
| --- | --- | --- | --- | --- |
| traced_inference_1k_tokens | 1k prompt + completion tokens | 50 (50k tokens) | 250 (250k tokens) | $0.50 |
| trace_graph | one attribution graph | 25 | 100 | $0.25 |
| trace_canary_read | one canary re-run or sidecar reading | 500 | 5,000 | $0.002 |
| trace_diff | one Neuron-CI diff | n/a | Enterprise only | $25.00 |
Free on every tier: reading the public neuron registry. Bulk registry export is Enterprise. Allowances are env-tunable (TRACE_INCLUDED_*), and a usage row records the full quantity (a 2,100-token completion records 3 units).
Attest contract billing
Carriers are anchored by carrier_id throughout, with a contract_type that is completely separate from the Authorize plan:
- Usage-based (InsurTech, $0.35/claim). A monthly job aggregates the prior month's
claims_attestusage per carrier and creates a finalized Stripe Invoice — invoice only, no card capture. In test mode Stripe sends no real email; the finalized invoice's hosted URL is the artifact. Run it withpnpm --filter api billing:invoice-month(in live mode it creates real invoices and requires--confirm-live). - Flat-annual. No Stripe object at all — billing is a manual finance process outside this system. Instead of enforcement we offer visibility:
GET /v1/admin/carriers/:carrier_id/usage-vs-contractreturns year-to-date attested volume as a percentage of the contract estimate, so overages surface as a renewal conversation rather than a silent cost.
Reconciling Enterprise-Authorize and flat-annual-Attest usage against invoiced contracts is a manual process in the current release — the ledger records it, but only Growth metering and InsurTech monthly invoicing are auto-billed.
Configuration
Stripe setup for a self-hosted deployment: two credential sets live side by side (STRIPE_TEST_* and STRIPE_LIVE_*) and STRIPE_MODE=test|live (default test) picks one. Run pnpm --filter @provenance/api stripe:bootstrap --mode test (live: --mode live --confirm-live) to create the "Provenance Growth" product plus a meter and metered price per event — it's idempotent and prints the env lines to paste. Register /v1/webhooks/stripe as a webhook endpoint in both Stripe modes; events from the inactive mode are acknowledged and ignored. We recommend a restricted key (rk_…). The API refuses to boot with a key in the wrong slot, and refuses STRIPE_MODE=live unless the deployment is API_ENVIRONMENT=live with the full live set configured. GET /health/deep reports stripe as live, test, misconfigured: <VAR> or not_configured.