How it works
app / agent ──▶ proxy ──▶ provider
│ 1 authenticate the virtual key (cached 5 s)
│ 2 price: SKU from path + field mask, worst case in micro-dollars
│ 3 one Lua call: kill switch, loop breaker, reserve against every cap
│ 4 forward with the real key (decrypted locally, never logged)
│ 5 settle the actual cost + usage event (one Lua call, background)
▼
Valkey ──▶ worker ──▶ Postgres ledger, alerts ──▶ notifier ──▶ webhooks / email
- Authenticate. The virtual key comes from
X-Brake-Key, or in place of the provider’s key (?key=sb_live_…,X-Goog-Api-Key: sb_live_…and so on). Key and org config are cached for 5 seconds. - Price. The provider adapter works out the SKU from the path, the field mask and the body, and the worst-case cost in micro-dollars, from price files that cite their source.
- Reserve. One Lua call on Valkey checks the kill switch and the loop breaker, then reserves the worst case against every cap that applies: org, key, SKU, environment, agent and session, per day and per month. Parallel requests cannot slip past together.
- Forward. The request goes to the provider’s fixed origin with the real key injected.
Every
X-Brake-*header, client credential and cookie is removed. Responses stream through; nothing is cached. - Settle. In the background, the actual cost replaces the reservation and a usage event is appended for the ledger. If settling fails, a sweeper commits the reservation at its worst case.
Overshoot is bounded by one request’s worst case. If Valkey is down, the proxy fails closed
(503) unless a key opts into a small fail-open allowance; fail-open spend is recorded as soon
as Valkey answers again.
Twilio sends are settled later, at the price Twilio reports for the message. The difference to
the worst case shows up as an adjustment event.
Caps
A cap belongs to a scope and a window. Scopes:
| Scope | Example |
|---|---|
| The whole org | org |
| One virtual key | key:key_ab12 |
| One SKU | sku:google-maps:geocoding |
| An environment | env:production |
| An agent | agent:claude |
| An agent session | sess:sess_ab12 |
Each scope can have a hard cap per day and per month (the request is refused) and a soft limit (alerts only). Daily windows follow the org’s time zone.
Kill switch
brake kill <target>, POST /v1/kill, a dashboard button or an alert link stops all spend for
the org (org), a key (key:<id>), a provider (prov:<name>) or a session (sess:<id>)
immediately. unkill lifts it.
Messaging (Twilio)
On top of dollar caps, message sends are checked before any money moves:
- Countries: a key may only send to the countries in its
countrieslist; without a list, only the US and Canada. - Pumping: at most 5 sends per destination number and 50 per number prefix per hour per key.
Going over opens the key’s circuit for an hour (
429 loop_detected,reason: "pumping") and raises a loop alert.
Responses
Every response carries X-Brake-Request-Id and X-Brake-Cost-Micros. Requests that were
reserved also carry X-Brake-Remaining-Micros: the smallest headroom left across the request’s
capped scopes, omitted when nothing is capped. Fail-open requests add X-Brake-Fail-Open: 1.
| Status | error |
When |
|---|---|---|
| 401 | invalid_key |
Missing, unknown, revoked, expired key, or IP not allowed |
| 402 | budget_exceeded |
scope, window, cap, spent (committed + held), resetsAt |
| 402 | unpriced_route |
No price and the org’s policy is deny |
| 403 | killed |
target is in the kill set |
| 403 | sku_not_allowed |
SKU or provider not allowed for the key |
| 429 | loop_detected |
Circuit open; retryAfter and Retry-After |
| 503 | budget_unavailable |
Valkey unreachable and the key is fail-closed |
| 400 | invalid_request |
Malformed X-Brake-Session or body too large |
| 404 | unknown_route |
No provider for the path prefix |
| 502 | upstream_unavailable |
Provider unreachable (refunded) |
| 504 | upstream_timeout |
Provider timed out (committed at worst case) |
Every error body has a one-line message an agent can act on.
Provider terms
Spend Brake is built to stay inside the Google Maps Platform Terms of Service:
- Bring your own key, always. Each org uses its own Google key and billing account; Spend Brake never resells access or shares a key.
- No caching to cut billing. Responses are streamed through, never stored to answer later requests. Spend Brake stores only request metadata (SKU, units, cost, status, a hash), never Place IDs, coordinates, bodies or query strings.
- Usage stays visible to Google. Every request reaches Google with your key.
- Client-side Maps is out of scope. The Maps JavaScript API, tiles and client widgets are not proxied.
This is not legal advice: have someone read sections 3.2 and 3.3 of the Maps Platform Terms and the Service Specific Terms before you offer Spend Brake to others as a hosted service.
The parts
| Part | What it does |
|---|---|
| apps/proxy | The request path |
| apps/api | Admin API (keys, caps, kill, credentials, spend, events, webhooks, sessions) |
| apps/worker | Ledger, sweeper, alert jobs, partitions, price drift check |
| apps/notifier | Signed webhooks, email, action-link pages |
| apps/cli | brake: init, agent sessions, capped calls, admin |
| apps/web | Dashboard |
| packages/budget | Atomic budgets on Valkey (Lua) |
| packages/catalog | Prices, SKU rules, provider adapters, migration estimator |
| packages/db | Postgres schema and migrations |
| packages/shared | Envelope encryption, signing, redaction, telemetry |