The usage-based billing release: everything between a usage event and the
money it becomes — billable metrics, tiered pricing, unattended renewals,
prepaid wallets, minimum commitments, usage alerts, batch ingestion, an
append-only audit trail — plus the first wave of ecosystem integrations
(GoCardless, Adyen, NetSuite, Avalara, HubSpot, S3 export) shipped as
experimental pending sandbox certification.
Added
-
HubSpot CRM sync (EXPERIMENTAL) — a daily worker upserts every
customer as a HubSpot contact (keyed by email) carrying
recurso_customer_idandrecurso_subscription_state
(active/churned). Private-app token viaHUBSPOT_ACCESS_TOKEN;
SaaS egress, so blocked underRESIDENCY_MODE=self_hosted.
Salesforce follows as its own spec (JWT auth design needed). -
NetSuite accounting sync (EXPERIMENTAL) — a SuiteTalk REST adapter
in the existing accounting-sync framework (customer / invoice / item
upserts with Location-header id capture,ErrExternalGoneremapping,
per-line major-unit amounts). Residency-guarded like QuickBooks/Xero;
connect with providernetsuite(RealmID = NetSuite account id). -
Avalara AvaTax provider (EXPERIMENTAL) — US sales-tax quotes via
uncommitted SalesOrder transactions (AVALARA_ACCOUNT_ID/
AVALARA_LICENSE_KEY/AVALARA_COMPANY_CODE), sharing TaxJar's error
taxonomy, 24h rate cache, and theRESIDENCY_MODE=self_hostedguard.
TaxJar takes precedence when both are configured. -
S3 finance export (EXPERIMENTAL) — a daily worker ships each
tenant's general ledger CSV to operator-owned object storage
(S3_EXPORT_BUCKET/REGION/PREFIX,S3_EXPORT_ENDPOINTfor
MinIO/R2), signed with a dependency-free SigV4 client. Idempotent keys
per day; disabled unless configured. -
Automation & Okta docs — Zapier/n8n recipes over the signed webhook
surface and the Okta SAML SSO walkthrough (Track D6/D7 ride existing
code). -
GoCardless + Adyen gateways (EXPERIMENTAL) — bank-debit rails
(SEPA/BACS via mandate-first billing requests) and global card
processing (Checkout Sessions, off-session stored-method charges), both
behindGATEWAY_CURRENCY_OVERRIDES(e.g.EUR=gocardless,SGD=adyen)
with the existing INR→Razorpay / default→Stripe routing untouched.
httptest-verified request shapes and idempotency keys; sandbox
verification pending (founder-gated). -
Append-only audit trail — every successful config-grade mutation
(plans, metrics, charges, coupons, webhooks, wallets, alerts, team,
settings, ...) is recorded via middleware with actor (dashboard user or
API key), route, entity, and the truncated request payload.
GET /v1/audit-logsfilters by entity/actor/time; the table rejects
UPDATE and DELETE at the database level. -
Ingestion hardening —
POST /v1/usage/events/batchingests up to
500 events with per-item results, and events accept an optional
transaction_ididempotency key: a retried event with the same
(subscription, transaction_id) collapses to the original, so SDK retries
can never inflate usage. A covering index on
(subscription_id, dimension, timestamp) backs the aggregation hot path. -
Usage threshold alerts —
/v1/usage-alertsconfigures per-
subscription thresholds on a billable metric (absolute quantity or
percent of the matching entitlement limit). The billing-cycle sweep
evaluates them and fires AT MOST once per billing period per threshold —
a conditional claim dedups concurrent sweeps — emitting the
usage.alert.triggeredwebhook event plus an email to the customer. -
Minimum commitments —
PUT /v1/subscriptions/{id}/commitmentsets a
per-period floor (minor units); when a period's subtotal (flat + add-ons- metered usage) falls short, a "Minimum commitment true-up" line fills
exactly the difference, taxed at the plan HSN. The usage-amount preview
reports the projected true-up. Overage needs nothing: usage past the
floor bills naturally.
- metered usage) falls short, a "Minimum commitment true-up" line fills
-
Prepaid wallets — money-denominated stored value per
customer+currency (/v1/wallets, top-ups, transactions, auto-recharge).
Invoices drain the wallet FIRST (wallet → credit notes → gateway), with
residues consumed oldest-expiring-first so dated promotional credit is
spent before it lapses; expired residue is written off by the sweep.
Every movement is append-only with balance-after and posts balanced
ledger legs (DR Cash / CR Customer Credit on top-up; DR Customer Credit /
CR AR on drain). Auto-recharge tops up below-threshold wallets from the
saved payment method on the billing-cycle tick. -
Unattended renewals — a billing-cycle scheduler claims due,
locally-billed subscriptions (leased at-most-once claims), generates the
renewal invoice (flat fee in advance + metered usage in arrears),
advances the period anchor-preservingly, and best-effort charges the
saved payment method; declines flow to dunning.BILLING_CYCLE_INTERVAL
configures the tick (default5m,0disables).cancel_at_period_end
subscriptions get their final usage rated, then cancel. -
Metered mandate debits — UPI-mandate debit invoices now carry the
subscription's rated usage lines, and the subscription billing period
advances with each cycle (it previously never advanced for mandate
subscriptions, leaving current-period usage reporting stale). Usage that
would exceed the mandate's authorized ceiling is billed on a separate
open invoice instead of over-charging. -
SDK metering methods — Node
recurso@1.3.0, Gov1.1.0, and Python
1.2.0expose billable metrics, plan charges, the usage-amount preview,
and event properties. -
Usage-based billing v1 (
docs/spec_usage_billing.md) — the metering
engine: billable metrics (POST/GET/PUT/DELETE /v1/billable-metrics)
aggregate usage events bycount,sum,max, orunique(distinct
values of an event property); charges attachper_unit,graduated,
volume, orpackagepricing for a metric to a plan
(PUT/GET /v1/plans/{id}/charges), with per-currency amounts and
sub-minor-unit decimal rates (0.0035/call) computed exactly and rounded
once per line; rating at period close bills the elapsed period's
usage in arrears on the renewal invoice as tax-resolved line items, with
ausage_ratingswindow claim making retried generation idempotent;
immediate cancellation bills the partial window on a final usage invoice.
GET /v1/subscriptions/{id}/usage-amountpreviews the running period,
andPOST /v1/usage/eventsaccepts optionalproperties.