Requests & billing
Quote, reserve, dispatch, capture. Every billable request follows the same lifecycle and ends with a receipt.
Quotes
A quote locks the price for a specific input for a few minutes. It returns an exact amount, or a ceiling when the supplier meters usage (is_ceiling: true).
curl https://api.uselocal.sh/v1/quotes \
-H "Authorization: Bearer $LOCAL_API_KEY" -H "Content-Type: application/json" \
-d '{"tool":"{provider}/{slug}","input":{...}}'
# → { quote_id, tool, tool_version, pricing_mode, amount_usd, is_ceiling, currency: "USD", expires_at, request_hash }Pass quote_id to execute. The input must hash to the same request_hash or you get 409 quote_mismatch; after expires_at you get 409 quote_expired.
Execute
POST /v1/tools/{provider}/{slug}/execute
Headers: Authorization, Idempotency-Key (required), Content-Type: application/json
Body: { input, quote_id? , max_charge_usd?, project_id?, async? }
→ 200 ExecuteResponse | 202 ExecuteResponse with "job" | error envelopeWithout a quote, max_charge_usd is your hard ceiling. We reserve min(ceiling, price) before dispatch and never charge above it. If the ceiling is lower than the tool’s minimum charge you get 422 ceiling_too_low and nothing is reserved.
Idempotency
Idempotency-Key is required on every billable call. The scope is (workspace, operation, key). Retrying with the same key and the same body replays the original outcome without charging again. Reusing a key with a different body returns 409 idempotency_conflict. A key whose first attempt is still running returns 409 idempotency_in_progress.
Recommended pattern
Generate one UUID per logical attempt. Retry network failures with the same key. Generate a new key only when the input changes.
Billing states
| State | Meaning | Final |
|---|---|---|
none | Nothing was reserved (validation failed, free tool, or rejected before pricing). | Yes |
reserved | Funds held, dispatch pending or in progress. | No |
captured | Final charge posted; the remainder of the hold was released. | Yes |
released | Confirmed unbilled failure; the hold was returned. | Yes |
reconciling | Outcome uncertain (timeout or ambiguous supplier response). We check the supplier receipt and then capture or release. | No |
manual_review | Could not be resolved automatically; an operator decides. Funds stay held until then. | No |
Dispatched reservations are never released by a timer. A request in reconciling shows “We’re checking this request’s final charge.” in the dashboard until it is resolved.
Receipts
GET /v1/requests/{request_id} returns the request record with receipt: { charged_usd, reserved_usd, final, note }. GET /v1/usage lists requests with filters and totals; add format=csv for an export.
Response headers
X-Local-Request-Id— always present; quote it when contacting support.X-Local-Charged-Usd,X-Local-Billing-State,X-Local-Reserved-Usd— on billable responses.