Skip to main content

Burn rate and wallet runway

Orbit bills from a prepaid wallet. Every message, voice minute, and AI token debits the balance as it happens, and every debit lands in the transaction ledger behind GET /api/v1/billing/transactions. Because the wallet drains with every send, the question a finance operator actually asks is not “what did we spend this month?” but “at this pace, when does the wallet hit zero?” Burn rate is that answer as a number you can poll. GET /api/v1/billing/burn-rate returns your average daily spend over a lookback window, the live wallet balance, and a projection of how many days of balance remain at that pace. It is a read-only forecast: it reports the pace, and it never throttles sending, never tops up the wallet, and never sends a notification by itself. When you want the platform to act, pair it with spend caps and usage anomaly alerts, covered below.

1. Why burn rate is a first-class metric

A prepaid wallet makes runway an operational concern. A wallet holding 1,500thatspends1,500 that spends 42.50 a day has about 35 days of sending left; when the balance reaches zero, sends start failing for insufficient balance, mid-campaign if nobody was watching. Spend reports tell you where the money went after the fact. Burn rate tells you how long the current balance lasts before the hard stop, while there is still time to top up or slow down. It is deliberately different from the two guards that can act on their own:
  • A spend cap is a hard threshold you set. When spend crosses it, Orbit pauses outbound sending. It is deterministic and enforced, but it fires only at the line you drew, not before.
  • A usage anomaly alert notifies you when a metric crosses a threshold or deviates sharply from your learned baseline. It tells you something unusual is happening, not how long the wallet lasts.
  • Burn rate is a forecast. It divides the balance by the recent daily pace every time you ask, so a traffic increase shows up as a shorter runway on the next poll, days before any cap or alert would trip.

2. The computation window

The endpoint aggregates wallet debits over a caller-selected lookback window: Within that window:
  • Only debits count as spend. Top-ups and grants are excluded, refunds are excluded, and operator adjustments applied to your balance are excluded, so a large top-up yesterday does not read as “spending that much per day”.
  • The divisor adapts to young accounts. If your first spend inside the window is more recent than the full window, the average divides by the days actually elapsed (at least one) instead of the whole window. The response’s lookback_days tells you which divisor was used, and first_spend_at gives the first spend timestamp in the window.
  • The projection is balance divided by pace. projected_days_remaining is the live balance divided by the average daily spend, truncated to one decimal place. It is null when there is no recent spend, because there is no pace to divide by; dashboards render that as “no recent activity”.
  • Results are cached for about a minute. Polls inside that window return the same snapshot, so a dashboard that re-fetches on focus stays cheap and consistent. A poll also never observes a partially computed answer: concurrent polls share one computation.
  • A data blip degrades to zeros, never to an error. If the billing data behind the endpoint is briefly unavailable, the endpoint still returns 200 with zeroed projections rather than a 5xx, and the degraded snapshot is not cached, so the next poll recomputes and self-heals. Treat an all-zero response right after a healthy one as “retry in a minute”, not as “spend stopped”.

3. How the balance enters the forecast

The two halves of the ratio come from different places, and both are the same numbers you see elsewhere in Orbit:
  • current_balance_minor is the live wallet balance, the same value GET /api/v1/billing/balance and the dashboard balance widget report. If that live read is itself unavailable, the response reports a zero balance, and therefore zero days remaining, rather than an error.
  • avg_daily_spend_minor is computed from the transaction ledger, the same ledger GET /api/v1/billing/transactions pages through.
Because the balance is read live on every poll while the spend average is the cached window aggregate, a top-up extends projected_days_remaining on the next poll even while the spend side of the ratio is still within its cache minute. All *_minor fields are integer cents of the wallet currency (USD); divide by 100 for dollars. Top-ups paid in another currency are converted into the wallet on the way in (see wallet currency conversion), so the runway math is always single-currency.

4. Endpoint contract

Requires an owner, admin, or billing role. An off-spec days value degrades to the 30-day default rather than rejecting the request. Response fields, inside the standard { data, meta } envelope: Each by_channel row carries channel, month_to_date_minor, last_7d_total_minor, and projected_month_end_minor (the same month-to-date plus last-7-day-pace formula, per channel). Channel names are sms, mms, whatsapp, voice, email, rcs, agents, verify, numbers, marketplace, and other; the rows always sum back to the headline projected_month_end_minor.
200

5. How it relates to spend caps and anomaly alerts

The three instruments answer different questions, and a mature billing setup uses all three: burn rate to see the runway, anomaly alerts to hear about a sudden change in pace, and spend caps as the hard stop that enforces the limit you are comfortable with. The division of labor matters. A spend cap set at $2,000/month says nothing about whether the wallet survives to month end; a burn-rate poll on day 20 saying “6 days of balance left” says nothing about whether the pace is anomalous. An anomaly alert on a spend surge catches the compromise or misconfiguration that a steady, legitimate ramp will not trip, while the burn-rate projection catches the legitimate ramp that no baseline will ever flag. Because burn rate never acts, nothing on this page can pause your traffic: the forecast is advisory until you, or a cap you configured, decides otherwise.

6. Example: chart runway in Grafana

The endpoint is built to be polled. A common pattern is an hourly cron that publishes projected_days_remaining as a Prometheus gauge via the node exporter textfile collector, then a Grafana panel and alert rule on top:
In Grafana, alert when the gauge stays below your replenishment lead time (for example 7 days) for a few consecutive hours, so a single zero-spend poll or a brief degraded snapshot cannot page anyone. The same poll can feed projected_month_end_minor into a month-end forecast panel next to your committed spend budget. Remember the contract while wiring alerts: a zeroed response during a billing-data blip is a 200, and null means “no recent spend”, not “exhausted”. Alert on sustained values, and keep the real hard stop where it belongs, in a spend cap.