Skip to main content

Plans & Billing

Customer-facing endpoints for viewing plans, managing a subscription, and reading billing history. All endpoints are brand-scoped — every request is resolved to your brand via session cookie or brand-scoped PAT, and only ever returns your own brand's data.

Base path: /api/v1/dashboard/plans

Authentication: dashboard session or brand-scoped PAT (the standard OPVS brand auth). See Authentication.

GET endpoints support format negotiation — append ?format=yaml or ?format=md for compact, agent-friendly output.

View your subscriptions

GET /api/v1/dashboard/plans/me

Returns your active / trialing / past-due subscriptions, each with per-service monthly entitlement bars (cap = your monthly allowance, used = current usage). A cancel_at_period_end flag drives the "cancelling on …" state.

Browse what you can buy

GET /api/v1/dashboard/plans/browse           # tariffs (with annual pricing + savings)
GET /api/v1/dashboard/plans/browse/bundles # multi-product discount bundles

is_current marks tariffs you already subscribe to. stripe_annual_price_id being present means annual billing is available for that tariff.

Billing history

GET  /api/v1/dashboard/plans/invoices        # your last 20 Stripe invoices
POST /api/v1/dashboard/plans/billing-portal # → { url } to the Stripe Billing Portal

Subscribe & change plan

POST /api/v1/dashboard/plans/upgrade-preview # proration preview (no commit)
POST /api/v1/dashboard/plans/subscribe # → { checkout_url } (Stripe Checkout)
POST /api/v1/dashboard/plans/change # switch tier / interval in place
POST /api/v1/dashboard/plans/cancel # schedule cancel at period end
POST /api/v1/dashboard/plans/resume # undo a scheduled cancel

subscribe body:

{
"tariff_version_id": 10,
"billing_interval": "month",
"success_url": "https://app.opvs.ai/dashboard/plans?ok=1",
"cancel_url": "https://app.opvs.ai/dashboard/plans"
}

success_url / cancel_url must point at an allowed dashboard origin (else 400).

Status codes

CodeWhen
404Unknown tariff_version_id, or no active subscription to change/cancel/resume
409Annual billing requested but not offered · resume when nothing is cancelling · already subscribed to this product (use /change or /resume instead of a second subscription)
429A concurrent plan change for the same item is already in flight

cancel and resume schedule the change for the end of the current billing period — you keep access until then, and resume un-cancels cleanly.

Recent denials

GET /api/v1/dashboard/plans/recent-denies

Your recently denied requests (last 30 days) when a rate, cost, or quota limit was hit — service_type, reason_code, retry_after_s, and the usage-vs-limit that triggered it. Empty until usage enforcement is active for your services.