---
title: "Process orchestrator request"
method: POST
path: "/api/v1/process"
tags: ["Processing"]
---

# Process orchestrator request

`POST /api/v1/process`

## Request

- Base URL: `https://orchestrator.getaxonflow.com`
- URL: `https://orchestrator.getaxonflow.com/api/v1/process`
- Auth: none declared
- TypeScript: <https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/docs/api/v1/process/post.ts> (a function with typed inputs; its response is checked against the schema)

Main processing endpoint. Handles:
1. Request plane: the anchored engine decides the request; a withheld request answers 403 (see `engine` and `verdict`)
2. LLM provider routing
3. Response plane: the anchored engine decides the LLM response (see `engine` and `verdict`)
4. Audit logging
5. Metrics collection

**Note**: This endpoint is typically called by the Agent, not directly by clients.
For MCP queries (`request_type: mcp-query`), routes to the Agent MCP handler.

## Request body

- OrchestratorRequest
  - `request_id` string: Unique request identifier
  - `query` string, required: Query to process
  - `request_type` string: Type of request
  - `skip_llm` boolean: Skip LLM calls (for testing)
  - `user` UserContext, required
    - `id` integer
    - `email` string
    - `role` string
    - `region` string: User's region, read by geo-based routing policies.
    - `permissions` string[]
    - `tenant_id` string
    - `org_id` string: Organisation for multi-tenant isolation, populated from the X-Org-ID header the agent stamps on the trusted hop.
  - `client` ClientContext, required
    - `id` string
    - `name` string
    - `org_id` string
    - `tenant_id` string
  - `context` object: Free-form request metadata. Routing controls: - `provider` (string): preferred provider - `strict_provider` (boolean, optional): when true, hard-pins `provider` and disables fallback for this request. Default is false unless server env `LLM_STRICT_PROVIDER_DEFAULT=true`.
  - `timestamp` string, date-time
  - `media` MediaContentRequest[]: Optional media content (images) for multimodal governance analysis
    - `source` 'base64' | 'url', required: How the media is provided
    - `base64_data` string: Base64-encoded image data (required when source=base64)
    - `url` string, uri: URL to image (required when source=url)
    - `mime_type` 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp', required: Media content type

## Response `200`

Request processed successfully

- OrchestratorResponse
  - `request_id` string
  - `success` boolean
  - `data` unknown
  - `error` string
  - `redacted` boolean
  - `redacted_fields` string[]
  - `policy_info` PolicyEvaluationResult: The policy verdict carried on every orchestrator response. Its property SET is held equal to the Go type's JSON members by `TestThePublishedSchemasMatchTheTypesThePlatformMarshals`, which compares the two by reflection, so a field added to one and not the other fails CI rather than reaching a spec-generated client (#3724). The properties are also written in the Go type's declaration order as a courtesy to a reader diffing the two; nothing enforces that, and order is not part of the contract.
    - `allowed` boolean
    - `applied_policies` string[]
    - `risk_score` number
    - `severity` 'critical' | 'high' | 'medium' | 'low': Highest severity among the matched policies.
    - `severity_policy_id` string: The policy that contributed `severity`.
    - `required_actions` string[]
    - `processing_time_ms` integer
    - `database_accessed` boolean
    - `evaluation_error` boolean: Distinguishes **could not govern** from **a policy said block**, and is the only signal that does. True when the engine could NOT complete evaluation because governance-segment resolution failed (a resolver or storage error -- never "the caller belongs to zero segments"). `allowed` is always false when this is set, because the engine fails CLOSED on that error, so a consumer reading only `allowed` still behaves safely; a consumer that audits or alerts MUST read this field to tell an availability failure apart from a genuine policy match. Before it existed the only signal was the magic string `applied_policies: ["segment_resolution_failed"]`.
    - `segments_resolved` boolean: True only when a resolved, non-empty governance-segment set was actually factored into this verdict. False covers every legitimate organisation-only case -- no identity supplied, no resolver wired (community, or no SCIM), or the caller belongs to zero segments -- as well as the `evaluation_error` case. None of those are failures: the flag exists so a reader of a policy-simulation preview does not mistake a legitimate org-only allow for a segment-aware one.
    - `applied_policies_detail` AppliedPolicyDetail[]: Structured mirror of `applied_policies` carrying each matched policy's risk level and allow_override metadata, without a second query. No session override is applied to a result in v11 (#4252).
      - `policy_id` string
      - `policy_name` string
      - `description` string
      - `action` string
      - `risk_level` 'low' | 'medium' | 'high' | 'critical'
      - `allow_override` boolean: False if and only if the policy forbids a session override.
      - `matched_rule` string
      - `segment_id` string: The governance segment this policy is scoped to, or absent when it is not segment-scoped. ATTRIBUTION AND AUDIT ONLY -- it is not an override-eligibility signal anywhere: a segment-scoped policy uses the same `allow_override` contract as a tenant policy.
    - `preferred_provider` string: LLM provider a matched routing policy prefers. When more than one applying route row names one, the LAST applying row in evaluation order wins it and the routing reason: rows are walked by priority, highest first, then newest first, so the winner is the lowest-priority applying row (#4249).
    - `allowed_providers` string[]: Strict provider allow-list for compliance routing. Failover stays within this list. It is the intersection of every applying route row's list, whatever their order; an empty intersection refuses the request (`no_compliant_provider`).
    - `routing_reason` string: Why routing was changed.
  - `provider_info` ProviderInfo
    - `provider` 'openai' | 'azure-openai' | 'anthropic' | 'bedrock' | 'ollama' | 'gemini' | 'mock'
    - `model` string
    - `response_time_ms` integer
    - `tokens_used` integer
    - `cost` number
  - `processing_time` string
  - `media_analysis` MediaAnalysisResponse
    - `results` MediaAnalysisItemResponse[]
      - `media_index` integer: Index of the media item in the request
      - `sha256_hash` string: SHA-256 hash of the image data
      - `has_faces` boolean: Whether faces were detected
      - `face_count` integer: Number of faces detected
      - `has_biometric_data` boolean: Whether biometric data was detected (GDPR Art. 9)
      - `nsfw_score` number, double: NSFW content score (0.0-1.0)
      - `violence_score` number, double: Violence content score (0.0-1.0)
      - `content_safe` boolean: Aggregated content safety flag
      - `document_type` string: Classified document type (e.g., id_card, passport, bank_statement)
      - `is_sensitive_document` boolean: Whether the document is classified as sensitive
      - `has_pii` boolean: Whether the platform's PII detectors found PII in text extracted from the image. A finding only when `scanned` contains `pii`; without it the text was not scanned and `false` says nothing.
      - `pii_types` string[]: The detectors that found PII, named by their policy id (e.g. `sys_pii_ssn`, `sys_pii_email`). Meaningful only when `scanned` contains `pii`.
      - `has_extracted_text` boolean: Whether text was extracted from the image via OCR (a finding only when `scanned` contains `text`)
      - `extracted_text_length` integer: Length of extracted text in characters (0 if none; meaningful only when `scanned` contains `text`)
      - `scanned` string[]: The analysis capabilities that RAN on this item, sorted. Each signal above is a finding only when its capability is listed: `content_safety` for `nsfw_score`, `violence_score` and `content_safe`; `faces` for `has_faces`, `face_count` and `has_biometric_data`; `document` for `document_type` and `is_sensitive_document`; `pii` for `has_pii` and `pii_types`; `text` for `has_extracted_text` and `extracted_text_length`. A signal whose capability is not listed is reported at a default that was not measured (`content_safe` as `true`, the others at zero); the policy decision reads it as unknown. Empty when no analyzer produced a result for the item.
      - `estimated_cost_usd` number, double: Estimated analysis cost for this media item
      - `warnings` string[]: Governance warnings for this media item
      - `structured_warnings` object[]: Structured governance warnings with codes
        - `code` string: Warning code identifier
        - `message` string: Human-readable warning message
    - `total_cost_usd` number, double: Total cost of media analysis across all items
    - `analysis_time_ms` integer: Total analysis time in milliseconds
  - `engine` 'anchored': The engine that decided: `anchored`, the ADR-065 decision plane (PRD v11 §1.1). On a request the request plane withholds (403, `error` "Request blocked by policy") it names that decision; otherwise it names the response plane's verdict on the LLM response, decided over the shared engine's detector facts. Omitted on an answer no anchored decision covers.
  - `subject_type` string: The type of principal the request or response was decided for. The orchestrator admits the client credential the agent's proxy authentication forwards, so it is `Client` on every edition. Omitted on an answer withheld before a subject was admitted, and wherever `engine` is.
  - `policy_bundle` string: The digest of the policy set that decided the request or the response. Omitted wherever `subject_type` is.
  - `verdict` 'allowed' | 'redacted' | 'blocked': The verdict `engine` names: `allowed` (released as the provider sent it), `redacted` (released masked, as a `field_redact` obligation required; `data` carries the masked content) or `blocked` (withheld: `success` is false and `error` says so; a request the request plane withholds is always `blocked`). A request or response that could not be decided is withheld, never released. Omitted wherever `engine` is.

## Other responses

- `400`: Invalid request
- `403`: Blocked by policy. When media was submitted the refusal carries `media_analysis` (per item `has_pii`, `pii_types`, `scanned`, never the extracted text), so a caller can tell a refusal on a finding (for example `has_pii: true` beside `scanned` containing `pii`) from one on a signal nothing measured (its capability absent from `scanned`, `unknown_constraint` in `policy_info.required_actions`). Before #4300 a refusal carried no `media_analysis`.
- `500`: Internal server error

---

[API](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api.md) - [All operations](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/llms.txt) - [OpenAPI document](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/revisions/f408ef3ec9dd?raw)
