Skip to main content

Policy Configuration

Workflow policies govern what happens when an external orchestrator (LangGraph, Temporal, Airflow, or any custom system) reaches a step gate. Instead of writing governance logic inside your orchestrator code, you define declarative policies in AxonFlow. In v11 every step gate is decided by the policy decision engine that decides every other enforcement plane, and the gate returns one of three decisions: allow the step, block it, or require human approval before proceeding.

One engine for workflows and requests​

The step gate is the wcp enforcement plane, and from v11.1.0 it is the only thing that plane decides: the Orchestrator's request routes (POST /api/v1/process and POST /api/v1/plan/execute), which decided under wcp before, have their own plane, orchestrator_request. It is decided by the same engine, reading the same policies, as a model request through Gateway Mode or Proxy Mode: the platform's shipped policies and your organization's typed policy document. What differs is the context the engine evaluates: a model request carries prompt and response text, while a step gate carries the structured step context described below.

The rules you add for workflows are policies on your organization's typed policy document, written through /api/v1/typed-policies or the portal's Policy Authoring page; see Typed Policy Authoring. The v10.x tenant-policy route this page used to document, POST /api/v1/policies, answers 409 LEGACY_POLICY_WRITE_FROZEN in v11 and names the typed route.

The step gate context​

When a step gate request arrives, the WCP policy adapter builds the context the engine evaluates from the step. Fields fall into a few dotted-path namespaces.

Which of these fields a policy on your organization's document can read is declared by the deployment's vocabulary, which GET /api/v1/typed-policies/edition reports; a path the vocabulary does not declare is refused when you validate the document.

Core Step Fields (under context.)​

FieldTypeDescription
context.step_typestringThe type of step: llm_call, tool_call, connector_call, or human_task
context.step_namestringHuman-readable name of the step (e.g., "generate_code", "deploy_to_staging")
context.modelstringLLM model identifier (e.g., "gpt-4", "claude-opus-4-1-20250805", "amazon.titan-text-express-v1")
context.providerstringLLM provider (e.g., "openai", "anthropic", "bedrock")
context.step_indexintThe sequential position of this step in the workflow

Workflow Context Fields (under context.)​

FieldTypeDescription
context.workflow_idstringThe unique workflow identifier
context.workflow_namestringHuman-readable workflow name
context.sourcestringOrchestrator source: langgraph, langchain, crewai, or external

Step Input Fields (under step_input.)​

Any key in the step_input object is available under the direct step_input. prefix - no context. wrapping. For example, a step gate request with "step_input": {"environment": "production", "region": "us-east-1"} carries step_input.environment and step_input.region.

Tool Context Fields (Per-Tool Governance)​

When per-tool governance is used, these fields are added from the tool_context object.

FieldTypeDescription
context.tool_namestringName of the specific tool being invoked (e.g., "web_search", "sql_query")
context.tool_typestringTool category: "function", "mcp", or "api"
tool_input.*variesIndividual keys from the tool's input arguments, prefixed directly with tool_input. (limited to 50 keys, no context. wrapping)

Retry & Idempotency Fields​

These step.* fields are populated from the step's retry_context before each decision:

FieldTypeDescription
step.gate_countintNumber of /gate calls for this (workflow_id, step_id), including the current one
step.completion_countintNumber of successful /complete calls (0 or 1 in normal flows)
step.prior_completion_statusenum"none", "completed", or "gated_not_completed"
step.prior_output_availablebooltrue iff a prior /complete landed
step.last_decisionenum"allow", "block", or "require_approval" from the prior gate call
step.first_attempt_age_secondsintSeconds since the first gate call on this step
step.idempotency_keystringThe caller-supplied key; empty string when none was supplied

A retry-aware decision is only re-made on a subsequent /gate call when the caller passes retry_policy: "reevaluate". Default-idempotent retries return the cached decision without consulting the engine. See Retry Semantics & Idempotency for the full wire shape and SDK Integration for the cache semantics.

Decisions​

The step gate returns one of three decisions.

allow lets the step proceed, with any safeguards the matching policies attach.

block prevents the step from executing. The gate response names the blocking policy and its reason, which your orchestrator should use to abort the workflow or skip the step. A shipped policy whose action is block, or a ceiling on your organization's document, blocks. This is the right decision for hard prohibitions like "never use GPT-4 in production" or "block destructive SQL operations."

require_approval pauses the workflow and creates a human-in-the-loop (HITL) approval request. The gate response includes an approval_id (always populated) and an approval_url (populated only when the orchestrator is configured with a PORTAL_BASE_URL env var). The workflow cannot proceed until a human approves or rejects the step. On rejection, the workflow is automatically aborted. The approval REQUEST is created only on Professional and above. On Community and Evaluation the step still pauses, but no reviewer entry is created and the gate response reports approval_enqueue: "tier_disabled" with no approval_id.

Writing a workflow rule​

A workflow rule is a policy on your organization's typed policy document, whose condition reads the step context. The table shows how the v10.x rules would translate. It is illustrative: a policy can read a step field only if the deployment's vocabulary declares it (see The step gate context).

v10.x rulev11 policy
Block GPT-4 in production workflow LLM callsA ceiling that denies when context.step_type is llm_call and context.model is gpt-4
Deployment connector calls require human approvalA requirement that attaches an approval when context.step_type is connector_call and context.step_name contains deploy
Block steps flagged as containing PIIA ceiling that denies when step_input.contains_pii is true
Block OpenAI on compliance-sensitive workflowsA ceiling that denies when context.step_type is llm_call and context.provider is openai
Block the code_executor toolA ceiling that denies when context.tool_name is code_executor
Route MCP tool calls through the HITL queueA requirement that attaches an approval when context.tool_type is mcp and context.step_type is tool_call

Attaching an approval and scoping a policy to a group are Enterprise constructs.

The shipped DROP TABLE and TRUNCATE guards are organization-editable policies that block on every plane that runs them; whether the step gate runs them follows the per-plane restriction Shipped Policy Posture describes.

The v10.x tenant-policy body these rules were written as

In v10.x each rule above was a POST /api/v1/policies body, for example:

{
"name": "block-gpt4-in-workflows",
"description": "Block GPT-4 in production workflow LLM calls",
"type": "context_aware",
"category": "dynamic-security",
"priority": 900,
"enabled": true,
"conditions": [
{"field": "context.step_type", "operator": "equals", "value": "llm_call"},
{"field": "context.model", "operator": "equals", "value": "gpt-4"}
],
"actions": [
{"type": "block", "config": {"reason": "GPT-4 not allowed in production workflows"}}
]
}

That route refuses the body in v11. A rule you exported in this shape is what to re-author as a typed policy.

How a Step Is Decided​

On every step gate the engine reads all the policies that apply to the step and combines them by kind:

  • A matching ceiling blocks the step, even if another policy asks for an approval: an explicit constraint wins over every other outcome.
  • Otherwise, a matching approval requirement holds the step for a person.
  • Otherwise the step is allowed, with every safeguard the matching requirements attach.

This differs from v10.x, where a matching require_approval took precedence over a matching block, so that a human could override it. In v11 a block is final; if a human should be able to decide, write the rule as an approval requirement, not a ceiling.

The step gate response always includes two lists: policies_evaluated (the policies that were checked) and policies_matched (the policies whose conditions matched and contributed to the decision), so you can see why a step was allowed or blocked.