AxonFlow Gateway Mode API

Pre-check and audit for SDK-managed LLM calls

Operations 2

POST /api/policy/pre-check Pre-check request before LLM call #
POST /api/audit/llm-call Audit LLM call after completion #

Documentation

Specifications

Schemas & Data

📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-decide-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-mcpcheck-input-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-pre-check-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-client-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-decide-request-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-mcpcheck-output-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-audit-log-entry-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-step-gate-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-ojkaudit-export-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-approval-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-audit-action-report-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-policy-evaluation-result-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-simulate-policies-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-create-policy-request-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-update-policy-request-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-impact-report-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-apply-template-request-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-test-policy-response-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-featassessment-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-aisystem-registry-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-create-registry-request-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-update-assessment-request-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-kill-switch-schema.json
📊
JSONSchema
https://raw.githubusercontent.com/api-evangelist/axonflow/refs/heads/main/json-schema/axonflow-update-registry-request-schema.json

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/axonflow:axonflow-gateway-mode-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

axonflow-gateway-mode-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Axonflow Gateway Mode API
  version: 11.1.0
  contact:
    name: AxonFlow Support
    url: https://getaxonflow.com/support
  license:
    name: Business Source License 1.1
    url: https://github.com/getaxonflow/axonflow/blob/main/LICENSE
  description: 'Operations tagged Gateway Mode across 2 of this provider''s published API definitions: axonflow-agent-api.yaml, axonflow-agent-openapi.yml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://agent.getaxonflow.com
  description: Production (SaaS)
- url: https://axonflow.example.com
  description: Self-hosted deployment (agent single entry point, ADR-024)
- url: http://localhost:8080
  description: Local Development
tags:
- name: Gateway Mode
  description: Pre-check and audit for SDK-managed LLM calls
paths:
  /api/policy/pre-check:
    post:
      tags:
      - Gateway Mode
      summary: Pre-check request before LLM call
      description: 'Gateway Mode Step 1: Call this endpoint before making your own LLM API call.


        The Agent validates the request against policies and returns:

        - `verdict` - the canonical `allow` | `deny`, the same vocabulary

        `POST /api/v1/decide` uses. Read this one. Since v11 the pre-check

        never holds a request: a `require_approval` policy is a deny.

        - `approved: true` if the request is allowed (retained)

        - `decision_id` — the decision identifier. Use it for the subsequent

        audit call, AND for `GET /api/v1/decisions/{decision_id}/explain`,

        which is keyed on it.

        - `context_id` — a deprecated alias of `decision_id`, same value

        - Optional `approved_data` from MCP connectors

        - Rate limit information


        **`approved_data` is only prefetched for clean approvals** (#2868):

        when the request is blocked, requires HITL approval, or requires

        redaction, connector prefetch is skipped and `approved_data` is

        never populated — governed data is not fetched for a request that

        may not proceed.


        **Context expires after 5 minutes.**


        ## Example Flow

        ```

        1. SDK calls pre-check → gets context_id, approved=true

        2. SDK makes direct LLM call (OpenAI, Anthropic, etc.)

        3. SDK calls audit with context_id and response metadata

        ```'
      operationId: gatewayPreCheck
      parameters:
      - $ref: '#/components/parameters/LicenseKey'
      - $ref: '#/components/parameters/AxonflowPEPHandshake'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PreCheckRequest'
            examples:
              basic:
                summary: Basic pre-check
                value:
                  query: What is the customer's order status?
                  user_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
                  client_id: customer-portal
              withDataSources:
                summary: Pre-check with data sources
                value:
                  query: Find flights from NYC to LAX next week
                  user_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
                  client_id: travel-app
                  data_sources:
                  - amadeus
                  context:
                    departure_date: '2025-01-20'
                    return_date: '2025-01-25'
      responses:
        '200':
          description: Pre-check result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreCheckResponse'
              examples:
                approved:
                  summary: Request approved
                  value:
                    context_id: ctx_abc123def456
                    approved: true
                    policies:
                    - pii-detection
                    - rate-limit
                    rate_limit:
                      limit: 1000
                      remaining: 995
                      reset_at: '2025-01-15T11:00:00Z'
                    expires_at: '2025-01-15T10:35:00Z'
                piiRedaction:
                  summary: PII detected - flagged for redaction
                  description: Returned when the matched PII policy's resolved request-phase action is redact - its stored action, or an organization's pii=redact detection-posture override (since v11 no environment variable sets it). Request approved but PII will be redacted in response.
                  value:
                    context_id: ctx_abc123def456
                    approved: true
                    requires_redaction: true
                    policies:
                    - pii-ssn
                    expires_at: '2025-01-15T10:35:00Z'
                blocked:
                  summary: Request blocked (resolved action block - a stored block action or an organization's pii=block override)
                  value:
                    context_id: ctx_abc123def456
                    approved: false
                    policies:
                    - pii-credit-card
                    block_reason: Query contains credit card number
                    expires_at: '2025-01-15T10:35:00Z'
                withData:
                  summary: Approved with data
                  value:
                    context_id: ctx_abc123def456
                    approved: true
                    approved_data:
                      amadeus:
                        rows:
                        - flight_number: UA123
                          departure: '2025-01-20T08:00:00Z'
                          price: 299.99
                        row_count: 5
                        duration_ms: 450
                    policies:
                    - pii-detection
                    expires_at: '2025-01-15T10:35:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: 'Budget exceeded — a configured cost budget blocks this request

            (Enterprise cost controls).

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          description: 'Community SaaS tenants past the daily request cap (written by

            the auth middleware; shared rate-limit envelope).

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitEnvelope'
        '503':
          description: 'Circuit breaker is open — an emergency stop matching this

            request''s scope is active.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    servers:
    - url: https://agent.getaxonflow.com
      description: Production (SaaS)
    - url: https://axonflow.example.com
      description: Self-hosted deployment (agent single entry point, ADR-024)
    - url: http://localhost:8080
      description: Local Development
  /api/audit/llm-call:
    post:
      tags:
      - Gateway Mode
      summary: Audit LLM call after completion
      description: 'Gateway Mode Step 2: Call this endpoint after your LLM API call completes.


        Records:

        - Token usage for billing and quotas

        - Latency metrics

        - Provider and model information

        - Estimated cost


        **Requires a valid context_id from pre-check (not expired).**'
      operationId: auditLLMCall
      parameters:
      - $ref: '#/components/parameters/LicenseKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuditLLMCallRequest'
            example:
              context_id: ctx_abc123def456
              client_id: travel-app
              response_summary: Found 5 flights matching criteria
              provider: openai
              model: gpt-4
              token_usage:
                prompt_tokens: 150
                completion_tokens: 200
                total_tokens: 350
              latency_ms: 1250
              metadata:
                request_type: travel_search
                cache_hit: false
      responses:
        '200':
          description: Audit recorded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditLLMCallResponse'
              example:
                success: true
                audit_id: aud_xyz789
        '400':
          description: Invalid or expired context
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: Invalid or expired context
        '401':
          $ref: '#/components/responses/Unauthorized'
    servers:
    - url: https://agent.getaxonflow.com
      description: Production (SaaS)
    - url: https://axonflow.example.com
      description: Self-hosted deployment (agent single entry point, ADR-024)
    - url: http://localhost:8080
      description: Local Development
components:
  schemas:
    AuditLLMCallRequest:
      type: object
      required:
      - context_id
      - client_id
      - provider
      - model
      - token_usage
      properties:
        context_id:
          type: string
          description: Context ID from pre-check
        client_id:
          type: string
          description: Client application ID
        response_summary:
          type: string
          description: Brief summary of LLM response (for audit)
          maxLength: 500
        provider:
          type: string
          description: LLM provider name
          enum:
          - openai
          - azure-openai
          - anthropic
          - bedrock
          - ollama
          - gemini
        model:
          type: string
          description: Model identifier
          example: gpt-4
        token_usage:
          $ref: '#/components/schemas/TokenUsage'
        latency_ms:
          type: integer
          description: LLM call latency in milliseconds
        metadata:
          type: object
          additionalProperties: true
          description: Additional metadata for audit
    RateLimitEnvelope:
      type: object
      description: 'Shared tier rate-limit envelope written by the Community SaaS

        limiter for daily-quota 429s (the same shape is used with 403 for

        Pro-only feature limits). On the REST routes, per-minute 429s use

        a plain `{"error": "..."}` body with only a Retry-After header —

        not this envelope. On `/api/v1/mcp-server` both limits use it

        (`per_minute` and `daily_quota`, 429, #4261), and so do the

        `tools/call` tier gates (403, #4274) and the tier admission

        refusals on every method (403, or 429 while the admission ledger

        cannot be reached; #4249 row 5682255301), wrapped in a JSON-RPC

        result.

        Accompanied by the `X-Axonflow-Tier-Limit` and

        `X-Axonflow-Upgrade-URL` headers, and by `Retry-After` when the

        limit has a reset time (not for `feature_pro_only`). Source of truth:

        `platform/agent/community_saas_ratelimit_response.go`

        (rateLimitEnvelope).

        '
      properties:
        error:
          type: string
        limit_type:
          type: string
          description: 'Which limiter fired: `daily_quota`, `per_minute` (MCP server

            only), `hitl_approvals_window`,

            `feature_pro_only`, or the refused admission dimension

            (`service_principal` / `human_principal`, MCP server only).

            '
        tier:
          type: string
        limit:
          type: integer
        remaining:
          type: integer
        window:
          type: string
        resets_at:
          type: string
          format: date-time
        upgrade:
          type: object
          properties:
            tier:
              type: string
            wording:
              type: string
            compare_url:
              type: string
            buy_url:
              type: string
              description: 'Empty on a tier admission refusal: the V1 buy link is

                Plugin Pro''s, which lifts no edition ceiling.

                '
        code:
          type: string
          description: 'The refusal''s machine-readable code where it has one: a tier

            admission refusal''s `ERR_TIER_LIMIT_<DIMENSION>` (MCP server).

            Omitted on every other limit.

            '
    AuditLLMCallResponse:
      type: object
      properties:
        success:
          type: boolean
        audit_id:
          type: string
          description: Unique audit record ID
    RateLimitInfo:
      type: object
      properties:
        limit:
          type: integer
          description: Rate limit per window
        remaining:
          type: integer
          description: Remaining requests in current window
        reset_at:
          type: string
          format: date-time
          description: When the rate limit resets
    TokenUsage:
      type: object
      properties:
        prompt_tokens:
          type: integer
          description: Tokens in the prompt
        completion_tokens:
          type: integer
          description: Tokens in the completion
        total_tokens:
          type: integer
          description: Total tokens used
    PreCheckRequest:
      type: object
      required:
      - query
      - client_id
      properties:
        query:
          type: string
          description: Query to validate
          minLength: 1
        user_token:
          type: string
          description: JWT token for user authentication
        client_id:
          type: string
          description: Client application ID
        data_sources:
          type: array
          items:
            type: string
          description: MCP connectors to fetch data from
        context:
          type: object
          additionalProperties: true
          description: Additional context
    PreCheckResponse:
      type: object
      properties:
        decision_id:
          type: string
          description: "The decision identifier, and the CANONICAL name for it. Every other\nplane that mints a decision calls it `decision_id`:\n`POST /api/v1/decide`, `POST /api/v1/mcp/check-input`,\n`POST /api/v1/mcp/check-output`, and the AuthZEN adapter's\n`context.decision_id`.\n\nIT IS THE KEY TO ANOTHER ENDPOINT, and that linkage was\npreviously documented nowhere:\n\n  * `GET /api/v1/decisions/{decision_id}/explain` returns the full\n    policy explanation for this decision.\n\nA caller that reads only `context_id` below still has the value, but\nnothing told it that the value works on that endpoint, so\nintegrations built against this plane silently lost a capability\nthat integrations built against `/api/v1/decide` got for free.\n\n`POST /api/v1/overrides` was the second such endpoint until\nv11.0.0. It is keyed on a policy, never on `decision_id`, and from\nv11.0.0 it writes nothing and answers\n`409 LEGACY_POLICY_WRITE_FROZEN` (#4252).\n\nAlso valid for the subsequent `POST /api/audit/llm-call` for five\nminutes, which is what `context_id` was originally named for.\n"
        verdict:
          type: string
          enum:
          - allow
          - deny
          description: 'The CANONICAL answer to "may I do this?", in the same vocabulary and

            with the same values `POST /api/v1/decide` returns.


            Read this rather than `approved` in new integrations. Across the

            governed surface the same question was answered by five different

            keys in two different types - `verdict` (string) on

            `/api/v1/decide`, `approved` (bool) here, `allowed` (bool) on both

            MCP check endpoints, `decision` (bool) on the AuthZEN adapter and

            `decision` (string) on the decisions feed - so a client typed from

            one plane could not deserialise another.


            Since v11 the pre-check never holds a request: a `require_approval`

            policy is a deny, because an anchored CHALLENGE is a refusal. So

            `verdict` and `approved` never disagree.


            The AuthZEN adapter (`POST /api/v1/access/evaluation`) keeps its

            boolean `decision` and is not a divergence to be fixed: AuthZEN 1.0

            mandates a boolean, and the four-valued state rides in that

            endpoint''s response context behind profile negotiation.

            '
        approved:
          type: boolean
          description: 'Whether the request is allowed. RETAINED and not deprecated - every

            shipped SDK reads it. Prefer `verdict`.

            '
        context_id:
          type: string
          deprecated: true
          description: 'DEPRECATED ALIAS of `decision_id`, carrying the identical value.


            Retained because every shipped SDK reads it and removing it would

            break them all. New integrations should read `decision_id`; this

            member will be removed no earlier than the release after the one

            that introduced `decision_id`.

            '
        approved_data:
          type: object
          additionalProperties: true
          description: Data fetched from MCP connectors
        policies:
          type: array
          items:
            type: string
          description: 'Policies that were evaluated.


            `segment_resolution_failed` no longer appears here (it did from

            #3312). No segment gate stands on the pre-check any more: it

            resolved the caller''s governance segments and refused the request

            when that failed, on behalf of an organization''s segment-scoped

            static rows. Those rows no longer decide: the anchored engine

            authors this verdict and reads no segments (PRD v11 §1.1, §1.2).

            '
        rate_limit:
          $ref: '#/components/schemas/RateLimitInfo'
        expires_at:
          type: string
          format: date-time
          description: When the context expires
        block_reason:
          type: string
          description: Reason if request was blocked
        trace_id:
          type: string
          description: 'W3C OpenTelemetry trace_id (32-char lowercase hex) emitted

            by the decision tracer. Optional: present when the tracer

            is enabled via AXONFLOW_OTEL_ENDPOINT, omitted otherwise.

            Policy Enforcement Points propagate this id downstream so

            multi-gateway decisions stitch into one end-to-end trace.

            '
          example: b3a1f1f3a8c6e0d791bc3e7a8c2d5f4a
        engine:
          type: string
          enum:
          - anchored
          description: 'Which policy engine authored this verdict: the ADR-065 decision

            plane, the only author on this route (PRD v11 §1.1). Omitted

            on a refusal no engine decided - an authentication failure, or a

            request refused before the policy pass ran.

            '
        subject_type:
          type: string
          description: 'The type of principal the verdict was decided for (PRD v11 §1.6):

            `User` for a verified user token, `Client` when the request

            presented no user identity and its client credential is the

            principal. Omitted wherever `engine` is.

            '
        policy_bundle:
          type: string
          description: 'The digest of the policy set that decided: the system corpus''s

            restriction for this route and the organization root - the

            organization''s active typed document composed with the

            deployment''s baseline permission pack, or, while it has published

            nothing, the implicit bundle of that pack and the organization

            template. A rollback reinstates an earlier digest. Omitted wherever

            `engine` is.

            '
        legacy_validators:
          type: array
          description: 'A checksum validator that acted BEFORE the anchored engine decided

            (#4122): under an organization''s recorded `pii=block` or

            `pii=redact` detection override, the Indonesia or India validator

            blocked the request or masked the response ahead of the decision

            plane. Omitted when none did, which is every request without

            such an override.

            '
          items:
            type: object
            required:
            - validator
            - action
            properties:
              validator:
                type: string
                enum:
                - indonesia_pii
                - india_pii
              action:
                type: string
                enum:
                - blocked
                - masked
    ErrorResponse:
      type: object
      description: 'Handler-written error envelope. Note the agent has a second error

        envelope for middleware-written errors (see JSONError) — clients

        should tolerate both shapes on 4xx/5xx.

        '
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          description: Error message
  responses:
    Unauthorized:
      description: 'Missing or invalid authentication. Handler-written 401s use the

        `{success, error}` envelope; 401s written by the auth middleware

        use the `{"error": {"code", "message"}}` envelope (JSONError).

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: 'Authentication required: provide Authorization header with Basic auth (clientId:clientSecret)'
    Forbidden:
      description: Access denied by policy or tenant mismatch
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: Tenant mismatch
    BadRequest:
      description: Invalid request body or parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: Invalid request body
  parameters:
    AxonflowPEPHandshake:
      name: X-Axonflow-PEP-Handshake
      in: header
      required: false
      description: 'The ADR-065 **PEP capability handshake**: base64url of a compact JSON

        document in which an external enforcement point declares what it is and

        which obligations it can discharge. See `PEPHandshake` for the document.


        **Absent is the default and changes nothing.** A caller that omits the

        header takes byte-for-byte the path it took before this header existed.


        **What an absent header means for a redaction depends on the plane, by

        design (PRD v11 section 1 item 16, #4257).** The MCP passes discharge a

        redaction of the content they hand back: the request pass

        (`check-input`, `check_policy`) masks the statement, and a redaction

        that masks nothing in the statement is refused `unsupported_obligation`

        to every caller, and one that masks a request parameter to every caller

        that has not declared `field_redact` at version 2 (on Community, to

        every caller) (#4264);

        the response passes (`check-output`, the MCP server''s `check_output`)

        mask the rows or the message. `/api/v1/decide` and the gateway

        pre-check return a decision rather than content, so a required

        redaction is a `field_redact` obligation for the enforcement point, and

        a caller that has not declared `field_redact` is refused

        `unsupported_obligation`. A caller that declares NO redaction

        (`capabilities: []`) is refused on each of these planes; on

        Community the MCP passes still return a checksum validator''s masked

        content to it (reachable only through a directly inserted

        `detection_action_overrides` row) until #4122.


        A header that is PRESENT and cannot be read is **refused**, never

        treated as absent: degrading a malformed declaration to "legacy caller"

        would go on handing an enforcement point obligations it had just said it

        cannot discharge. The refusal is `400` and its message names this header,

        which matters on `/api/v1/access/evaluation` where the refusal is

        rendered through that surface''s existing `incomplete_evaluation` code and

        the message is the only thing distinguishing a malformed HEADER from a

        malformed body ENVELOPE.


        Present more than once is refused: RFC 7230 permits an intermediary to

        join repeated field lines with a comma, and a comma is outside the

        base64 alphabet, so a joined pair can only decode to malformed. That is

        why the document is base64 rather than raw JSON, which would join into

        something a lenient parser might accept.


        When a decision carries a **mandatory** obligation the declared set does

        not cover, the request is answered `200` with `verdict: deny` and the

        reason `unsupported_obligation` (ADR-065 invariant 8) - a decision about

        the request, not a transport error. That holds on every edition. An

        Enterprise deployment adds a second reason beginning

        `pep_capability_unsupported` naming the gap, and refuses a checksum

        validator''s mask on the MCP passes; a Community deployment hands that

        masked content over (#4257 split 2, #4122).

        '
      schema:
        type: string
        maxLength: 4096
        description: base64url (padding optional) of the PEPHandshake document.
    LicenseKey:
      name: Authorization
      in: header
      required: true
      description: 'OAuth2-style Basic authentication header.

        Format: `Basic base64(clientId:clientSecret)`


        - `clientId`: Your organization identifier (required)

        - `clientSecret`: Authentication credential (optional for community mode)


        Not required when `DEPLOYMENT_MODE=community`.

        '
      schema:
        type: string
        example: Basic bXktb3JnOkFYT04tVjIteHh4
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
      description: "OAuth2-style Basic authentication using `clientId:clientSecret` credentials.\n\n**Header format:** `Authorization: Basic base64(clientId:clientSecret)`\n\n- `clientId` (required): Your organization/client identifier\n- `clientSecret` (optional): Authentication credential. Optional for community/self-hosted mode.\n\n**Example:**\n```bash\n# With clientSecret (enterprise)\ncurl -H \"Authorization: Basic $(echo -n 'my-org:AXON-V2-xxx' | base64)\" ...\n\n# Without clientSecret (community mode)\ncurl -H \"Authorization: Basic $(echo -n 'my-org:' | base64)\" ...\n```\n\n## Per-user identity behind a shared credential\n\nThis credential authenticates an ORGANIZATION or client, not a person.\nBehind one such credential can sit many human principals, each\noptionally forwarding a **per-user token** that proves who they are.\nWhere that token is read depends on the envelope: the `user_token`\nfield of the request body on `POST /api/v1/decide` and the four MCP\nREST routes, and the `X-User-Token` header on the MCP-server JSON-RPC\nplane. The two spellings are deliberately not interchangeable.\n\n**A presented per-user token that fails to validate is a refused\naccess attempt, not a legacy caller** (`401`, audited\n`user_token_rejected`). It is never downgraded to a shared service\nidentity, so revocation, expiry, algorithm pinning and signature\nchecks take effect on every plane that reads one.\n\n**Whether presenting a token is REQUIRED is a per-organization\nposture, `require_user_token`, and it is off by default (#3476).**\nWith it off, an enterprise caller that presents no token at all is\nserved under a synthetic org-scoped service identity\n(`<client-id>@axonflow.local`, role `service`), which is the correct\nanswer for an infrastructure gateway acting as a Policy Enforcement\nPoint with no end-user token to forward. With it on, that caller is\nrefused at AUTHENTICATION, before any policy is evaluated (`401`,\naudited `user_token_required`).\n\nThe posture exists because a policy that names a PERSON - a\nprincipal-scoped constraint or permission in the organization's typed\ndocument (PRD v11 §1.6) - is only meaningful if a caller cannot CHOOSE\nto arrive without an identity: with the posture off such a policy\nstill applies to everyone who presents a token, but a caller can\ndecline to present one and be decided as the credential\n(`subject_type=Client`). Governance segments (ADR-060) decide on no\nagent route since v11.0.0 (#4253). Two levers set it, and an explicit\nper-organization row wins over the deployment-wide default in EITHER\ndirection:\n\n- `organizations.require_user_token`, per organization, default\n  `false`.\n- `AXONFLOW_REQUIRE_USER_TOKEN`, deployment-wide, default `false`.\n\nA posture change takes up to one cache window to become live\n(`AXONFLOW_REQUIRE_USER_TOKEN_TTL_SECONDS`, default 60 seconds,\nclamped to `[5, 600]`). A posture that cannot be READ resolves to\nREQUIRED rather than not-required, so a database outage cannot\nquietly switch the control off; a genuinely absent organization row\nis not a read failure and falls through to the deployment default.\n\n`POST /v1/chat/completions` is outside this guarantee: it mirrors\nOpenAI's wire shape and carries no per-user token field at all, so it\nkeeps the synthetic-identity fallback regardless of the posture.\nCommunity and community-SaaS deployments never reach any of the above.\n"
    InternalServiceID:
      type: apiKey
      in: header
      name: X-Internal-Service-ID
      description: 'Internal-service (operator lane) credential — **part one of two**.

        Must be sent together with `X-Internal-Service-Token`; either header

        alone is not a credential.


        This is the HMAC identity the Orchestrator and the Enterprise

        customer-portal use to call agent endpoints without holding a

        customer license. `apiAuthMiddleware` lifts both headers (plus an

        optional `X-Tenant-ID` scope) into `AuthHints`

        (`internalServiceHints` in `platform/agent/auth.go`) and

        `Authenticate()` validates them before any mode-specific auth

        (`platform/agent/authenticator.go:120-155`).


        Value: the service id, `orchestrator-internal`.


        ⚠️ An invalid or expired token is **not** an error by itself — it

        falls through to the deployment''s normal auth

        (`platform/agent/authenticator.go:153-154`). Send the internal-service

        headers on their own: paired with an `Authorization: Basic` header, a

        stale token silently yields a *tenant*-scoped answer that looks like a

        successful operator call.

        '
    InternalServiceToken:
      type: apiKey
      in: header
      name: X-Internal-Service-Token
      description: 'Internal-service (operator lane) credential — **part two of two**.

        Must be sent together with `X-Internal-Service-ID`.


        Format: `AXON-INTERNAL-{unix_ts}-{sig}`, where `sig` is the first 16

        hex characters of HMAC-SHA256 over `orchestrator-internal:{unix_ts}`

        keyed with `AXONFLOW_INTERNAL_SERVICE_SECRET`. Validated by

        `platform/shared/serviceauth` within a 5-minute clock-skew window, so

        it must be re-minted per session. See

        `technical-docs/runbooks/RUNBOOK_CONNECTOR_CONFIGURATION.md` for the

        exact minting snippet.

        '
x-refined-from:
- axonflow-agent-api.yaml
- axonflow-agent-openapi.yml