Open Spanner
Tutorials

Enforce Entitlements

Check or atomically consume a subject's plan quota.

First create a meter, add it as a plan limit, assign a subject to the plan, and create an API key with plans:read and usage:write.

Use atomic consumption for product actions that must remain correct under concurrency. Use a separate check for previews and advisory UI; quota can change between a check and a later usage write.

Atomically Consume Quota

POST /v1/entitlements/consume decides and records in one transaction. Give every product action a stable idempotency key so network retries return the original decision without consuming twice.

curl -X POST "$BASE_URL/v1/entitlements/consume" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotency_key": "api-call-001",
    "subject": "org_123",
    "meter": "api_calls",
    "quantity": 1
  }'

The response contains accepted, replayed, the recorded event when accepted, and the quota decision. A hard limit returns HTTP 429 without recording usage when the projected value exceeds the limit. An advisory limit accepts the event and reports the warning or exceeded state.

Open Spanner persists accepted and rejected decisions. Reusing an idempotency key returns its original result even if quota later resets. Generate a new key only for a new product action.

Preview Availability

POST /v1/entitlements/check evaluates without recording usage:

curl -X POST "$BASE_URL/v1/entitlements/check" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "org_123",
    "meter": "api_calls",
    "quantity": 1
  }'
FieldMeaning
allowedWhether the requested quantity fits the active policy.
stateok, warning, exceeded, no_plan, or not_in_plan.
currentUsage already recorded in the active period.
remainingUnits remaining after the requested quantity.
plan_namePlan used for the decision.
from / toUTC quota period.

warning still allows usage. exceeded, no_plan, and not_in_plan deny it. If a preview is allowed, record the accepted action with POST /v1/usages and a stable idempotency key.

Failure Policy

Use failure_policy: fail_open when preserving usage facts is more important than enforcing quota during an evaluation failure. Use fail_closed when the action must be rejected unless quota can be verified. The defaults are advisory enforcement and fail_open.

Read Progress And Decisions

curl "$BASE_URL/v1/plans/subjects/org_123/progress" \
  -H "Authorization: Bearer $API_KEY"

curl "$BASE_URL/v1/entitlements/states?subject=org_123&meter=api_calls" \
  -H "Authorization: Bearer $API_KEY"

curl "$BASE_URL/v1/entitlements/decisions?subject=org_123&meter=api_calls" \
  -H "Authorization: Bearer $API_KEY"

Progress is useful for customer-facing quota views. State events show warning and exceeded transitions. Decision history provides an immutable audit of accepted, rejected, and replayed consumption attempts.

Language-specific consume, check, progress, and decision clients are documented under SDKs. Runnable examples live in examples/rest/advance/entitlement-check and examples/stream/advance/entitlement-gate.

On this page