Skip to main content
Metering is two calls from your backend: check before an action asks whether the customer can afford it, and track after it succeeds spends the credits. The examples continue from Setup: a chat_message feature that costs 1 credit, and a customer on the Free plan.

Check, run, track

  • check never spends. It returns { "allowed": true, "availableCredits": 50, "creditCost": 1 }, and allowed: false with a 200 when the balance can’t cover the cost. That’s the moment to offer an upgrade or a credit pack.
  • Track only what succeeded. If the action failed, don’t track it and the customer isn’t charged.
  • The request ID makes track idempotent. Use an ID you already store with the action: a message ID, a job ID, or a UUID you save with it. Up to 255 characters, and one ID belongs to one feature.
  • Retry with the same ID. If a track response is lost, send it again with the same requestId. Ledger returns the original result with "idempotent": true and charges nothing more. A new ID records a second use.
track returns what it spent:
Response

Handle a refused track

check doesn’t reserve credits, so another request can spend the last credit between your check and your track. track decides again in its own transaction and can refuse with a 409. Nothing is charged, and the same request ID can succeed later, for example after the customer buys a pack.
A 404 customer_not_found or feature_not_found means the customer or feature doesn’t exist with this key’s environment. Errors lists every code.

When Ledger is unreachable

The SDKs fail open by default: check is allowed and track is replayed later with the same request ID, both marked failedOpen: true, so an outage never blocks your users. Pass failOpen: false (Python fail_open=False) to handle outages yourself. Call Ledger from your backend only, never from a browser or mobile app. Usage in the dashboard lists every tracked request, charged or blocked, with the reason.
Other ways. Without an SDK, call POST /v1/check and POST /v1/track; over HTTP the request ID field is request_id.