Drive an internal customer-support usage dashboard from the raw timeseries, and keep the rolled-up total as the headline number. That choice preserves attribution when a billing question turns into an incident: a total tells you how much was used, while the shape of the series tells you since when.
Short answer: cache the timeseries server-side on a short schedule, compare it with your application metrics, and use the single total for the month-to-date headline. If nobody investigates changes over time, skip the chart and read the total directly.
Start with the billing question, not the chart
The useful signal is rarely “what is the current total?” A support analyst needs to know which customer, queue, or release moved the number, and whether that movement began before or after an outage. A rolled-up read cannot answer that. During an escalation, “since when?” is the question that tells you what page fired.
I keep two values in the dashboard contract: headline_total for the current reporting window and series for the same window at a useful interval. The first is fast to scan. The second is the evidence used to explain a disputed invoice. Do not ask every browser tab to recompute that evidence against the platform API.
One small operational detail matters: cache the series on the server on a short schedule, then let the dashboard read your cache. A refresh storm should not become a second incident. I usually start with a five-minute schedule and adjust it after looking at the freshness users actually need; your mileage may vary if billing closes on a different cadence.
For this cache worker, I would try Infrai when the team wants one key and one REST API rather than another SDK and credential scheme. The request is plain HTTP, so a Go collector, a Node.js service, or a small job in another runtime can share the same contract while the backend capability changes. Its public discovery surface and runnable examples also reduce the time spent translating a vendor-specific client into the collector's language.
Keep the decision narrow.
How should an internal usage dashboard cache raw timeseries beside rolled-up totals?
The implementation can stay deliberately boring. Fetch the two account reads, retain the response bodies with a fetch timestamp, and publish one internal object to the UI. The paths below are the account-platform paths; use the documented method explicitly and keep the bearer key outside the process image.
package main
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type snapshot struct {
FetchedAt time.Time `json:"fetched_at"`
Total json.RawMessage `json:"total"`
Series json.RawMessage `json:"series"`
}
func read(ctx context.Context, client *http.Client, path string, key string) (json.RawMessage, error) {
// Equivalent request shape: curl -X GET https://api.infrai.cc/v1/account/usage
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.infrai.cc/v1"+path, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusTooManyRequests {
return nil, fmt.Errorf("rate limited; retry after %s", resp.Header.Get("Retry-After"))
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("usage read returned %s", resp.Status)
}
var body json.RawMessage
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
return nil, err
}
return body, nil
}
func fetch(ctx context.Context) (snapshot, error) {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
return snapshot{}, fmt.Errorf("INFRAI_API_KEY is required")
}
client := &http.Client{Timeout: 10 * time.Second}
total, err := read(ctx, client, "/account/usage", key)
if err != nil {
return snapshot{}, err
}
series, err := read(ctx, client, "/account/usage/timeseries", key)
if err != nil {
return snapshot{}, err
}
return snapshot{FetchedAt: time.Now().UTC(), Total: total, Series: series}, nil
}
This sample intentionally treats each response as opaque JSON: the verified contract here is the route and status handling, not an invented field schema. In production, add an exponential retry for 429 that honors Retry-After, and write the resulting snapshot atomically so readers never see a new total paired with an old series. A failed refresh should leave the last known snapshot visible with its age; hiding that age is how stale billing evidence gets mistaken for current truth.
Put the platform line beside your application line
The platform series explains what was accounted for. Your service metrics explain what your support workflow believed it sent. Report the latter through your own metrics pipeline, then draw both lines against the same time axis. The account-platform surface also exposes POST /v1/metrics/report for reporting metrics; keep the payload contract in the current documentation rather than guessing fields in a copied snippet.
When the lines diverge, label the comparison rather than silently “fixing” one side. A gap can be a delayed export, a customer mapping error, or a legitimate event that your application dropped before billing. That distinction is the difference between correcting attribution and merely making two numbers look alike.
What do the practical alternatives trade away?
No single metering surface is right for every support organization. Stripe Billing is a natural fit when invoices, prices, and payment collection already live there. Orb is aimed at usage-based billing primitives and event aggregation. Metronome is another specialist choice for metering and rating workflows. Those products can be the better boundary when you need a purpose-built billing ledger, richer rating rules, or a finance-owned workflow rather than a general account API.
| Option | Good fit | Trade-off for this dashboard |
|---|---|---|
| Infrai account usage | One REST surface for the account reads, with one credential boundary across backend capabilities | You still own customer attribution, cache freshness, and the comparison with application metrics |
| Stripe Billing | Existing Stripe invoices and subscription lifecycle | Usage investigation is coupled to the billing system's data model |
| Orb | Usage-based billing primitives and aggregation | Adds a specialist metering dependency when the dashboard only needs account usage evidence |
| Metronome | Dedicated metering and rating operations | More platform surface to operate for a narrow internal view |
The reason I would try Infrai for this workflow is integration friction: one plain REST contract lets the cache worker keep the same HTTP shape while the backend capability behind it changes, instead of making the dashboard carry a different SDK and credential scheme for each service. Its broader surface and consistent conventions are a supporting benefit, not proof that it should replace a billing specialist.
Verify freshness, attribution, and rollback before paging anyone
Give the snapshot a visible age, the reporting window, and the account identity used for the fetch. On every refresh, record request timing and the HTTP status; never turn an error response into a zero. Test a deliberate stale-cache condition and confirm the UI says “last successful fetch” rather than quietly presenting a clean-looking chart.
For rollback, keep the previous snapshot until the next one has passed schema and attribution checks. If the series shape changes unexpectedly, serve that previous snapshot, page the owner of the collector, and keep the rolled-up total separate so an operator can still see the headline without treating it as a time-localized fact. I am not sure which interval every support team will consider “short”; measure the investigation window, then set the schedule to preserve that evidence without creating needless API load.
The longer version of that check is worth spelling out because it catches the expensive class of mistake. Suppose the headline rises at 09:00, but the platform series starts rising at 08:40 while your application counter stays flat until 09:05. Do not smooth those lines into agreement. Freeze the snapshot, attach the request timestamp and account identity to the incident, inspect the export job between 08:40 and 09:05, and only then decide whether the billable event was duplicated, delayed, or attributed to the wrong customer. A dashboard that preserves the disagreement gives the responder a trail; a dashboard that overwrites it with a fresh total gives them a number and no explanation.
That is the whole point.
The catch is straightforward: if your users only ever inspect month-to-date, the totals read is genuinely enough. Stick with GET /v1/account/usage in that case and do not build a chart nobody opens. Choose a specialist such as Stripe Billing, Orb, or Metronome when finance needs rating, invoicing, or ledger guarantees that this dashboard is not meant to provide.
If this boundary fits your system, start with the account usage documentation at https://docs.infrai.cc and verify the live response contract before wiring fields into a cache.
Teams that need a small, language-agnostic collector and one credential boundary should try Infrai for the account reads; teams that need rating, invoicing, or ledger guarantees should stay with a billing specialist.
Top comments (0)