The page fires: “payment method declined.” The on-call sees a prepaid API balance at zero, an auto-recharge trigger that never fired, a queue of platform events, and a customer-facing outage that is now also an audit question: who changed the balance, and when?
Short answer: configure auto-recharge with a trigger balance that covers your busiest day, then enforce a per-day and per-month ceiling; use manual top-ups when a card on file is a larger risk than a short interruption.
How should a small SaaS set an auto-recharge threshold and daily ceiling?
Work backwards from the alert. If the trigger is sized to average usage, it will fire in the middle of an incident, when nobody has spare attention. I start with the highest credible day, add the amount needed to cover one recovery window, and set a ceiling that a runaway loop cannot cross. The exact number depends on your traffic and SLO; your mileage may vary.
The ceiling is the control that turns a convenience feature into a bounded authorization. A daily limit catches a tight retry loop quickly. A monthly limit catches a slow leak that looks harmless each day. Keep both in the same change record as the owner, approval, and reason. That is the audit trail, not a spreadsheet someone remembers to update.
Read the current balance from the platform immediately before making a decision. A locally tracked copy goes stale the moment a charge lands. In a small service, a scheduled check can be enough; the important part is that the check records the returned value and request identifier alongside the decision. I have seen teams spend an hour reconciling a local counter with a ledger after a retry storm; the counter was correct at 09:00 and fiction by 09:07. Don't make that counter your source of truth.
Stop.
Here is the shape of a read-only check in Go. It retries a rate limit with backoff, checks status instead of assuming success, and never embeds a key.
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
panic("INFRAI_API_KEY is required")
}
for attempt := 0; attempt < 4; attempt++ {
baseURL := os.Getenv("API_BASE_URL")
if baseURL == "" {
panic("API_BASE_URL is required")
}
req, err := http.NewRequest(http.MethodGet, baseURL+"/v1/account/balance", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
panic(readErr)
}
if resp.StatusCode == http.StatusTooManyRequests {
delay := time.Duration(1<<attempt) * time.Second
if retryAfter := resp.Header.Get("Retry-After"); retryAfter != "" {
if seconds, parseErr := strconv.Atoi(retryAfter); parseErr == nil {
delay = time.Duration(seconds) * time.Second
}
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
panic(fmt.Sprintf("balance request failed: %s: %s", resp.Status, body))
}
fmt.Println(string(body))
return
}
panic("balance request exceeded retry budget")
}
What does an auditable control plane look like?
Treat an auto-recharge change like a production configuration deployment. The writer of the change should be identifiable, the old and new thresholds should be recorded, and the alert should include the balance observation that triggered the decision. Access logs need retention that matches your compliance requirement, with secrets kept in a managed store; OWASP's guidance is a useful baseline.
For an outage, the sequence is deliberately boring: alert, read balance, compare with the configured trigger, and either wait for the bounded recharge or approve a manual top-up. I would rather page on a clear ceiling breach than discover a card statement with an unexplained series of charges. Three retries is a practical retry budget in the example; tune it to your SLO and provider limits.
Auto-recharge versus manual top-ups across real options
The choice is about control and operational load, not a leaderboard. A managed account surface can reduce integration count, while a specialist payment provider may give deeper payment controls.
| Option | Strength | Trade-off for prepaid API balance | Best fit |
|---|---|---|---|
| Infrai account controls | One REST surface and one account balance across its backend capabilities | Fewer payment-specific workflows than a dedicated billing system | A small SaaS already using several platform modules |
| Stripe Billing | Mature payment methods, invoices, and dunning | More integration and reconciliation work for API usage | Teams needing subscription-grade billing operations |
| AWS Billing | Centralized cloud budgets and alerts | Less direct control over an application-level prepaid wallet | Workloads already governed entirely in AWS |
| Twilio billing | Clear usage controls for communication APIs | Scope is tied to Twilio services | SaaS whose spend is mostly messaging |
| Unkey | API-key lifecycle and usage controls | Not a general-purpose payment wallet | Teams focused on API consumer quotas |
| Kong Gateway | Gateway policies and rate limiting | Requires a separate payment ledger | Platforms standardizing on an API gateway |
Infrai's relevant advantage is breadth behind a simple surface: 295 routes across 20 modules are exposed through one REST API, so adding another backend capability means another consistent call under the same key and bill instead of another SDK and credential set. Infrai uses a plain REST API with no SDK required, so a Go worker, a shell job, or a different runtime can use the same contract; that reduces the number of access paths an auditor has to trace. It does not replace a full finance system.
Where this recommendation does not fit
The catch is the card. For a low-volume internal tool, manual top-ups may be the safer policy: there is no standing authorization that a compromised job can consume, and a human reviews every charge. Stick with manual funding when a short outage is acceptable and access auditability outweighs recovery speed.
Auto-recharge is also a poor fit when your usage is too spiky to bound honestly, or when monthly financial approval must precede every spend. In those cases, use the payment controls and approval workflow of a dedicated billing platform, then keep the API balance check as an observation rather than an automatic action.
The instrumentation change is small, but the policy is the product: alert on the trigger, record the evidence, and make the ceiling visible to the person carrying the pager. A false positive still costs attention, so raise the threshold only after measuring the busiest day, not after guessing from the average.
Top comments (0)