> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mudraid.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Decide one protected action

> Called by an enforcement adapter — the V2 middleware's
`HttpDecideClient`, the Node adapter or sidecar, or the Kong plugin —
once per protected action, before the action is forwarded. The adapter
authenticates with its own per-adapter bearer (issued at adapter
registration). The caller's access token travels separately, inside
the body as `presented_authorization`; identity, tenant and scopes are
derived only from that verified token, never from other body fields.

The request's surface claims (`surface.canonical_resource_uri`,
platform, environment, bundle version and digest) are compared with
what MudraID has registered for the authenticated adapter. Any
authentication or binding failure answers one neutral `401`.

The decision runs a fixed gate order: contract → identity/tenant →
authority/delegation → containment → risk → entitlement → governance.
It is **deny-closed**: an authoritative negative is `deny`; a missing,
stale, conflicting or unavailable required fact, or a breached
deadline, is `not_safely_decided`; only a decision where every gate
passes on fresh facts is `allow`. An `allow` authorizes one forward
(`directive: forward_once`); every other outcome carries
`directive: block`. Adapters treat any non-`200` as "could not consult
the authority" and deny.

When the adapter sends an `execution` snapshot, the decision is bound
to the digest of the exact request bytes it will forward. Decision
responses can carry an RS256 `signature`; signing is a deployment
setting and is off by default, so do not assume a response is signed.




## OpenAPI

````yaml /openapi.yaml post /api/v1/adapter/enforcement/decide
openapi: 3.0.3
info:
  title: MudraID Public Contracts
  version: 2.0.0
  description: >
    This document defines the **public contracts** an integrator calls

    directly: the OAuth endpoints an agent's machine client uses to obtain an

    access token, the discovery documents a resource server verifies those

    tokens with, the scope file the FastAPI middleware loads, and the live

    decision endpoint enforcement adapters call. Administrative and internal

    endpoints are intentionally omitted — they are documented by each FastAPI

    service's auto-generated `/openapi.json` at runtime and may evolve more

    freely.


    What lives in *this* file:

      - `POST /oauth2/token` — machine-client `client_credentials` exchange
      - `POST /oauth2/revoke` — RFC 7009 token revocation
      - `GET /.well-known/oauth-authorization-server` — RFC 8414 metadata
      - `GET /.well-known/jwks.json` — token-verification keys
      - `GET /api/v1/platforms/{platform_id}/scopes.yaml` — scope file for the middleware
      - `POST /api/v1/adapter/enforcement/decide` — live decision for one protected action
      - the access-token claim shape and the `mudraid_scopes.yaml` schema

    **Retired native agent credentials.** `POST /api/v1/auth/token`,

    `POST /api/v1/auth/verify`, `POST /api/v1/auth/agents/me/platforms`,

    `POST /api/v1/agents/register`, `PUT
    /api/v1/agents/{agent_id}/permissions/{platform_id}`

    and `POST /api/v1/agents/{agent_id}/rotate-secret` (and its `/emergency`

    variant) answer `410 Gone` with `code: agent_native_credentials_retired`.

    Register an agent identity (`POST /api/v1/agents/identities`), link an

    OAuth machine client to it and use `POST /oauth2/token`. Every

    `/api/v1/verify/*` path also answers `410 Gone` with the same code.
servers:
  - url: https://api.mudraid.ai
    description: Production
security: []
tags:
  - name: oauth
    description: |
      OAuth 2.0 endpoints. A machine client linked to an agent authenticates
      here with `private_key_jwt` (recommended) or, with an organization
      compatibility approval, `client_secret_basic`.
  - name: resource-server
    description: |
      Documents a protected API (or the FastAPI middleware) reads to verify
      access tokens and enforce route scopes.
  - name: enforcement
    description: |
      The live decision endpoint called by enforcement adapters (the V2
      middleware, the Node adapter/sidecar and the Kong plugin).
paths:
  /api/v1/adapter/enforcement/decide:
    post:
      tags:
        - enforcement
      summary: Decide one protected action
      description: |
        Called by an enforcement adapter — the V2 middleware's
        `HttpDecideClient`, the Node adapter or sidecar, or the Kong plugin —
        once per protected action, before the action is forwarded. The adapter
        authenticates with its own per-adapter bearer (issued at adapter
        registration). The caller's access token travels separately, inside
        the body as `presented_authorization`; identity, tenant and scopes are
        derived only from that verified token, never from other body fields.

        The request's surface claims (`surface.canonical_resource_uri`,
        platform, environment, bundle version and digest) are compared with
        what MudraID has registered for the authenticated adapter. Any
        authentication or binding failure answers one neutral `401`.

        The decision runs a fixed gate order: contract → identity/tenant →
        authority/delegation → containment → risk → entitlement → governance.
        It is **deny-closed**: an authoritative negative is `deny`; a missing,
        stale, conflicting or unavailable required fact, or a breached
        deadline, is `not_safely_decided`; only a decision where every gate
        passes on fresh facts is `allow`. An `allow` authorizes one forward
        (`directive: forward_once`); every other outcome carries
        `directive: block`. Adapters treat any non-`200` as "could not consult
        the authority" and deny.

        When the adapter sends an `execution` snapshot, the decision is bound
        to the digest of the exact request bytes it will forward. Decision
        responses can carry an RS256 `signature`; signing is a deployment
        setting and is off by default, so do not assume a response is signed.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DecideRequest'
      responses:
        '200':
          description: Decision produced (which may be `deny` or `not_safely_decided`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecideResponse'
        '401':
          description: |
            Adapter authentication or surface binding failed. One neutral body
            — `{"detail": {"error_code": "ADAPTER_AUTH_FAILED", ...}}` — for
            every cause.
        '402':
          description: Decision allowance exhausted (`DECISION_ALLOWANCE_REFUSED`). Deny.
        '409':
          description: >-
            Decision allowance operation conflict
            (`DECISION_ALLOWANCE_REFUSED`). Deny.
        '422':
          description: |
            Malformed envelope, or no supported request contract
            (`DECIDE_REQUEST_CONTRACT_UNSUPPORTED`, which lists the supported
            values).
        '503':
          description: |
            The decision could not be produced safely — signing is enabled and
            the response could not be signed (`DECISION_SIGNING_UNAVAILABLE`),
            or the decision allowance could not be checked. Deny.
      security:
        - adapterBearer: []
components:
  schemas:
    DecideRequest:
      type: object
      additionalProperties: false
      description: |
        Strict, bounded envelope; unknown top-level fields are refused with
        `422`. One of `schema_version` or `schema` must name a supported
        request contract.
      properties:
        schema_version:
          type: string
          enum:
            - mudraid.enforce.decide-request/1
            - '2.0'
        schema:
          type: string
          description: Older spelling of `schema_version`, still read.
        decision_id:
          type: string
          description: Caller-chosen request identity; one durable decision per id.
        requested_at:
          type: string
          format: date-time
        deadline_at:
          type: string
          format: date-time
          description: >-
            Clamped to a server ceiling; the server receive time is the decision
            clock.
        environment:
          type: string
        correlation_id:
          type: string
        presented_authorization:
          type: string
          maxLength: 8192
          description: |
            The protected request's `Authorization` header (`Bearer <access
            token>`). Without it the decision is `deny` (`credential_missing`).
        surface:
          type: object
          additionalProperties: false
          properties:
            platform_id:
              type: string
            environment:
              type: string
            canonical_resource_uri:
              type: string
              description: Must equal the resource registered for this adapter.
        request:
          type: object
          additionalProperties: false
          description: How the protected request arrived (evidence only).
          properties:
            transport:
              type: string
            http_method:
              type: string
            path:
              type: string
        execution:
          type: object
          additionalProperties: false
          required:
            - profile
            - body_sha256
            - content_type
          description: The exact bytes the adapter will forward if allowed.
          properties:
            profile:
              type: string
              enum:
                - mudraid.execution.request/1
            body_sha256:
              type: string
              pattern: ^[0-9a-f]{64}$
            body_base64:
              type: string
            content_type:
              type: string
        action:
          type: object
          description: |
            The canonical action resolved from the signed bundle:
            `action_id`, `action_version`, `mapping_id`, `mapping_version`,
            `required_scopes`. (`protected_action` is accepted as an alias.)
        protected_action:
          type: object
        adapter:
          type: object
          description: Adapter `type` and `version`.
        bundle:
          type: object
          description: Active bundle `version` and lowercase SHA-256 `payload_digest`.
        evaluation:
          type: object
          description: Evaluation `mode` (default `live_required`).
        evidence:
          type: object
          description: Evidence fields such as `idempotency_key`.
        trace:
          type: object
          description: Trace fields such as `correlation_id`.
        token_context:
          type: object
          description: Accepted for compatibility; its identity fields are ignored.
    DecideResponse:
      type: object
      required:
        - schema_version
        - decision_id
        - outcome
        - directive
        - reason
      properties:
        schema_version:
          type: string
          enum:
            - '2.0'
        decision_id:
          type: string
        outcome:
          type: string
          enum:
            - allow
            - deny
            - not_safely_decided
        directive:
          type: string
          enum:
            - forward_once
            - block
        decision:
          type: string
          enum:
            - allow
            - deny
          description: >-
            Compatibility projection of `outcome`; `not_safely_decided` projects
            to `deny`.
        reason:
          type: object
          properties:
            primary:
              type: string
              description: Stable machine reason code.
            contributing:
              type: array
              items:
                type: string
            category:
              type: string
              nullable: true
        evaluation:
          type: object
          description: Source, versions and per-fact freshness notes behind the decision.
        effective_authority:
          type: object
        evaluated_policies:
          type: array
          items:
            type: object
            properties:
              policy_set_id:
                type: string
                nullable: true
              policy_set_version:
                type: integer
                nullable: true
              policy_digest:
                type: string
                nullable: true
        decided_at:
          type: string
          format: date-time
        deadline_at:
          type: string
          format: date-time
        timings_ms:
          type: object
        telemetry:
          type: object
        correlation_id:
          type: string
          nullable: true
        action_executed:
          type: boolean
          enum:
            - false
          description: Always `false` — an allow authorizes; it does not execute anything.
        proof_context:
          type: object
          description: >-
            The authenticated adapter/action/bundle binding, when the action
            binding was verified.
        signature:
          type: object
          description: >-
            RS256 decision signature. Present only when decision signing is
            enabled.
  securitySchemes:
    adapterBearer:
      type: http
      scheme: bearer
      description: >-
        The per-adapter bearer issued when the enforcement adapter is
        registered.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.