# MeterBox > The monetization platform for the AI era. ## Features - **Isolated by design** (`{ affinity: isolated }`): Single-tenant SaaS architecture — your organization gets its own dedicated environment and storage. Your data is kept separate from other customers, with no shared databases and no competition for resources. This provides strong security, reliable performance, and built-in isolation for compliance and data governance needs. - **Real-time metering** (`POST /v1/meter`): Ingest token usage events via REST or batch. Each event writes the in-cluster ledger — the authoritative meter — deduplicated on `transaction_id` and recorded without ever blocking your inference path. Late events backfill into the right historical period and auto-reconcile at the next close. - **Ingest from anywhere** (`POST /v1/meter/cloudevents`): Point your existing event stream at MeterBox without a translation layer: CloudEvents 1.0 (binary, structured, or batched), a Segment destination, an Airbyte destination, a long-lived Kafka consumer, or S3/GCS/Azure bucket sweeps can all be mapped directly to meter events through dotted-path field mapping, with the same deduplication and idempotency guarantees as the native API. - **AI-native pricing** (`model_versions + latency_sla`): Price what AI actually does: `per-model_version` price bands (charge claude-3.7 differently from claude-3.5), latency-SLA premiums for sub-500ms responses, custom billing dimensions (`gpu_seconds`, `training_cycle_seconds`, `storage_gb`) with their own tier ladders, and dimensional plan-price overrides by region, deployment tier, or any key you define. - **Outcome-based pricing** (`POST /v1/meter/outcomes`): The top of the pricing ladder: claim a billable outcome ("ticket resolved", "campaign sent") priced from your catalog — it bills only after you confirm it, or when its verification window closes undisputed. Disputed claims void without charge. Solves the attribution problem that keeps outcome pricing rare. - **Entitlement engine** (`GET /v1/entitlements/:id`): Sub-millisecond quota checks from an in-memory cache. Model access gates, plan feature flags, configuration entitlements (per-plan scalar values like `support_sla_hours`), grace periods between soft-alert and hard-block, overage policies (block, throttle, or bill), JWT tokens embedding full entitlement state, and an in-process Node SDK (`@meterbox/entitlement-sidecar`) with stale-while-error so your gateway stays live through API outages. - **Tamper-Proof Ledger** (`GET /v1/ledger/:id`): Every usage event, subscription charge, overage, credit, and discount becomes a ledger entry, and each closed billing period is sealed with a verifiable SHA-256 digest — so a finalized period that was altered is detectable (SOC 1 ICFR ledger immutability). Monthly statements with day-by-day running balances and per-model breakdowns. Revenue recognition (ASC 606 / GAAP) derives deferred vs recognized revenue straight off the same ledger. - **Agent wallets** (`POST /v1/wallets/:id/reserve`): Per-agent (sub-customer) credit wallets with their own balance, spending limits, and budget caps — manage each AI agent's budget independently under a single account. Credits are temporarily set aside before use and then either charged or returned, preventing overspending even when multiple requests happen at the same time. Unused reservations are automatically released if a client disconnects or fails to complete a request. - **Credits, Caps & dunning** (`POST /v1/customers/:id/credits`): Promotional and paid credit grants with priority and expiry ordering, balance-low webhooks, off-session auto-recharge, and credit notes for finalized invoices. Per-customer spend caps return HTTP 402 before a runaway agent can burn through the budget, and a configurable dunning policy moves past-due customers from retries to escalation. - **Trials & win-back** (`POST /v1/customers/:id/trial`): Trial entitlements grant access to a richer plan for a specified number of days without modifying the underlying subscription. This includes reverse trials, which begin with full access and subsequently step down to the original plan. The cancellation flow presents plan-configured retention offers (for example, discounts, trial extensions, or downgrades), allowing the churn screen to attempt user retention before processing the cancellation. - **Discounts** (`POST /v1/coupons`): Percentage-off and fixed-amount coupons with once, repeating, or forever durations. Plan restrictions, max redemptions, expiry dates, and automatic promotion code sync with Stripe. - **Pricing intelligence** (`GET /v1/dashboard/pricing-insights`): A COGS-aware margin view (revenue vs. provider cost by model), pricing simulations that replay real usage against a draft price book, NRR and GRR retention cohorts, deterministic A/B pricing experiments, and market packaging benchmarks alongside your own catalog. - **Bring your own billing provider** (`PUT /v1/settings/billing`): Stripe and Zuora out of the box, or Custom Invoicing for a BYO-ERP flow where your finance system owns issuance and collections against MeterBox-computed totals. Per-customer billing profiles let you override provider, currency, payment terms, and invoice numbering for tailored contracts—supporting seat-based billing, contract commitments with minimum-spend true-ups, and localized pricing by country and currency. - **Quota alerting & durable webhooks** (`GET /v1/alerts/:id`): Configurable alerts can be triggered at 80%, 90%, 100%, or custom absolute token thresholds, with per-period deduplication to prevent duplicate notifications. Events are delivered via HTTP POST or AWS SQS with at-least-once delivery guarantees, exponential backoff retries, and a replayable delivery log, ensuring that downstream outages never result in lost events. - **Billing-ops audit trail** (`GET /v1/audit`): Every plan, customer, and credit mutation is captured in a queryable audit trail with before/after diffs and actor attribution—including per-individual operator attribution for shared admin keys. CLI and MCP clients forward `X-Operator-Id` from `METERBOX_OPERATOR_ID`, ensuring every action is attributable to a specific operator. Audit records are protected by a tamper-evident SHA-256 hash chain. Filter by event, target, and time, explore activity in the dashboard, or export to your SIEM. - **Granular RBAC** (`PUT /cp/rbac/matrix`): Beyond the built-in Owner/Admin/Member roles, each organization defines a configurable capability matrix that authorizes privileged actions independently. This enables fine-grained delegation—for example, billing operations staff can issue account credits without also receiving permissions to publish plans. Capabilities are assigned individually rather than inherited from broad roles, while Owners always retain unrestricted access to all capabilities. - **Period-end close** (`POST /v1/billing/close`): Automated billing close: we read usage from the in-cluster ledger, calculate overage, add Stripe line items, finalize invoices, mark ledger entries paid, and check coupon durations. Opt-in finance approval gate holds invoices at ready-to-issue until a human signs off. - **Tax engine** (`POST /v1/tax/quote`): Flexible tax handling for invoices and credit notes: use built-in jurisdiction rates for on-prem and air-gapped deployments, or connect Avalara or Anrok for automated tax calculation. A single quote endpoint lets you preview exactly what will be applied before closing, and credit notes automatically handle both net and tax amounts. - **Accounting export** (`GET /v1/accounting/journals`): Keep your ledger as the source of truth. Invoices, payments, credit notes, and tax entries are projected into your general ledger through an NDJSON stream or NetSuite/Xero import CSVs, mapped to your accounting structure. Lazy projection means no duplicated state and no sync drift. - **Revenue anomaly detection** (`GET /v1/anomalies`): An hourly scan of the ledger identifies spend spikes (comparing 24-hour cost against the trailing median), churn spikes, and aggregation drift—such as customers whose integration has silently broken. Results are deduplicated daily, surfaced in the pricing insights dashboard, and trigger an `anomaly.detected` webhook on first detection. - **Marketplace bridges** (`/v1/integrations/*-marketplace`): Sell through AWS, GCP, and Azure Marketplaces without giving MeterBox access to your cloud credentials. Lightweight entitlement bridges operate in records-only mode: your seller-side handler validates marketplace events and POSTs the resolved entitlement data to MeterBox. MeterBox then maps lifecycle events to customer status and makes the customer immediately available for metering. - **CLI + MCP Server** (`npm i -g @meterbox/cli`): `@meterbox/react` provides drop-in widgets such as `PricingTable`, `PlanComparison`, `SubscriptionPreview`, `CreditBalance`, `CreditGrantsTable`, `CreditUtilization`, and `UsageChart` that self-fetch via customer-portal endpoints using short-lived customer JWTs (no admin key in browser). Alternatively, `@meterbox/customer-portal` exposes a full Account/Plans/Credits portal in one component. Built on the typed `@meterbox/node` SDK. - **MeterBox UI Components + Customer Portal** (`npm i @meterbox/customer-portal`): `@meterbox/react` drops ``, ``, ``, ``, ``, ``, and `` straight into your product — each widget self-fetches via the customer-portal endpoints with a short-lived customer JWT, so no admin key in the browser. Or skip the wiring entirely with `@meterbox/customer-portal` — a multi-page Account/Plans/Credits portal in one component. Backed by `@meterbox/node`, a fully typed JavaScript SDK. ## Security & compliance - **Built for SOC 2 & FedRAMP** (`SOC 2 · FedRAMP · NIST 800-53`): Technical controls mapped to NIST 800-53 out of the box — access control, audit, session management, transport, and input validation. A compliance-ready foundation that shortens your path to an audit. - **MFA, SSO & strong auth** (`RFC 6238 · OIDC · NIST 800-63B`): TOTP multi-factor with one-time backup codes, OIDC single sign-on for Google and enterprise IdPs, bcrypt-hashed credentials, and NIST 800-63B password rules with optional breached-password screening via k-anonymity. - **Session controls** (`FedRAMP AC-11 / AC-12`): The application enforces sliding idle session timeouts with a hard absolute session lifetime in alignment with FedRAMP AC-11 and AC-12. It implements CSRF protection using the double-submit cookie pattern and secures session cookies with the `HttpOnly`, `Secure`, and `SameSite` attributes. Sessions automatically expire after a period of inactivity and are terminated upon reaching the maximum allowed session duration. Long-lived (e.g., 30-day) session tokens are not permitted. - **Two append-only audit trails** (`GET /cp/audit · /v1/audit`): User lifecycle events (signup, login, MFA, invites, role changes) land in a tamper-evident SHA-256-chained log on the control plane. A separate billing-ops trail captures plan / customer / credit mutations with before-after diffs. Both pull straight from the API with cursor pagination. - **Your data never leaves your cluster** (`Air-gapped by design`): Single-tenant by design: includes a bundled in-cluster datastore and cache, with no managed or shared database. Optional encrypted off-cluster backups (using a tenant-supplied encryption key—never ours) can be sent to your S3, GCS, or Azure storage on a schedule you choose. Air-gap deployable for regulated and sovereign environments. - **Hardened by default** (`OWASP · CSP · HSTS`): The OWASP secure headers baseline is applied to every response, including strict `Content-Security-Policy` with violation reporting, HSTS, and cross-origin isolation. Defense in depth is built in from day one, not bolted on after launch. ## How it works - **01. Define your config**: Set up your AI models, pricing per 1k tokens, plan limits, and feature flags. The platform validates it on startup. - **02. Your config is provisioned immediately**: We wire up everything for you: the model catalog and pricing tiers, Stripe products and prices, and seed coupons. - **03. Start metering usage**: At each usage event, fire POST /v1/meter with the token counts. Get back an entitlement decision, a cost estimate, and current usage percentages. - **04. Close billing cycle**: A single scheduled job orchestrates the complete period-end close, including ledger usage reads, overage computation, Stripe invoice generation, ledger updates, and coupon tick processing. ## Deployment tiers - **Cloud** — Same solution, same APIs, run by MeterBox in shared infra. - For: SaaS teams that want the platform without maintaining the cluster. - Deployment: MeterBox-operated cluster, multi-tenant control plane + per-(org, env) data-plane setup. Each tenant gets its own PVC; no row-level filters. - SLA: 99.9% uptime, 24h response on P1 - Compliance: SOC 2 Type II - **Self-Managed** — Install the same solution in your own cloud. - For: Teams that need data residency, regulatory control, or zero outbound dependencies. - Deployment: Deploy MeterBox in any conformant cloud (EKS, GKE, AKS, k3s, OpenShift, on-prem). Bundled DB + Cache; no managed services required. - SLA: Best-effort community support, paid support add-on - Compliance: HIPAA-ready · ISO 27001 path - **Managed (sovereign)** — MeterBox operates a dedicated cluster inside your sovereign cloud or region. - For: Defense, public sector, fintech, healthcare — workloads with data-sovereignty + FedRAMP / HIPAA / GDPR-grade requirements. - Deployment: Single-tenant cluster in your chosen region (AWS GovCloud, GCP Assured, Azure Government, EU-only AWS, on-prem). MeterBox SRE runs it while you still hold the keys. - SLA: 99.95% uptime, 4h response on P1, 24/7 oncall - Compliance: FedRAMP Moderate (target) · HIPAA · GDPR data-residency · ISO 27001 ## Pricing - **Starter** ($0 forever): For prototyping and solo projects. - Up to 3 customers - All core APIs - In-cluster metering - Ledger & statements - Community support - **Growth** ($149 per month): For production workloads and growing teams. - Up to 500 customers - Quota alerting (Slack/webhook) - Entitlement JWT tokens - Coupon engine - Period-end close automation - Email support + 4h SLA - **Enterprise** (Custom contract): Unlimited customers, SLA, dedicated support. - Unlimited customers - White-glove onboarding - Custom SLA - SSO & audit logs - On-prem deployment option - Dedicated Slack channel ## API reference Full machine-readable spec: [https://meterbox.ai/openapi.json](https://meterbox.ai/openapi.json) Symbols: `*` = required field. `(admin)` = requires `x-admin-key` header. ### Meta - `GET /health` — Liveness check - returns (200): `HealthResponse` ### Catalog - `GET /v1/addons` — List catalog add-ons - returns (200): `{addons*: object[]}` - `POST /v1/addons` (admin) — Create or upsert an add-on (admin) - body: `any` - returns (201): `any` - `PUT /v1/addons/{id}` (admin) — Replace an add-on (admin) - body: `any` - returns (200): `any` - `DELETE /v1/addons/{id}` (admin) — Delete an add-on (admin); attached customers keep their snapshot - `GET /v1/catalog/pricing-page` — Public, landing-page-ready pricing snapshot (no auth) - returns (200): `any` - `GET /v1/catalog/pricing-page.js` — Embeddable pricing-widget script (no auth) - `GET /v1/models` — List models with public pricing. Cursor-paginated, newest first. - query: `{before: string, limit: integer}` - returns (200): `{models*: Model[]}` - `POST /v1/models` (admin) — Create or upsert a model (admin) - body: `any` - returns (201): `any` - `PUT /v1/models/{id}` (admin) — Replace a model (admin) - body: `any` - returns (200): `any` - `DELETE /v1/models/{id}` (admin) — Delete a model (admin); refuses if any plan or customer still references it - `GET /v1/offerings/export` (admin) — Atomic full-catalog export (admin) - returns (200): `any` - `POST /v1/offerings/import` (admin) — Atomic full-catalog replace (plans + models + billing + webhooks + company) from a single JSON blob (admin) - body: `any` - returns (200): `{plans*: number, models*: number, company*: string}` - `GET /v1/plans` — List plans (with customer count + MRR). Cursor-paginated, newest first. - query: `{before: string, limit: integer}` - returns (200): `{plans*: Plan[]}` - `POST /v1/plans` (admin) — Create or upsert a plan (admin) - body: `any` - returns (201): `any` - `PUT /v1/plans/{id}` (admin) — Replace a plan (admin) - body: `any` - returns (200): `any` - `DELETE /v1/plans/{id}` (admin) — Delete a plan (admin); refuses if any customer is on it - `POST /v1/plans/{id}/publish` (admin) — Publish the working draft as the next immutable plan version (admin) - returns (201): `{plan_id*: string, version*: integer, published_at*: string}` - `GET /v1/plans/{id}/versions` (admin) — Published version history for a plan, with per-version subscriber counts (admin) - returns (200): `{plan_id*: string, versions*: object[], unpinned_subscribers*: integer}` ### Customers - `GET /v1/customers` (admin) — List all customers (admin). Cursor-paginated, newest first. - query: `{before: string, limit: integer, include_archived: string, tags: string}` - returns (200): `CustomersResponse` - `POST /v1/customers` — Provision a customer (Stripe + ledger) - body: `{name*: string, email*: string, external_id*: string, plan_id*: string, stripe_price_id: string, rate_card_id*: string, …}` - returns (201): `{customer_id*: string, plan*: string, status*: string, stripe_customer_id*: string, stripe_subscription_id*: string}` - `DELETE /v1/customers/{cid}/credits/{gid}` (admin) — Revoke an active credit grant (admin) - returns (200): `{grant_id*: string, status*: enum}` - `GET /v1/customers/{id}` — Get a customer with usage summary - returns (200): `{customer*: object, usage*: UsageSummary}` - `DELETE /v1/customers/{id}` — Cancel subscription - query: `{immediately: string}` - returns (200): `{customer_id*: string, status*: string}` - `GET /v1/customers/{id}/addons` — List a customer's attached add-ons - returns (200): `{add_ons*: object[]}` - `POST /v1/customers/{id}/addons` — Attach an add-on (snapshots it onto the subscription) - body: `{add_on_id*: string}` - returns (201): `{add_ons*: object[]}` - `DELETE /v1/customers/{id}/addons/{addOnId}` — Detach an add-on - returns (200): `{add_ons*: object[]}` - `GET /v1/customers/{id}/allowance` (admin) — Usage-allowance window + banked reset tokens (admin) - returns (200): `any` - `POST /v1/customers/{id}/allowance/reset` (admin) — Spend a banked reset → refill the window now (admin) - returns (200): `any` - `POST /v1/customers/{id}/allowance/reset-tokens` (admin) — Grant banked reset tokens (admin) - returns (200): `any` - `POST /v1/customers/{id}/archive` (admin) — Archive a customer (admin) — reversible soft-delete; does not cancel the subscription - returns (200): `{customer_id*: string, archived_at*: string}` - `GET /v1/customers/{id}/billing-profile` (admin) — Get the customer's effective billing profile (admin) - returns (200): `{customer_id*: string, effective*: object, source*: object, override*: object}` - `PATCH /v1/customers/{id}/billing-profile` (admin) — Set per-customer billing profile overrides (admin) - body: `{provider: enum, currency: string, payment_terms: enum, tax_behavior: enum, invoice_number_sequence: string, tax_region: string, …}` - returns (200): `{customer_id*: string, effective*: object, source*: object, override*: object}` - `DELETE /v1/customers/{id}/billing-profile` (admin) — Clear the customer's billing profile override (admin) - returns (200): `{customer_id*: string, effective*: object, source*: object, override*: object}` - `DELETE /v1/customers/{id}/cancellation` (admin) — Revert cancel intent (admin) - returns (200): `{customer_id*: string, reverted*: enum}` - `GET /v1/customers/{id}/cancellation` (admin) — Read the cancellation record (admin) - returns (200): `{customer_id*: string, cancellation*: object}` - `POST /v1/customers/{id}/cancellation/accept-offer` (admin) — Apply a retention offer (admin) - body: `{offer_id*: string}` - returns (200): `{customer_id*: string, cancellation*: object, offer*: object, applied*: object}` - `POST /v1/customers/{id}/cancellation/confirm` (admin) — Confirm voluntary cancellation (admin) - returns (200): `{customer_id*: string, cancellation*: object}` - `POST /v1/customers/{id}/cancellation/initiate` (admin) — Record cancel intent + return retention offers (admin) - body: `{reason_code*: enum, reason_text: string}` - returns (201): `{customer_id*: string, cancellation*: object, offers*: object[]}` - `PUT /v1/customers/{id}/contract-commit` (admin) — Set or replace the contract commit overlay (admin) - body: `{monthly_amount_usd*: number, term_months*: integer}` - returns (200): `{customer_id*: string, contract_commit*: object}` - `DELETE /v1/customers/{id}/contract-commit` (admin) — Clear the customer's contract commit overlay (admin) - returns (200): `{customer_id*: string, contract_commit*: object}` - `POST /v1/customers/{id}/coupon` — Apply a coupon to an existing subscription - body: `{code*: string}` - returns (200): `any` - `DELETE /v1/customers/{id}/coupon` — Remove the active coupon - returns (200): `any` - `POST /v1/customers/{id}/credit-notes` (admin) — Issue a credit note against a finalized invoice (admin) - body: `{amount_usd*: number, stripe_invoice_id: string, reason: enum, memo: string}` - returns (201): `{ledger_entry_id*: string, customer_id*: string, amount_usd*: number, stripe_invoice_id*: string, stripe_credit_note_id*: string, reason*: string, …}` - `GET /v1/customers/{id}/credit-notes` (admin) — List credit notes for a customer (admin) - returns (200): `{customer_id*: string, credit_notes*: object[]}` - `GET /v1/customers/{id}/credits` — List a customer's credit grants + active balance - returns (200): `{customer_id*: string, balance_usd*: number, grants*: any[]}` - `POST /v1/customers/{id}/credits` (admin) — Grant a credit to a customer (admin) - body: `{amount_usd*: number, type: enum, priority: integer, effective_at: string, expires_at: string, applies_to: object, …}` - returns (201): `{grant: any}` - `POST /v1/customers/{id}/credits/checkout` (admin) — Mint a Stripe Checkout session for a self-serve credit top-up (admin) - body: `{amount_usd*: number, success_url*: string, cancel_url*: string}` - returns (200): `{id*: string, url*: string}` - `PATCH /v1/customers/{id}/credits/settings` (admin) — Set the per-customer credit-balance low-alert threshold (admin) - body: `{credit_balance_low_threshold_usd: number, auto_recharge: object}` - returns (200): `{customer_id*: string, credit_balance_low_threshold_usd*: number}` - `GET /v1/customers/{id}/credits/timeseries` — Balance history for a customer's credit grants - query: `{from: string, to: string, granularity: string}` - returns (200): `{customer_id*: string, from*: string, to*: string, granularity*: string, points*: object[]}` - `PATCH /v1/customers/{id}/currency` (admin) — Override the customer's pinned billing currency (admin) - body: `{currency*: string}` - returns (200): `{customer_id*: string, resolved_currency*: string}` - `GET /v1/customers/{id}/draft-invoice` (admin) — Mid-period draft invoice — admin twin of the portal view - returns (200): `{customer_id*: string, period*: string, as_of*: string, elapsed_pct*: number, currency*: enum, resolved_price*: object, …}` - `POST /v1/customers/{id}/dsar/erase` (admin) — GDPR Art. 17 — irreversibly erase PII on the customer record; financial records retained (admin) - body: `{confirm*: enum}` - returns (200): `{customer_id*: string, erased_at*: string}` - `GET /v1/customers/{id}/dsar/export` (admin) — GDPR Art. 15 — export a subject's bundle (customer + ledger + credit grants) (admin) - returns (200): `{generated_at*: string, customer*: object, ledger_entries*: any[], credit_grants*: any[]}` - `POST /v1/customers/{id}/entitlements/recalculate` (admin) — Recompute effective entitlements (admin): materialize expired promo grants, reseed the metering cache, re-issue token - returns (200): `{customer_id*: string, plan*: string, features*: string[], models_allowed*: string[], limits*: object, expired_promo_grants*: integer, …}` - `GET /v1/customers/{id}/optimization` (admin) — Plan-fit view — admin twin of the portal view - returns (200): `{customer_id*: string, current_plan_id*: string, period*: string, plans*: object[], recommendation*: object}` - `GET /v1/customers/{id}/outcomes` (admin) — List claimed outcome events (admin) - returns (200): `{customer_id*: string, outcomes*: object[]}` - `POST /v1/customers/{id}/outcomes/{eventId}/confirm` (admin) — Confirm a pending outcome early (admin) - returns (200): `{id*: string, customer_id*: string, outcome_id*: string, outcome_name*: string, status*: enum, amount_usd*: number, …}` - `POST /v1/customers/{id}/outcomes/{eventId}/dispute` (admin) — Dispute a pending outcome (admin) - body: `{reason: string}` - returns (200): `{id*: string, customer_id*: string, outcome_id*: string, outcome_name*: string, status*: enum, amount_usd*: number, …}` - `POST /v1/customers/{id}/pending-lines` (admin) — Stage an ad-hoc invoice line (admin) - body: `{amount_usd*: number, description*: string}` - returns (201): `{id*: string, customer_id*: string, amount_usd*: number, description*: string, status*: string, created_at*: string, …}` - `GET /v1/customers/{id}/pending-lines` (admin) — List pending invoice lines for a customer (admin) - returns (200): `{customer_id*: string, pending_lines*: object[]}` - `DELETE /v1/customers/{id}/pending-lines/{lineId}` (admin) — Revoke a pending invoice line (admin) - returns (200): `{id*: string, customer_id*: string, amount_usd*: number, description*: string, status*: string, created_at*: string, …}` - `PATCH /v1/customers/{id}/plan` — Upgrade or downgrade a plan (immediate or scheduled for period end) - body: `{plan_id*: string, stripe_price_id: string, effective: enum}` - returns (200): `any` - `DELETE /v1/customers/{id}/plan/scheduled` — Cancel a pending scheduled plan change - returns (200): `{cancelled*: enum}` - `PATCH /v1/customers/{id}/pricing-dimensions` (admin) — Replace the customer's pricing-dimension map (admin) - body: `{pricing_dimensions*: object}` - returns (200): `{customer_id*: string, pricing_dimensions*: object}` - `GET /v1/customers/{id}/promo-entitlements` — List a customer's promotional entitlements - returns (200): `{promo_entitlements*: object[]}` - `POST /v1/customers/{id}/promo-entitlements` (admin) — Grant a free, time-bounded promotional entitlement (admin) - body: `{features: string[], models_allowed: string[], limits_delta: object, reason: string, expires_at: string}` - returns (201): `{promo_entitlement*: object, promo_entitlements*: object[]}` - `DELETE /v1/customers/{id}/promo-entitlements/{grantId}` (admin) — Revoke a promotional entitlement (admin); record kept for audit - returns (200): `{promo_entitlements*: object[]}` - `GET /v1/customers/{id}/referral` (admin) — Referral code + usage (admin) - returns (200): `any` - `POST /v1/customers/{id}/referral/redeem` (admin) — Redeem a referral code for this customer (admin) - returns (200): `any` - `PATCH /v1/customers/{id}/resource-limits` (admin) — Replace the per-resource quota map (admin); '*' key is the default cap - body: `{resource_limits*: object}` - returns (200): `{customer_id*: string, resource_limits*: object}` - `GET /v1/customers/{id}/resource-usage` — Per-resource (sub-customer/workspace) MTD usage vs configured caps - returns (200): `{customer_id*: string, period*: string, resource_limits*: object, default_limit*: object, resources*: object[]}` - `PATCH /v1/customers/{id}/seats` (admin) — Update the customer's seat count (admin) - body: `{seat_count*: integer}` - returns (200): `{customer_id*: string, seat_count*: integer, pending_charge_usd*: number}` - `PUT /v1/customers/{id}/spend-cap` (admin) — Set or replace the per-customer spend cap (admin) - body: `{spend_cap_usd*: number}` - returns (200): `{customer_id*: string, spend_cap_usd*: number}` - `DELETE /v1/customers/{id}/spend-cap` (admin) — Clear the customer's spend cap (admin) - returns (200): `{customer_id*: string, spend_cap_usd*: number}` - `POST /v1/customers/{id}/subscription/preview` — Preview the cost impact of a plan and/or add-on change (read-only) - body: `{plan_id: string, plan_version: integer, add_ons_add: string[], add_ons_remove: string[]}` - returns (200): `{customer_id*: string, current*: object, target*: object, warnings*: string[], direction*: enum, period*: object, …}` - `PATCH /v1/customers/{id}/tags` (admin) — Replace the customer's segmentation tags (admin) - body: `{tags*: string[]}` - returns (200): `{customer_id*: string, tags*: string[]}` - `POST /v1/customers/{id}/trial` (admin) — Start a trial entitlement (admin) - body: `{source_plan_id*: string, target_plan_id*: string, ends_at*: string}` - returns (201): `{customer_id*: string, trial*: object}` - `GET /v1/customers/{id}/trial` (admin) — Get the customer's trial overlay (admin) - returns (200): `{customer_id*: string, trial*: object}` - `DELETE /v1/customers/{id}/trial` (admin) — Cancel an active trial (admin) - returns (200): `{customer_id*: string, cancelled*: enum}` - `POST /v1/customers/{id}/trial/convert` (admin) — Convert an active trial to paid (admin) - body: `{target_plan_id: string}` - returns (200): `{customer_id*: string, trial*: object}` - `POST /v1/customers/{id}/unarchive` (admin) — Restore an archived customer (admin) - returns (200): `{customer_id*: string, archived_at*: any}` - `POST /v1/customers/{id}/wallets` (admin) — Create a per-(customer, resource_id) USD wallet (admin) - body: `{resource_id*: string, label: string, hard_cap_usd: number, spend_rate_limit_usd_per_min: number}` - returns (201): `{id*: string, customer_id*: string, resource_id*: string, label*: string, status*: string, balance_usd*: number, …}` - `GET /v1/customers/{id}/wallets` (admin) — List wallets for a customer (admin) - returns (200): `{customer_id*: string, wallets*: object[]}` - `GET /v1/customers/export` (admin) — Export all customers as JSON (default) or CSV (admin) - query: `{format: string, include_archived: string}` - returns (200): `{customers*: object[], count*: integer}` - `POST /v1/customers/import` (admin) — Bulk records-only import of customers (admin); migrate off another biller - body: `{customers*: object[], mode: enum, dry_run: boolean}` - returns (200): `any` - `GET /v1/wallets/{wid}` (admin) — Get a wallet by id (admin) - returns (200): `{id*: string, customer_id*: string, resource_id*: string, label*: string, status*: string, balance_usd*: number, …}` - `PATCH /v1/wallets/{wid}` (admin) — Update wallet label / cap / rate limit (admin) - body: `{label: string, hard_cap_usd: number, spend_rate_limit_usd_per_min: number}` - returns (200): `{id*: string, customer_id*: string, resource_id*: string, label*: string, status*: string, balance_usd*: number, …}` - `DELETE /v1/wallets/{wid}` (admin) — Archive a wallet (admin) - returns (200): `{id*: string, customer_id*: string, resource_id*: string, label*: string, status*: string, balance_usd*: number, …}` - `GET /v1/wallets/{wid}/balance` (admin) — Get live wallet balance (admin) - returns (200): `{wallet_id*: string, balance_usd*: number, reserved_usd*: number, available_usd*: number, granted_usd_total*: number, spent_usd_total*: number, …}` - `POST /v1/wallets/{wid}/grant` (admin) — Top up wallet balance (admin) - body: `{amount_usd*: number, description: string}` - returns (201): `{wallet*: object, ledger_entry_id*: string}` - `GET /v1/wallets/{wid}/reservations` (admin) — List reservations for a wallet (admin) - returns (200): `{wallet_id*: string, reservations*: object[]}` - `POST /v1/wallets/{wid}/reservations/{rid}/commit` (admin) — Commit a pending reservation (admin) - body: `{amount_usd: number}` - returns (200): `{reservation_id*: string, wallet*: object, committed_amount_usd*: number, ledger_entry_id*: string}` - `POST /v1/wallets/{wid}/reservations/{rid}/release` (admin) — Release a pending reservation (admin) - returns (200): `{reservation_id*: string, status*: string}` - `POST /v1/wallets/{wid}/reserve` (admin) — Reserve wallet headroom for a pending charge (admin) - body: `{amount_usd*: number, ttl_seconds: integer}` - returns (201): `{reservation_id*: string, wallet_id*: string, amount_usd*: number, expires_at*: string, balance_usd*: number, reserved_usd*: number, …}` ### Metering - `POST /v1/meter` — Record a single usage event - body: `{customer_id*: string, model_id*: string, input_tokens: integer, output_tokens: integer, request_id: string, metadata: object, …}` - returns (200): `MeterResult` - `POST /v1/meter/backfill` (admin) — Backfill / late-event ingestion (admin) - body: `{events*: object[]}` - returns (200): `{succeeded*: object[], failed*: object[], current_period_count*: integer, past_period_count*: integer}` - `POST /v1/meter/batch` — Record up to 5,000 usage events - body: `{events*: object[]}` - returns (200): `{succeeded*: any[], failed*: any[]}` - `POST /v1/meter/cloudevents` — Record usage as a CloudEvents 1.0 envelope (single / structured / batched) - body: `{customer_id*: string, model_id*: string, input_tokens: integer, output_tokens: integer, request_id: string, metadata: object, …}` - returns (200): `any` - `POST /v1/meter/outcomes` — Claim a billable outcome (outcome-based pricing) - body: `{customer_id*: string, outcome_id*: string, request_id: string, metadata: object}` - returns (200): `{event*: object, deduplicated*: enum}` ### Entitlements - `GET /v1/entitlements/{id}` — Pre-flight quota check - query: `{model_id: string, estimated_input_tokens: integer, estimated_output_tokens: integer}` - returns (200): `any` - `GET /v1/entitlements/{id}/config` — Configuration entitlements for the customer's plan - returns (200): `{customer_id*: string, plan*: string, config*: object, sources*: object}` - `GET /v1/entitlements/{id}/config/{key}` — Single configuration-entitlement check - returns (200): `any` - `GET /v1/entitlements/{id}/features` — All feature gates for the customer's plan - returns (200): `any` - `GET /v1/entitlements/{id}/features/{feature_id}` — Single feature check - returns (200): `any` - `GET /v1/entitlements/feature-catalog` — Feature catalog organised into ordered groups (paywall/portal-ready) - returns (200): `{groups*: object[]}` - `GET /v1/usage/{id}` — Cached period usage - returns (200): `UsageSummary` ### Tokens - `POST /v1/tokens/{id}` (admin) — Issue a JWT entitlement token (admin) - returns (200): `TokenIssueResult` - `POST /v1/tokens/{id}/refresh` (admin) — Force-rotate the entitlement token (admin) - returns (200): `TokenIssueResult` - `POST /v1/tokens/verify` — Verify and decode a token - body: `{token*: string}` - returns (200): `TokenVerifyResult` ### Coupons - `POST /v1/coupons` (admin) — Create a coupon (admin) - returns (201): `any` - `GET /v1/coupons` (admin) — List coupons (admin) - returns (200): `any` - `GET /v1/coupons/{code}` (admin) — Get a coupon by code (admin) - returns (200): `any` - `DELETE /v1/coupons/{code}` (admin) — Archive a coupon (admin) - returns (200): `any` - `POST /v1/coupons/validate` — Preview a coupon's discount (public) - body: `{code*: string, plan_id: string}` - returns (200): `any` ### Ledger - `GET /v1/ledger` (admin) — Cross-customer ledger feed (admin). Cursor-paginated by timestamp, newest first. - query: `{before: string, limit: integer}` - returns (200): `{entries*: LedgerEntry[]}` - `GET /v1/ledger/{id}` — Recent transaction feed for a customer - query: `{limit: integer, period: string, grain: string, from: string, to: string, model_id: string}` - returns (200): `any` - `POST /v1/ledger/{id}/credits` (admin) — Apply a manual credit (admin) - body: `{amount_usd*: number, description: string, applied_by: string}` - returns (201): `LedgerEntry` - `GET /v1/ledger/{id}/seals` (admin) — Period seals for a customer's ledger (admin) - returns (200): `any` - `GET /v1/ledger/{id}/seals/verify` (admin) — Verify a customer's sealed ledger periods (admin) - returns (200): `any` - `GET /v1/ledger/{id}/statement/{year}/{month}` — Full monthly statement - returns (200): `any` - `GET /v1/ledger/{id}/statement/current` — This month's statement - returns (200): `any` - `GET /v1/ledger/export.csv` (admin) — Download the cross-customer ledger as CSV (admin). Same dataset as `GET /v1/ledger`, newest first. `Content-Type: text/csv`, sent as an attachment. ### Alerts - `GET /v1/alerts` (admin) — All active alerts (admin). Cursor-paginated by fired_at, newest first. - query: `{before: string, limit: integer}` - returns (200): `{total_alerts*: integer, customers_with_alerts*: integer, by_customer*: object, alerts*: Alert[]}` - `GET /v1/alerts/{id}` — Active alerts for a customer - returns (200): `any` - `GET /v1/alerts/config` (admin) — Env-derived alert configuration (admin) - returns (200): `any` - `GET /v1/alerts/rules` (admin) — Configured alert rules + summary (admin) - returns (200): `{rules*: object[], summary*: object}` - `POST /v1/alerts/rules` (admin) — Add a custom quota-percentage alert rule (admin) - body: `{threshold*: number}` - returns (201): `{id*: string, threshold*: number}` - `DELETE /v1/alerts/rules/{ruleId}` (admin) — Remove a custom alert rule (admin) ### Billing - `GET /v1/billing/actuals/{id}` — Authoritative usage from the ledger - returns (200): `any` - `GET /v1/billing/ar-aging` (admin) — Accounts-receivable aging report (admin) - returns (200): `{as_of*: string, customers*: object[], totals*: object}` - `POST /v1/billing/checkout` (admin) — Start a hosted Stripe Checkout session to subscribe a tenant end-customer to a tenant plan (admin) - body: `{customer_id*: string, plan_id*: string, success_url*: string, cancel_url*: string}` - returns (200): `{id*: string, url*: string}` - `POST /v1/billing/close` (admin) — Month-end close (admin) - returns (200): `any` - `GET /v1/billing/invoices` (admin) — List MeterBox-side invoices (admin) - query: `{customer_id: string, state: string, period: string, limit: integer}` - returns (200): `{invoices*: object[]}` - `GET /v1/billing/invoices/{id}` (admin) — Get one MeterBox-side invoice (admin) - returns (200): `{id*: string, customer_id*: string, period*: string, provider*: string, state*: enum, line_items*: object[], …}` - `POST /v1/billing/invoices/{id}/advance` (admin) — Advance a draft invoice to ready_to_issue (admin) - returns (200): `{id*: string, customer_id*: string, period*: string, provider*: string, state*: enum, line_items*: object[], …}` - `POST /v1/billing/invoices/{id}/approve` (admin) — Approve and issue a ready_to_issue invoice (admin) - returns (200): `{id*: string, customer_id*: string, period*: string, provider*: string, state*: enum, line_items*: object[], …}` - `POST /v1/billing/invoices/{id}/draft/synchronized` (admin) — Custom Invoicing: ERP ack of the draft (admin) - returns (200): `{id*: string, customer_id*: string, period*: string, provider*: string, state*: enum, line_items*: object[], …}` - `POST /v1/billing/invoices/{id}/issuing/synchronized` (admin) — Custom Invoicing: ERP issued the invoice (admin) - body: `{provider_invoice_id*: string, issued_at: string}` - returns (200): `{id*: string, customer_id*: string, period*: string, provider*: string, state*: enum, line_items*: object[], …}` - `POST /v1/billing/invoices/{id}/payment/status` (admin) — Custom Invoicing: ERP reports collection outcome (admin) - body: `{status*: enum, paid_at: string}` - returns (200): `{id*: string, customer_id*: string, period*: string, provider*: string, state*: enum, line_items*: object[], …}` - `POST /v1/billing/invoices/{id}/request-payment-status` (admin) — Cue the tenant ERP to report payment status (admin) - returns (200): `{id*: string, customer_id*: string, period*: string, provider*: string, state*: enum, line_items*: object[], …}` - `POST /v1/billing/invoices/{id}/retry` (admin) — Retry a failed transition (admin) - returns (200): `{id*: string, customer_id*: string, period*: string, provider*: string, state*: enum, line_items*: object[], …}` - `POST /v1/billing/invoices/{id}/void` (admin) — Void an invoice (admin) - returns (200): `{id*: string, customer_id*: string, period*: string, provider*: string, state*: enum, line_items*: object[], …}` - `GET /v1/billing/payment-methods/{id}` (admin) — List a tenant end-customer's saved cards + the default (admin) - returns (200): `{default_payment_method_id*: string, payment_methods*: object[]}` - `DELETE /v1/billing/payment-methods/{id}/{pmId}` (admin) — Detach a saved card (admin); refuses the current default - returns (200): `{customer_id*: string, removed*: string}` - `POST /v1/billing/payment-methods/{id}/default` (admin) — Set a saved card as the default for future invoices (admin) - body: `{payment_method_id*: string}` - returns (200): `{customer_id*: string, default_payment_method_id*: string}` - `POST /v1/billing/payment-methods/setup-intent` (admin) — Mint a SetupIntent to save a tenant end-customer's card off-session (admin) - body: `{customer_id*: string}` - returns (200): `{client_secret*: string}` - `POST /v1/billing/portal-session` (admin) — Open a Stripe Billing Portal session so a tenant end-customer can self-manage their subscription (admin) - body: `{customer_id*: string, return_url*: string}` - returns (200): `{id*: string, url*: string}` - `GET /v1/invoices/{id}` — MeterBox + Stripe invoices for a customer - returns (200): `any` ### Dashboard - `GET /v1/audit` (admin) — Billing-ops audit trail (admin): plan / customer / credit mutations with before/after diff. Cursor-paginated by timestamp. - query: `{before: string, limit: integer, event: string, target_kind: string, target_id: string}` - returns (200): `any` - `GET /v1/audit/export` (admin) — Stream the billing-ops audit trail as NDJSON (admin) for off-host / SIEM ingestion. Oldest-first; optional `since`/`until` (ISO) window. `Content-Type: application/x-ndjson`, streamed line-by-line. - query: `{since: string, until: string}` - `GET /v1/audit/verify` (admin) — Verify the billing-ops audit WORM hash chain (admin): walks the SHA-256 chain and returns `{ valid, hashed_count, legacy_count, broken_at, reason }`. `broken_at` names the first tampered row (content or prev-link altered); pre-chain legacy rows are counted, not chained. - returns (200): `any` - `GET /v1/dashboard/cohorts` (admin) — Cohort retention heatmap by signup month (admin) - query: `{months_back: integer}` - returns (200): `{periods*: string[], cohorts*: object[]}` - `GET /v1/dashboard/margin` (admin) — COGS-aware revenue / margin breakdown (admin) - query: `{period: string, group_by: string}` - returns (200): `{period*: string, group_by*: string, rows*: object[], total*: object}` - `GET /v1/dashboard/overview` (admin) — Aggregate dashboard view (admin) - returns (200): `DashboardOverview` - `GET /v1/dashboard/pricing-insights` (admin) — Pricing/packaging insights (admin): MRR by plan, 90-day trial conversion, feature adoption, revenue-at-risk. - returns (200): `any` - `POST /v1/dashboard/pricing-sim` (admin) — Replay historical usage against a hypothetical pricing change (admin) - body: `{window_days: integer, hypothetical*: object}` - returns (200): `{window_days*: integer, events_total*: integer, per_customer*: object[], total*: object, winners*: any[], losers*: any[], …}` - `GET /v1/dashboard/retention` (admin) — Dollar-retention summary (admin) - query: `{period: string}` - returns (200): `{period*: string, prior_period*: string, summary*: object, rows*: object[]}` - `GET /v1/dashboard/win-back-stats` (admin) — Voluntary-cancel funnel + retention-offer take-rate (admin) - query: `{period: string}` - returns (200): `{period*: string, initiated*: integer, accepted*: integer, confirmed*: integer, reverted*: integer, take_rate_pct*: number, …}` - `GET /v1/revenue-recognition` (admin) — ASC 606 / GAAP recognition schedule for one monthly period. Walks ledger entries by `period`, groups by `(customer_id, kind)`, and surfaces the contributing `ledger_entry_ids[]` for auditor drill-down. - query: `{period: string, customer_id: string}` - returns (200): `{period*: string, customer_id: string, rows*: object[], totals*: object}` ### Settings - `GET /v1/apps` (admin) — Apps catalog with live status (admin) - returns (200): `any` - `GET /v1/apps/{id}` (admin) — Single app + status (admin) - returns (200): `any` - `POST /v1/apps/{id}/install` (admin) — Install an app — configure + activate (admin) - returns (200): `any` - `POST /v1/apps/{id}/uninstall` (admin) — Uninstall an app — disconnect (admin) - returns (200): `any` - `POST /v1/config-promotion/apply` (admin) — Apply selected domains of a config bundle to this environment (admin) - returns (200): `any` - `GET /v1/config-promotion/export` (admin) — Export this environment's config as a secret-free bundle (admin) - returns (200): `any` - `POST /v1/config-promotion/preview` (admin) — Diff a config bundle against this environment (admin) - returns (200): `any` - `GET /v1/settings/ai` (admin) — AI config (admin; key masked) - returns (200): `any` - `PUT /v1/settings/ai` (admin) — Set the tenant's Anthropic API key / model (admin) - returns (200): `any` - `GET /v1/settings/api-keys` (admin) — List API keys, masked (admin) - returns (200): `any` - `POST /v1/settings/api-keys` (admin) — Mint a new API key (admin). The plaintext secret is returned only in this response. - body: `any` - returns (201): `any` - `DELETE /v1/settings/api-keys/{id}` (admin) — Revoke an API key (admin) - `GET /v1/settings/billing` (admin) — Get the tenant's billing/payment-provider connection + toggles (admin) - returns (200): `{provider*: enum, connected*: boolean, checkout_via_meterbox*: boolean, collect_via_provider*: boolean, credential_hint: string}` - `PUT /v1/settings/billing` (admin) — Update billing provider + opt-in toggles (admin) - body: `{provider: enum, checkout_via_meterbox: boolean, collect_via_provider: boolean}` - returns (200): `{provider*: enum, connected*: boolean, checkout_via_meterbox*: boolean, collect_via_provider*: boolean, credential_hint: string}` - `POST /v1/settings/billing/connect` (admin) — Store the tenant's provider credentials (admin). Never echoed back in responses. - body: `{provider: enum, credentials*: object}` - returns (200): `{provider*: enum, connected*: boolean, checkout_via_meterbox*: boolean, collect_via_provider*: boolean, credential_hint: string}` - `POST /v1/settings/billing/disconnect` (admin) — Clear stored provider credentials (admin) - returns (200): `{provider*: enum, connected*: boolean, checkout_via_meterbox*: boolean, collect_via_provider*: boolean, credential_hint: string}` - `GET /v1/settings/bucket-ingest` (admin) — Get the bucket-ingest connector config + last-run status (admin) - returns (200): `{enabled*: boolean, provider*: string, bucket*: string, prefix*: string, region*: string, last_run_at*: string, …}` - `PUT /v1/settings/bucket-ingest` (admin) — Configure the bucket-ingest connector (admin) - body: `{enabled: boolean, provider: enum, bucket: string, prefix: string, region: string, credentials: object}` - returns (200): `{enabled*: boolean, provider*: string, bucket*: string, prefix*: string, region*: string, last_run_at*: string, …}` - `POST /v1/settings/bucket-ingest/run` (admin) — Run the bucket-ingest connector once on demand (admin) - returns (200): `{skipped*: boolean, reason: string, summary: object, error: string}` - `GET /v1/settings/email` (admin) — Lifecycle-email config: provider + all trigger templates (admin) - returns (200): `any` - `PUT /v1/settings/email` (admin) — Set the email provider / from address / credentials (admin) - returns (200): `any` - `PUT /v1/settings/email/templates/{trigger}` (admin) — Save a lifecycle-email template / enable a trigger (admin) - returns (200): `any` - `POST /v1/settings/email/test` (admin) — Send a test email through the configured provider (admin) - returns (200): `any` - `GET /v1/settings/privacy` (admin) — Get the tenant's data-privacy toggles (admin) - returns (200): `{zero_retention_meter*: boolean, enterprise_advisor_enabled*: boolean}` - `PATCH /v1/settings/privacy` (admin) — Update the tenant's data-privacy toggles (admin) - body: `{zero_retention_meter: boolean, enterprise_advisor_enabled: boolean}` - returns (200): `{zero_retention_meter*: boolean, enterprise_advisor_enabled*: boolean}` - `GET /v1/settings/tax` (admin) — Current tax-provider config (admin; credentials masked) - returns (200): `any` - `PUT /v1/settings/tax` (admin) — Upsert tax-provider config (admin) - body: `{provider: enum, fixed_rates: object[], avalara: object, anrok: object}` - returns (200): `any` - `GET /v1/settings/upstream-connectors` (admin) — List upstream connector configs (admin) - returns (200): `{connectors*: object[]}` - `PUT /v1/settings/upstream-connectors/{source}` (admin) — Configure an upstream connector (admin) - body: `object` - returns (200): `{id*: enum, enabled*: boolean, mapping*: object, airbyte_stream_filter: string[], kafka_brokers: string[], kafka_topic: string, …}` - `GET /v1/settings/webhook-deliveries` (admin) — List recent outbound webhook delivery attempts (admin). Cursor-paginated, newest first. - query: `{before: string, limit: integer, webhook_id: string, status: string}` - returns (200): `any` - `GET /v1/settings/webhook-deliveries/{id}` (admin) — Get a single webhook delivery attempt (admin) - returns (200): `any` - `POST /v1/settings/webhook-deliveries/{id}/replay` (admin) — Reset retry state and immediately attempt one delivery (admin) - returns (200): `any` - `GET /v1/settings/webhook-events` (admin) — List the supported webhook event taxonomy (admin) - returns (200): `{event_types*: string[]}` - `GET /v1/settings/webhooks` (admin) — List configured outbound webhooks (admin). Cursor-paginated, newest first. - query: `{before: string, limit: integer}` - returns (200): `any` - `POST /v1/settings/webhooks` (admin) — Register a new outbound webhook endpoint (admin). Supports HTTP POST or AWS SQS as the delivery transport. - body: `{kind: enum, url: string, sqs_queue_url: string, sqs_region: string, aws_access_key_id: string, aws_secret_access_key: string, …}` - returns (201): `any` - `PUT /v1/settings/webhooks/{id}` (admin) — Update a webhook endpoint (admin) - body: `any` - returns (200): `any` - `DELETE /v1/settings/webhooks/{id}` (admin) — Delete a webhook endpoint (admin) - `POST /v1/settings/webhooks/{id}/rotate-secret` (admin) — Rotate the HMAC signing secret for a webhook endpoint (admin) - returns (200): `any` ### Webhooks - `POST /webhooks/stripe` — Stripe inbound webhook (signature-verified) - returns (200): `any` ### Accounting - `GET /v1/accounting/coa` (admin) — Current chart-of-accounts mapping (admin) - returns (200): `{id*: string, accounts*: object}` - `PUT /v1/accounting/coa` (admin) — Upsert chart-of-accounts mapping (admin) - body: `{accounts: object}` - returns (200): `{id*: string, accounts*: object}` - `GET /v1/accounting/export/netsuite` (admin) — NetSuite Journal Import CSV (admin) - query: `{since: string, until: string}` - `GET /v1/accounting/export/xero` (admin) — Xero Manual Journals CSV (admin) - query: `{since: string, until: string}` - `GET /v1/accounting/journals` (admin) — NDJSON stream of projected journal entries (admin) - query: `{since: string, until: string}` - `GET /v1/accounting/manifest` (admin) — Dataset catalog + schema versions for accounting ingestion (admin) - returns (200): `any` ### Anomalies - `GET /v1/anomalies` (admin) — List anomaly findings (admin) - returns (200): `{anomalies*: object[]}` - `POST /v1/anomalies/{id}/acknowledge` (admin) — Acknowledge an anomaly finding (admin) - returns (200): `{id*: string, kind*: enum, severity*: enum, customer_id*: string, bucket_date*: string, detected_at*: string, …}` - `POST /v1/anomalies/run` (admin) — Run anomaly detection on-demand for the current environment (admin) - returns (200): `any` - `GET /v1/anomalies/settings` (admin) — Detector thresholds (admin) - returns (200): `{id*: string, thresholds*: object}` - `PUT /v1/anomalies/settings` (admin) — Update detector thresholds (admin) - body: `{thresholds: object}` - returns (200): `any` ### CRM - `POST /v1/crm/inbound` (admin) — Inbound CRM push — records-only customer upsert from Salesforce / HubSpot (admin) - body: `{provider: enum, external_id*: string, name*: string, email*: string, plan_id*: string, plan_version: integer, …}` - returns (200): `any` ### Customer Portal - `GET /v1/customer-portal/{id}/allowance` — Usage-allowance window + banked resets (customer JWT) - returns (200): `any` - `POST /v1/customer-portal/{id}/allowance/reset` — Self-serve: spend a banked reset to refill now (customer JWT) - returns (200): `any` - `POST /v1/customer-portal/{id}/checkout` — Customer-scoped Stripe Checkout session start (no admin key required) - body: `{plan_id*: string, success_url*: string, cancel_url*: string}` - returns (200): `{id*: string, url*: string}` - `GET /v1/customer-portal/{id}/credits` — Customer-scoped credit balance + grants (no admin key required) - returns (200): `{customer_id*: string, balance_usd*: number, grants*: any[]}` - `GET /v1/customer-portal/{id}/credits/timeseries` — Customer-scoped credit balance history (no admin key required) - query: `{from: string, to: string, granularity: string}` - returns (200): `{customer_id*: string, from*: string, to*: string, granularity*: string, points*: object[]}` - `GET /v1/customer-portal/{id}/draft-invoice` — Mid-period draft invoice (customer JWT) - returns (200): `{customer_id*: string, period*: string, as_of*: string, elapsed_pct*: number, currency*: enum, resolved_price*: object, …}` - `GET /v1/customer-portal/{id}/entitlements` — Customer-scoped entitlement check (no admin key required) - query: `{model_id: string, estimated_input_tokens: integer, estimated_output_tokens: integer}` - returns (200): `any` - `GET /v1/customer-portal/{id}/me` — Customer-scoped profile (no admin key required) - returns (200): `{customer_id*: string, name*: string, email*: string, plan_id*: string, plan_version*: integer, plan_name*: string, …}` - `GET /v1/customer-portal/{id}/optimization` — Plan-fit view — usage vs every plan's limits (customer JWT) - returns (200): `{customer_id*: string, current_plan_id*: string, period*: string, plans*: object[], recommendation*: object}` - `GET /v1/customer-portal/{id}/referral` — The customer's own referral code + usage (customer JWT) - returns (200): `any` - `POST /v1/customer-portal/{id}/referral/redeem` — Self-serve: redeem a referral code (customer JWT) - returns (200): `any` - `POST /v1/customer-portal/{id}/subscription/preview` — Customer-scoped preview of a plan and/or add-on change (authenticated by the entitlement JWT — no admin key required) - body: `{plan_id: string, plan_version: integer, add_ons_add: string[], add_ons_remove: string[]}` - returns (200): `{customer_id*: string, current*: object, target*: object, warnings*: string[], direction*: enum, period*: object, …}` - `GET /v1/customer-portal/{id}/usage` — Customer-scoped period usage summary (no admin key required) - returns (200): `any` ### Experiments - `GET /v1/experiments` (admin) — List A/B pricing experiments (admin) - returns (200): `{experiments*: object[]}` - `POST /v1/experiments` (admin) — Create an A/B pricing experiment (admin) - body: `any` - returns (201): `{experiment*: object}` - `POST /v1/experiments/{id}/promote` (admin) — Promote a variant as the winner; completes the experiment (admin) - body: `{variant_plan_id*: string}` - returns (200): `{experiment*: object}` - `GET /v1/experiments/{id}/results` (admin) — Per-variant counts + summed MRR (admin) - returns (200): `any` - `POST /v1/experiments/{id}/start` (admin) — Start an experiment — new control-plan signups are now assigned (admin) - returns (200): `{experiment*: object}` - `POST /v1/experiments/{id}/stop` (admin) — Stop an experiment without picking a winner (admin) - returns (200): `{experiment*: object}` ### Finance - `GET /v1/reconciliation` (admin) — Reconcile expected provider COGS vs recorded invoices (admin) - returns (200): `{period*: string, tolerance_pct*: number, rows*: object[], totals*: object}` - `POST /v1/reconciliation/fetch/{provider}` (admin) — Fetch the period total from the provider's cost API + record it (admin) - returns (200): `any` - `GET /v1/reconciliation/keys` (admin) — Provider admin keys (admin; masked hints only) - returns (200): `any` - `PUT /v1/reconciliation/keys` (admin) — Set OpenAI / Anthropic admin keys for cost fetching (admin) - body: `any` - returns (200): `any` - `PUT /v1/reconciliation/provider-invoices` (admin) — Record an LLM-provider invoice total for a period (admin) - body: `any` - returns (200): `any` - `GET /v1/reconciliation/provider-invoices` (admin) — List recorded provider invoices (admin) — ?period= filters - returns (200): `any` ### Integrations - `GET /v1/auth0/claims/{id}` (admin) — Return MeterBox entitlements as Auth0 URL-namespaced custom claims for a Post-Login Action (admin) - query: `{namespace: string}` - returns (200): `{namespace*: string, claims*: object}` - `POST /v1/integrations/airbyte/destination` (admin) — Airbyte destination receiver (admin) - body: `any` - returns (200): `{source*: string, events_received*: integer, events_ingested*: integer, events_failed*: integer, failures*: any[]}` - `GET /v1/integrations/aws-marketplace/customers` (admin) — List customers originated via the AWS Marketplace bridge (admin) - returns (200): `any` - `POST /v1/integrations/aws-marketplace/inbound` (admin) — Inbound AWS Marketplace push — records-only customer upsert keyed on AWS customer identifier (admin) - body: `{aws_customer_identifier*: string, aws_product_code*: string, plan_id*: string, name*: string, email*: string, status: string, …}` - returns (200): `any` - `POST /v1/integrations/aws-marketplace/notification` (admin) — Apply an AWS Marketplace SNS subscription notification to the linked customer (admin) - body: `{aws_customer_identifier*: string, action*: enum}` - returns (200): `any` - `GET /v1/integrations/azure-marketplace/customers` (admin) — List customers originated via the Azure Marketplace bridge (admin) - returns (200): `any` - `POST /v1/integrations/azure-marketplace/inbound` (admin) — Inbound Azure Marketplace push — records-only customer upsert keyed on the SaaS subscription id (admin) - body: `{azure_subscription_id*: string, azure_offer_id*: string, plan_id*: string, name*: string, email*: string, status: string, …}` - returns (200): `any` - `POST /v1/integrations/azure-marketplace/notification` (admin) — Apply an Azure Marketplace SaaS webhook action to the linked customer (admin) — activated / suspended / reinstated / unsubscribed - body: `{azure_subscription_id*: string, action*: enum}` - returns (200): `any` - `GET /v1/integrations/gcp-marketplace/customers` (admin) — List customers originated via the GCP Marketplace bridge (admin) - returns (200): `any` - `POST /v1/integrations/gcp-marketplace/inbound` (admin) — Inbound GCP Marketplace push — records-only customer upsert keyed on the procurement account id (admin) - body: `{gcp_account_id*: string, gcp_entitlement_id*: string, plan_id*: string, name*: string, email*: string, status: string, …}` - returns (200): `any` - `POST /v1/integrations/gcp-marketplace/notification` (admin) — Apply a GCP Marketplace entitlement event to the linked customer (admin) — entitlement-active / -suspended / -pending-cancellation / -cancelled - body: `{gcp_account_id*: string, action*: enum}` - returns (200): `any` - `POST /v1/integrations/kafka/run` (admin) — Restart the Kafka consumer (admin) - returns (200): `{source*: enum, env: string, connected*: boolean, envs*: object[]}` - `GET /v1/integrations/kafka/status` (admin) — Current Kafka consumer status (admin) - returns (200): `{source*: enum, env: string, connected*: boolean, envs*: object[]}` - `POST /v1/integrations/segment/track` (admin) — Segment track-event receiver (admin) - body: `any` - returns (200): `{source*: string, events_received*: integer, events_ingested*: integer, events_failed*: integer, failures*: any[]}` ### Onboarding - `GET /v1/onboarding/dismissals` (admin) — List dismissed onboarding suggestion ids for this env (admin) - returns (200): `{dismissals*: object[]}` - `POST /v1/onboarding/dismissals` (admin) — Dismiss an onboarding suggestion by id; shared across all admins in this env (admin) - body: `{id*: string}` - returns (201): `{id*: string, dismissed_at*: string, dismissed_by*: string}` - `DELETE /v1/onboarding/dismissals/{id}` (admin) — Un-dismiss a suggestion (admin) ### Quotes - `GET /v1/contracts` (admin) — List contract proposals (admin) — ?status=extracted|reviewed|converted - returns (200): `any` - `GET /v1/contracts/{id}` (admin) — Single contract proposal (admin) - returns (200): `any` - `PATCH /v1/contracts/{id}` (admin) — Edit extracted fields / mark a proposal reviewed (admin) - returns (200): `any` - `DELETE /v1/contracts/{id}` (admin) — Discard a contract proposal (admin) - `POST /v1/contracts/{id}/convert` (admin) — Convert a proposal into a draft quote (admin) - returns (200): `any` - `POST /v1/contracts/extract` (admin) — Extract billing terms from a signed PDF contract (admin) - query: `{filename: string}` - returns (201): `any` - `POST /v1/enterprise/apply` (admin) — MeterBox Advisor — Auto-Apply an approved proposal (admin) - returns (200): `any` - `POST /v1/enterprise/configure` (admin) — MeterBox Advisor — deal brief → catalog-grounded proposal (admin) - returns (200): `any` - `POST /v1/quotes` (admin) — Create a quote (admin) - body: `{customer_id: string, prospect: object, plan_id*: string, add_on_ids: string[], pricing_dimensions: object, contract_commit: object, …}` - returns (201): `any` - `GET /v1/quotes` (admin) — List quotes (admin) — ?status=open|accepted|declined|expired - returns (200): `any` - `GET /v1/quotes/{id}` (admin) — Single quote, including its share token (admin) - returns (200): `any` - `POST /v1/quotes/{id}/accept` (admin) — Accept a quote → convert to the live subscription (admin) - returns (200): `any` - `POST /v1/quotes/{id}/decline` (admin) — Decline an open quote (admin) - body: `{reason: string}` - returns (200): `any` - `GET /v1/quotes/{id}/view` — Public read-only share view (token-gated) - query: `{token*: string, format: string}` - returns (200): `any` ### Tax - `POST /v1/tax/quote` (admin) — Preview tax on a set of lines (admin) - body: `{customer_id*: string, lines*: object[]}` - returns (200): `{customer_id*: string, provider*: string, jurisdiction*: string, rate_pct*: number, total_tax_usd*: number, lines*: object[]}` ### Warehouse - `GET /v1/settings/reverse-etl` (admin) — List reverse-ETL sinks (admin; credentials masked) - returns (200): `{sinks*: object[]}` - `PUT /v1/settings/reverse-etl/{id}` (admin) — Create or update a reverse-ETL sink (admin) - body: `any` - returns (200): `{id*: string, kind*: enum, enabled*: boolean, datasets*: string[], config*: object, cursors*: object, …}` - `DELETE /v1/settings/reverse-etl/{id}` (admin) — Remove a reverse-ETL sink (admin) - `POST /v1/settings/reverse-etl/{id}/run` (admin) — Trigger one sync of a sink now (admin) - returns (200): `any` - `GET /v1/warehouse/export/credit-grants` (admin) — NDJSON export of credit grants (admin) - query: `{since: string, until: string}` - `GET /v1/warehouse/export/customers` (admin) — NDJSON export of customer records, optional [since, until) window (admin) - query: `{since: string, until: string}` - `GET /v1/warehouse/export/ledger` (admin) — NDJSON export of ledger entries across all customers (admin) - query: `{since: string, until: string}` - `GET /v1/warehouse/manifest` (admin) — Dataset catalog + schema versions for warehouse ingestion (admin) - returns (200): `any` ## Schemas Field list per registered schema. `*` marks required fields. - **Alert** — `{id*: string · customer_id*: string · customer_name: string · plan_id: string · plan_name: string · type*: enum · dimension*: enum · threshold*: number · threshold_type: string · used*: number · limit*: number · remaining*: number · pct_used*: number · fired_at*: string · acknowledged: boolean · message: string · period*: string}` - **AlertDimension** — `"input_tokens" | "output_tokens" | "api_calls"` - **AlertType** — `"QUOTA_WARNING" | "QUOTA_CRITICAL" | "QUOTA_EXCEEDED" | "ABSOLUTE_LOW"` - **AuditEvent** — `{id*: string · timestamp*: string · organization_id*: string · actor_user_id*: string · actor_email*: string · actor_kind*: enum · actor_operator_id: string · event*: string · target: object · outcome*: enum · ip*: string · user_agent*: string · meta*: object}` - **ChangePlanBody** — `{plan_id*: string · stripe_price_id: string · effective: enum}` - **ConfirmCodeBody** — `{code*: string}` - **CreateCustomerBody** — `{name*: string · email*: string · external_id*: string · plan_id*: string · stripe_price_id: string · rate_card_id*: string · model_id: string · payment_method_id: string · coupon_code: string · country: string · currency: string · pricing_dimensions: object · seat_count: integer · tags: string[]}` - **CreditBody** — `{amount_usd*: number · description: string · applied_by: string}` - **CreditCheckoutBody** — `{amount_usd*: number · success_url*: string · cancel_url*: string}` - **CreditSettingsPatch** — `{credit_balance_low_threshold_usd: number · auto_recharge: object}` - **CsrfTokenResponse** — `{csrf_token*: string}` - **CustomerCouponBody** — `{code*: string}` - **CustomerListItem** — `{id*: string · name*: string · email*: string · plan_id*: string · plan*: string · status*: string · mrr*: number · usage_pct*: number · last_seen*: string · scheduled_change: object · add_ons: object[] · seat_count: integer · contract_commit: object · tags: string[] · spend_cap_usd: number}` - **CustomersResponse** — `{customers*: object[]}` - **DashboardOverview** — `{stats*: object[] · revenueSeries*: object[] · modelUsage*: object[] · activity*: object[]}` - **Environment** — `{status*: enum · url*: string · provisioned_at*: string · affinity: enum · size*: enum · deleted_at: string · reclaimed_at: string · last_error: string}` - **Error** — `{error*: string}` - **GrantCreditBody** — `{amount_usd*: number · type: enum · priority: integer · effective_at: string · expires_at: string · applies_to: object · source: string · reason: string · external_id: string · granted_by: string}` - **HealthResponse** — `{status*: enum · company*: string · customers*: integer · uptime_s*: integer}` - **InviteAcceptBody** — `{token*: string · password*: string}` - **InviteCreateBody** — `{email*: string · role*: enum}` - **LedgerEntry** — `{id*: string · customer_id*: string · type*: enum · status*: enum · timestamp*: string · period*: string · description: string · amount_usd*: number · invoice_id: string · metadata: object · model_id: string · model_name: string · input_tokens: integer · output_tokens: integer · input_cost_usd: number · output_cost_usd: number · overage: boolean · transaction_id: string · request_id: string · coupon_code: string · applied_by: string}` - **LoginBody** — `{email*: string · password*: string}` - **LoginResponse** — `any` - **MeResponse** — `{user*: object · organization*: object · role*: enum}` - **MemberRoleBody** — `{role*: enum}` - **MeterBackfillBody** — `{events*: object[]}` - **MeterBatchBody** — `{events*: object[]}` - **MeterBody** — `{customer_id*: string · model_id*: string · input_tokens: integer · output_tokens: integer · request_id: string · metadata: object · resource_id: string · attributes: object · model_version: string · latency_ms: number}` - **MeterResult** — `{allowed*: boolean · blocked: boolean · duplicate: boolean · transaction_id: string · cost_estimate_usd: number · overage: boolean · reason: string · detail: string · upgrade_hint: boolean · current_usage: any · retry_after: integer · plan: string}` - **MfaCodeBody** — `{code*: string}` - **MfaDisableBody** — `{code*: string · password*: string}` - **MfaVerifyBody** — `{mfa_token*: string · code*: string}` - **Model** — `{id*: string · name*: string · description: string · pricing*: object}` - **Passkey** — `{id*: string · name*: string · device_type*: string · backed_up*: boolean · created_at*: string · last_used_at*: string}` - **PasswordResetConfirmBody** — `{token*: string · password*: string}` - **PasswordResetRequestBody** — `{email*: string}` - **Plan** — `{id*: string · name*: string · description: string · stripe_price_monthly_usd*: number · seat_price_monthly_usd: number · limits*: object · features*: string[] · display_features: string[] · customers*: integer · mrr*: number · published: boolean · latest_version: integer · has_unpublished_changes: boolean}` - **PrivacyPatch** — `{zero_retention_meter: boolean · enterprise_advisor_enabled: boolean}` - **PublicInvite** — `{id*: string · organization_id*: string · email*: string · role*: enum · status*: enum · invited_by*: string · created_at*: string · expires_at*: string · accepted_at*: string}` - **PublicMember** — `{user_id*: string · email*: string · role*: enum · created_at*: string · is_self*: boolean}` - **PublicOrganization** — `{id*: string · slug*: string · name*: string · plan*: enum · environments*: object}` - **PublicUser** — `{id*: string · email*: string · theme_preference*: enum · phone*: string · email_verified*: boolean · phone_verified*: boolean · mfa_enabled*: boolean · mfa_backup_codes_remaining*: integer · passkeys*: object[]}` - **RecentLedger** — `{customer_id*: string · current_period*: string · period_total_usd*: number · period_entry_counts*: object · period_token_totals*: object · filters*: object · entries*: object[] · returned*: integer · total_matching*: integer · has_more*: boolean}` - **ReclaimResponse** — `{organization*: object · backup_path*: string}` - **SignupBody** — `{email*: string · password*: string · organization_name*: string · plan: enum · phone: string · preferred_region: string}` - **Statement** — `{customer_id*: string · period*: string · status*: string · opening_balance_usd*: number · closing_balance_usd*: number · total_usd*: number · days*: any[] · model_subtotals*: any[] · type_subtotals*: any[] · token_totals*: object · entry_count*: integer · invoice_ids: string[] · status_breakdown: any}` - **TokenIssueResult** — `{token*: string · token_type*: enum · expires_in*: integer · expires_at*: string · issued_at*: string · payload*: object}` - **TokenVerifyResult** — `{valid*: boolean · payload: object · seconds_remaining: integer · error: string · detail: string}` - **UsageDimension** — `{used*: number · limit*: number · pct*: number}` - **UsageSummary** — `{period*: string · plan*: string · usage*: object · overage_allowed*: boolean · overage_behavior*: enum}` - **ValidateCouponBody** — `{code*: string · plan_id: string}` - **ValidationError** — `{error*: enum · details*: object[]}` - **VerifyEmailLinkBody** — `{token*: string}` - **VerifyTokenBody** — `{token*: string}` ## Optional - [Dashboard](https://meterbox.ai/app): Tenant admin UI (live data) - [OpenAPI spec](https://meterbox.ai/openapi.json): Machine-readable API surface