Provenance

Billing

Provenance has two deliberately different billing models, one per product — because the two buyers pay in completely different ways.

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.

FeatureWhat countsDeveloper / monthGrowth+
Webhook endpoints
webhook_endpoint
Create an endpoint3Unlimited
Step-up approvals
approval
Approve or deny in the inbox25Unlimited
Delegation mandates
mandate
Grant a mandate10Unlimited
Decision receipts
receipt
Fetch a signed receipt25Unlimited
Shadow policies
shadow_policy
Start a shadow run5Unlimited
Canary policy rules
canary_rule
Save a policy with canary conditions5Unlimited
Slack delivery
slack
Connect or test Slack5Unlimited
Two-sided agreements
agreement
Create an agreement10Unlimited
Approver network
approver_link
Invite an approver or route an approval5Unlimited
Clinical orders
order
Create or authorize an order10Unlimited
Prescriber verification
prescriber
Register or re-verify a prescriber5Unlimited
Embeds (capture / sign / order)
embed_token
Mint an embed token25Unlimited
Neuron disclosure grants
trace_disclosure_grant
Grant a disclosure request (auto or approved)10Unlimited
Account canary sets
canary_set
Create an account canary set1Unlimited

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:

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.