# Approval consumption API Status: implemented under Gate House decision `GH-DEC-2026-003`. The normative protocol is `gate-house/docs/contracts/approval-consumption.md`. This document records the approval-engine implementation surface; it does not redefine the protocol. ## Endpoint ```text POST /v1/approvals/{id}/consume ``` ```json { "request_digest": "sha256:<64 lowercase hex>", "decision_id": "decision:optional-provenance" } ``` The caller is the PEP that is about to perform the protected side effect. It calls consume after an ALLOW and before that side effect. `request_digest` is the canonical digest from the PDP decision binding, not a newly serialized request and not approval-engine's native binding digest. ## Results - First valid consume: atomically stores the digest, changes `approved` to `consumed`, and inserts one `approval.use` outbox row in the same transaction. - Same digest after consumption: `200` idempotent success and no second outbox row. - Different digest after consumption: `409 conflict`; the PEP must not act. - Revoked, superseded, expired, outside-window, requested, or unknown object: conflict or not-found; the PEP must not act. - Outbox insert/store failure: `503`; the transaction rolls back and the PEP must not act. There is no unconsume, release, or reserve. If the protected side effect fails after consumption, the approval remains spent and a retry needs a new approval. The response is mutation evidence, not a permission decision. It contains no `effect`, `allow`, `deny`, or decision result. ## The claim response is not an `ActionAuthorization` `GET /v1/approvals/{id}/claim` serves the **approval-claim** envelope defined in [`approval-claim.md`](approval-claim.md). It is not the `ActionAuthorization` object that secrets-engine's `validate_action_authorization` currently expects, and a PEP that points that validator at this URL fails closed for the wrong reason. Both envelopes carry `schema_version: "0.1"`, so the version check passes and the mismatch surfaces later as a missing-field or wrong-authority error. Do not read that failure as an approval-engine outage. | Validator expectation | What the claim actually serves | | --- | --- | | `id` (canonical UUID) | `approval_id` — the object id, same value, different key | | `status == "approved"` | `state` (`approved`, `consumed`, `revoked`, `superseded`, `expired`, `requested`) plus the `valid_now` predicate | | `superseded_by` | absent; supersession appears as `state: "superseded"` | | `provenance.authority == "state-hub"` | `issuer: "approval-engine"` — this engine is the authority for the approval object; the State Hub is a read model and never issues one | | `request` (full CheckRequest) | `binding` (`action`, `actor`, `principal`, `purpose`, `target`) plus `binding.digest`, and `binding.pdp_digest` when recorded at issue | | `approvals.required_count` / `entries` | not exposed; the distinct-approver threshold is already folded into `valid_now`, with `reason_code: "insufficient_approvers"` when unmet | | policy package/version pin | not carried; the policy pin belongs to the access-engine decision, not to the approval fact | The omissions are deliberate. A claim is a *fact about an approval object*, not a decision and not a permission; approver identities and policy pins are not republished to consumers. The consumer checks in [`approval-claim.md`](approval-claim.md) ("Required verification") are the supported validation path. **Settled 2026-09-06 by `GH-DEC-2026-005`** (with `FLEX-DEC-2026-006`). The approval-claim is the step-1 artifact; `ActionAuthorization` is not required on this path and **MUST NOT** be served from the claim endpoint. A PEP validates across two artifacts: the claim for the approval fact, and the step-2 flex-auth `DecisionEnvelope` for exact `CheckRequest` match and the policy pin. Gate House recorded the split as doctrine rather than as an implementation convenience: each artifact is validated against the layer that owns its data, and **a PIP MUST NOT republish the PDP's decision**. The `provenance.authority == "state-hub"` requirement was struck explicitly — State Hub is a read model holding no runtime approval authority, so a validator requiring it fails closed against every correctly issued claim. This engine's claim schema is unchanged by the ruling.