Every usage read is already tenant-scoped
GET /v1/platform/customers/{customer}/usage returns that customer’s workspace consumption for a billing period (YYYY-MM). Pass your platform key; no extra header is required because the customer is in the path.
period defaults to the current month. The response is the workspace’s usage analytics for that month, plus the customer’s billing fields. Abridged:
totals, byCategory, …) are camelCase; the customer billing fields are snake_case. Amounts in totals and the breakdowns are decimal strings in major units, six places.
This example is a customer-paid number, the default. metaCharges is what Meta’s rates come to for these messages — Meta invoices the business for it directly. totalCharge is what LazyApp charged, and it is 0 because nothing passed through your wallet. On a partner-managed number, totalCharge is Meta’s rate, plus 5% when the WhatsApp account is not billed in USD and a further 5% for messages not sent through LazyApp.
wholesale_total, marked_up_total and markup_bps are no longer returned. LazyApp adds nothing to Meta’s rates and platform customers have no markup, so there is nothing to mark up.usage summary for the current month: period, messages and total_charge.
List customers via GET /v1/platform/customers when you need a portfolio view.
Don’t proxy every dashboard render to the API
The tempting architecture — every widget on your customer’s billing page calls LazyApp on load — burns your per-key rate limit and returns data that only changes when messages send. The pattern that works: serve dashboards from your own database, and refresh it with a background sync worker (plus webhooks for alerts):The sync-worker pattern
One worker with a small concurrency cap, iterating your tenants:
On a
429, wait and retry. A concurrency cap of around five keeps a worker well inside your per-key rate limit.
Subscribe to customer.usage_alert so a tenant approaching usage_alert_cents is flagged without polling.
Show limits, not fake real-time
Surface what the API gives you:
Whatever you charge your own customers is yours to calculate and invoice; LazyApp does not hold a price for it.
Sends refused for billing return
402 — handle that in the messaging path so the tenant sees a clear reason.
Checklist
- Dashboards read from your database; a worker fills usage
- Path-scoped
GET …/usageper customer; portfolio list when needed -
customer.usage_alertwebhook for threshold crossings - Handle
402on send as a billing state, not a transient error - Don’t invent social engagement charts — this product is WhatsApp usage
Related
- Build a platform — billing modes and lifecycle
- Meta message billing — how Meta charges for messages
- Tenant messaging — where
402shows up on send - Rate limits — per-key budget
- Webhooks — usage alert event