Lock credits for an action
/credits/lockSynchronously reserves the credits for one billable action before the caller performs it. The handler runs a sufficiency check — (subscription remaining + on-demand remaining − already-locked reservations) ≥ action cost — and, when it passes, inserts a lock row reserving the cost. The action's cost is read from the action_types table (Redis-cached), so pricing changes need no API change.
Idempotency: the lock is keyed by the caller-supplied operation_id (your stable record id). Retrying the same operation_id does not create a second reservation and returns the same 200 — safe to retry on timeouts.
Allocation gate: while a credit (re)allocation for the Business Unit is in progress (Redis gate), the handler briefly re-polls (~4s total) and then returns 409 with retry: true — retry once the allocation settles.
A reservation stays held until it is settled via POST /credits/settle. There is no auto-expiry yet: a never-settled lock keeps its credits reserved.
Authorization
creditsApiKey Static bearer key for the Credit API, shared per-service (the CREDITS_API_KEY SST secret — the calling service is provisioned with the same value). The header must be exactly Authorization: Bearer <CREDITS_API_KEY>. This is an application-level check (no API Gateway authorizer) and it fails closed: if the secret is unset, every request is rejected with 401.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/credits/lock" \ -H "Content-Type: application/json" \ -d '{ "name": "request_info", "business_unit_id": 42, "operation_id": "request-info-789" }'{ "locked": true, "operation_id": "request-info-789", "amount": 1}{ "error": "invalid_request", "issues": [ { "path": [ "business_unit_id" ], "message": "Invalid input: expected number, received string" } ]}{ "message": "Unauthorized"}{ "error": "insufficient_credits", "required": 1, "available": 0}{ "error": "credits_allocation_in_progress", "retry": true}{ "message": "Internal server error"}