Billing API
Read plan/access state and use quote-bound start_paid_plan or full-period renew_period previews to restore exhausted Stripe-backed allowances.
Plans and Subscription
/api/v1/billingReturns plans, proTiers, the current subscription, and the authoritative account-level billingProvider (stripe or shopify). isShopifyMerchant remains a compatibility field; clients should use billingProvider. A live local Stripe contract in active, trialing, or past_due state retains Stripe ownership even if Shopify is connected later. Otherwise the connected platform capability determines the provider.
The subscription includes period boundaries, cancelAtPeriodEnd, a normalized planLifecycle, shipmentQuota, currentUsage, remainingQuota, usagePercent, shipmentAccess, and shipmentAccessCopy. The lifecycle distinguishes trial, renewing, ending, inactive, payment-issue, and permanent-free states without treating a scheduled cancellation as already inactive. Access reports whether new shipments may be admitted and whether existing non-terminal shipments may continue tracking.
subscription.cataloguePricing is always the current tax-exclusive public catalogue reference: { source: "current_catalogue", currency, monthlyUnitAmount, annualMonthlyUnitAmount, annualTotalUnitAmount }. Stripe calculates tax separately and these amounts are not necessarily the merchant's contract. For a Stripe-backed subscription, contractPricingStatus is available or unavailable. When available, contractPricing is { source: "stripe_contract", catalogue: "current" | "legacy", priceId, currency, unitAmount, quantity, billingInterval, taxBehavior } and reports the live immutable Stripe Price. taxBehavior preserves Stripe's exclusive, inclusive, unspecified, or null value. An exclusive amount is the recurring base price before tax, not the final invoice total; unspecified and null remain neutral without inferring from currency or catalogue. Stripe invoices are authoritative for final totals after tax, discounts, credits, and proration adjustments. Accounts without a Stripe contract return not_applicable and null. Never substitute cataloguePricing when live contract pricing is unavailable.
Numeric quota exhaustion blocks only new intake; already-admitted shipments continue polling. Trial expiry, payment failure, and cancellation pause both new intake and non-terminal existing tracking. This merchant-only operational policy is separate from carrier status and customer timelines.
Reference Rates
/api/v1/billing/reference-ratesThis merchant-authenticated route returns { data: { baseCurrency: "AUD", rates, rateDate, rateSource: "Frankfurter", ratesStale } }. rates is a map of positive currency rates and always includes AUD: 1 and a current USD quote. Frankfurter quote dates can differ legitimately, so each quote is freshness-validated independently, optional out-of-window quotes are omitted, and rateDate is the oldest accepted YYYY-MM-DD date. The API caches the provider snapshot server-side, permits only bounded stale reuse, and briefly backs off after a failed refresh when no usable cache remains. Cached fallback use is reported through ratesStale.
The route accepts no location or IP input and does not determine tax, contract currency, or the amount Stripe will offer. The dashboard uses a selected rate to produce one direct converted estimate from the AUD catalogue amount labelled Excluding GST, and labels the estimate Excluding Tax. Exact AUD amounts labelled Including GST and Excluding GST remain available from the billing response, and Checkout remains authoritative.
Create Checkout
/api/v1/billing/checkoutOwners/admins provide a Pro planSlug from pro_50 through pro_20000, billingInterval (monthly or annual), and absolute successUrl/cancelUrl. The generated Checkout Session displays the selected shipment allowance beside the confirmation button. Monthly plans show shipments per month; annual plans show the full yearly balance, monthly equivalent, and upfront-allocation behavior. Checkout enables automatic tax, requires a billing address, saves the entered billing address and name to the Stripe Customer, and offers supported business tax-ID collection. These details are collected for paid billing rather than at free registration. For an ordinary non-exhausted trial, Checkout carries the account's unused trial time into the first Stripe subscription and schedules the first charge for the existing trial end when at least 48 hours and 1 minute remain. The dashboard begins warning shortly before that cutoff so a page-clock tick cannot hide an immediate charge. Checkout never grants a second trial. An account that already has a live Stripe subscription is directed to its existing billing flow instead of creating a duplicate subscription.
Start Paid Access or Renew an Exhausted Period
/api/v1/billing/renew-quota/preview/api/v1/billing/renew-quota/confirmThese owner/admin routes support two Stripe-backed access-restoration modes. start_paid_plan applies when a Stripe trial is exhausted or expired; successful confirmation ends the trial and starts its selected paid allowance immediately. renew_period applies when an active paid subscription exhausts its allowance; successful confirmation purchases a full new recurring period with no unused-time proration credit, resets the billing period, and renews that allowance. Normal Stripe discounts, tax, and customer balance may still affect the amount due. Both routes reject a non-Stripe billingProvider before contacting Stripe or consuming a confirmation token. Cancelled, payment-failed, and permanent-free accounts are not eligible for these modes.
Preview requires no body and returns mode (start_paid_plan or renew_period), an estimated amountDue in the smallest currency unit, currency, expiresAt, currentPeriodEnd, quota, billingInterval, and a merchant-bound confirmationToken. The Redis-backed token expires after 10 minutes.
Confirm accepts { "confirmationToken": "UUID" }. The token binds the merchant, live subscription period, Stripe price items and their quantities, currency, quoted amount, and mode. Preview and confirm reject local-vs-live plan, interval, or quota drift. Confirm retrieves the live subscription and recalculates the same full-period preview; period, item, quantity, currency, or amount drift is rejected before payment. start_paid_plan ends the trial, while renew_period resets the billing anchor with no unused-time proration credit. Success returns the completed mode, refreshed subscription, and releasedShipments. Payment failure returns 402 BILLING_PAYMENT_REQUIRED; no held shipment is released, and the merchant must update the payment method and request a new estimate.
Billing Portal
/api/v1/billing/portalOwners/admins provide an absolute returnUrl. The route requires billingProvider: "stripe"; platform-managed accounts use their platform billing UI instead. Billing-address and supported tax-ID updates depend on the active Stripe Customer Portal configuration and should be enabled and verified in both test and live mode.
Canonical Pricing
Canonical plan prices are listed in AUD. Exact AUD amounts use the labels Including GST and Excluding GST. Where a currency selector is available, the selected currency becomes the plan-card headline. Available USD, EUR, GBP, CAD, and AUD options appear in that order under Popular; remaining currencies are alphabetical under All currencies, without duplicates. A non-AUD headline is one direct daily reference-rate estimate from the AUD amount labelled Excluding GST and is itself labelled Excluding Tax. The exact AUD price remains visible, and Stripe Checkout sets the final currency, tax, and amount.
| Monthly shipment quota | Monthly (AUD, Excluding GST) | Monthly (AUD, Including GST) | Annual (AUD, Excluding GST) | Annual (AUD, Including GST) |
|---|---|---|---|---|
| 50 | A$17 | A$18.70 | A$170 | A$187 |
| 100 | A$27 | A$29.70 | A$270 | A$297 |
| 200 | A$45 | A$49.50 | A$450 | A$495 |
| 500 | A$71 | A$78.10 | A$710 | A$781 |
| 1,000 | A$125 | A$137.50 | A$1,250 | A$1,375 |
| 2,500 | A$269 | A$295.90 | A$2,690 | A$2,959 |
| 5,000 | A$489 | A$537.90 | A$4,890 | A$5,379 |
| 10,000 | A$899 | A$988.90 | A$8,990 | A$9,889 |
| 20,000 | A$1,699 | A$1,868.90 | A$16,990 | A$18,689 |
The Stripe webhook is a public, Stripe-signature-authenticated internal receiver; clients should not call it directly. Lifecycle events are serialized per Stripe subscription, reconciled from live invoice/subscription state, and successful event IDs are retained for 31 days so delayed retries cannot repeat side effects.