n8n + AxonFlow Integration
AxonFlow gives n8n workflows a way to call the AxonFlow API for policy checks, decision recording, and human-in-the-loop approvals. There are two ways to wire it up.
- Interception point: an explicit AxonFlow node (the community node) or stock HTTP nodes that you place in the workflow — a WCP-style step gate, not an automatic interceptor.
- Governs: the specific step(s) where you wire it in — the default
checkPolicyoperation callsPOST /api/v1/mcp/check-inputbefore the guarded action,recordDecision/auditLogwrite the audit (POST /api/v1/audit/tool-call), andwaitForApprovalpauses the workflow for a HITL decision (POST /api/v1/hitl/queue). - Does not govern: steps where the node is not placed. Coverage is opt-in per workflow — an ungated node runs without any policy check.
See Governance Architecture & Coverage for how this fits the five runtime modes.
| Path | When to use |
|---|---|
| Tier 1 — Stock HTTP-node recipe | Works on any n8n install, including n8n Cloud. Four HTTP nodes per governed action. Useful for prototypes or as a fallback when community nodes are not available. |
Tier 2 — Community node (@axonflow/n8n-nodes-axonflow) | Self-hosted n8n. One node, four operations, idempotency by default, paired with the built-in Wait node for HITL resume. |
Tier 3 — verified node on n8n Cloud. Submission to n8n's verified registry is tracked separately and is not gated by this page.
Tier 1 — Stock HTTP-node recipe (any n8n install)
This recipe uses only built-in n8n nodes. It is the fastest path to "n8n is calling AxonFlow" — useful for prototyping or n8n Cloud where community nodes are not currently allowed.
Setup once
In Credentials → New → Header Auth create an AxonFlow Basic credential:
| Field | Value |
|---|---|
| Name | Authorization |
| Value | Basic <base64(clientId:userToken)> |
Why Header Auth and not Bearer Auth? n8n's built-in Bearer Auth class silently drops the header on some versions (n8n#15261). Header Auth attaches the
Authorizationline explicitly and is unaffected.
Wire four HTTP nodes per governed action
[HTTP] Trigger → [HTTP] AxonFlow Check Policy → [IF] allowed?
├── true → [HTTP] downstream action → [HTTP] AxonFlow Record Decision
└── false → [Wait] for webhook → [HTTP] downstream action → [HTTP] AxonFlow Record Decision
(error branch) → [HTTP] AxonFlow Audit Log
| Node | Method + URL | Body (JSON) |
|---|---|---|
| Check Policy | POST {endpoint}/api/v1/mcp/check-input | { "client_id": "{{ $env.AXONFLOW_CLIENT_ID }}", "user_token": "{{ $env.AXONFLOW_USER_TOKEN }}", "tenant_id": "{{ $env.AXONFLOW_CLIENT_ID }}", "connector_type": "n8n", "statement": "{{ $json.action_description }}", "operation": "execute" } |
| Record Decision | POST {endpoint}/api/v1/audit/tool-call | { "tool_name": "{{ $json.tool_name }}", "tool_type": "n8n_decision", "workflow_id": "{{ $workflow.id }}", "input": {{ JSON.stringify($('Trigger').item.json) }}, "output": {{ JSON.stringify($json) }}, "success": true } |
| Audit Log (error branch) | same URL as Record Decision | same body shape, "success": false, "error_message": "{{ $json.error }}" |
| Wait for Approval | POST {endpoint}/api/v1/hitl/queue | { "client_id": "{{ $env.AXONFLOW_CLIENT_ID }}", "original_query": "{{ $json.description }}", "request_type": "workflow_step", "triggered_policy_id": "n8n-manual", "triggered_policy_name": "n8n manual approval", "trigger_reason": "Workflow gate", "severity": "medium", "expires_in_seconds": 86400 } — followed by a built-in Wait node configured for "On Webhook Call" mode. To actually resume on approval, store the Wait node's webhook URL and either trigger it from a polling sidecar (see HITL pattern) or have a reviewer POST to it manually after deciding in the portal. As of v8.1.0, AxonFlow can POST to this URL automatically via the notify_url field — see the v8.1.0 release notes for details. |
Always set an Idempotency-Key header on every HTTP node so n8n's Retry on Fail does not double-record. Template: {{ $execution.id }}-{{ $itemIndex }}-{{ $node.name }}.
Tier 2 — @axonflow/n8n-nodes-axonflow community node
A single community node with four operations, idempotency-key handling, and a credential type that uses Header Auth (not the buggy Bearer Auth class).
Install
n8n GUI (self-hosted)
Settings → Community Nodes → Install → @axonflow/n8n-nodes-axonflow. Restart n8n.
Manual
cd ~/.n8n/custom
npm install @axonflow/n8n-nodes-axonflow
# restart n8n
n8n Cloud does not currently allow unverified community nodes. Use the Tier 1 recipe until this package is verified.
Configure the credential
Credentials → New → AxonFlow API.
| Field | Description |
|---|---|
| Endpoint | Base URL of your AxonFlow Agent. SaaS: https://try.getaxonflow.com. Self-hosted: typically port 8080. |
| Client ID | Your tenant identifier. |
| User Token | Sent as the password half of HTTP Basic auth. Stored encrypted in n8n. |
The four operations
| Operation | Endpoint | Use it… |
|---|---|---|
| Check Policy | POST /api/v1/mcp/check-input | Before a sensitive action — branch on {allowed, block_reason?}. |
| Record Decision | POST /api/v1/audit/tool-call | After a successful action — capture inputs, outputs, success. |
| Audit Log | POST /api/v1/audit/tool-call | From error branches — same endpoint with success: false. |
| Wait for Approval | POST /api/v1/hitl/queue + Wait node | When you need a human signoff — pause until reviewer responds. |
HITL pattern: Wait node + webhook resume
The Wait for Approval operation creates the AxonFlow approval entry and immediately returns the approval_id. To actually pause-and-resume on the reviewer decision, pair it with a built-in Wait node:
AxonFlow Wait for Approval → Wait (mode: On Webhook Call) → downstream action
Configure the Wait node:
- Resume:
On Webhook Call - Webhook Method:
POST - Webhook URL is auto-generated by n8n. Store this URL — you'll either trigger it from a polling sidecar or hand it to a reviewer for manual resume.
AxonFlow v8.1.0+ can POST to the Wait node automatically via the
notify_urlfield on/api/v1/hitl/queue. Whennotify_urlis set, the platform fires a signed webhook to that URL on approval/rejection — no polling sidecar needed. For self-hosted deployments still on v8.0.x, two manual paths work.
Resume path A — polling sidecar (recommended for unattended workflows)
Run a small process that polls AxonFlow for the approval status and POSTs to the Wait node when the reviewer decides. Minimal reference (any language; bash + curl shown):
#!/usr/bin/env bash
# resume-sidecar.sh APPROVAL_ID WEBHOOK_URL
set -euo pipefail
APPROVAL_ID="$1"; WEBHOOK_URL="$2"
POLL_INTERVAL="${POLL_INTERVAL:-10}"
# Cap the total wait at the AxonFlow approval default (24h) plus a small buffer
# so a never-decided approval can't leak the sidecar process forever.
MAX_POLLS="${MAX_POLLS:-9000}" # 9000 × 10s = 25h
for i in $(seq 1 "$MAX_POLLS"); do
RESP="$(curl -sS --fail-with-body \
-u "$AXONFLOW_CLIENT_ID:$AXONFLOW_USER_TOKEN" \
"$AXONFLOW_ENDPOINT/api/v1/hitl/queue/$APPROVAL_ID")" || {
echo "warn: HITL fetch failed (iter=$i) — will retry" >&2
sleep "$POLL_INTERVAL"; continue
}
STATUS="$(echo "$RESP" | jq -r '.data.status')"
case "$STATUS" in
approved|rejected|overridden|expired|cancelled)
curl -sS --fail-with-body -X POST -H 'Content-Type: application/json' \
-d "$(echo "$RESP" | jq '.data')" "$WEBHOOK_URL"
exit 0
;;
esac
sleep "$POLL_INTERVAL"
done
echo "error: HITL approval $APPROVAL_ID timed out after $MAX_POLLS polls" >&2
exit 1
Invoke once from the n8n workflow itself (an Execute Command or HTTP node), or run it as a long-lived process keyed by approval_id. The Wait node receives the reviewer decision payload as $json in the next node.
The terminal-states case (approved | rejected | overridden | expired | cancelled) covers every status the AxonFlow HITL service emits — anything else is a transient pending and keeps polling. If the n8n Wait node has its own wait-timeout and you'd rather not leak the sidecar, kill the process when the workflow expires.
Resume path B — manual portal resume
The reviewer copies the Wait node's webhook URL from the n8n workflow editor and POSTs to it from the AxonFlow portal's approval-detail screen after approving or rejecting. Works without a sidecar; trades automation for simplicity. Recommended for small operations teams or during integration smoke-tests.
On v8.1.0+, pass the Wait node's webhook URL as notify_url in the HITL queue request and AxonFlow will POST to it automatically on approval — the polling sidecar can be retired. The Wait-node side of the workflow is unchanged.
Idempotency by default
Every operation sends an Idempotency-Key header. The default template is:
{{ $execution.id }}-{{ $itemIndex }}-{{ $node.name }}
This pins the request to a unique tuple — execution × item × node — so a future server-side deduplicator can reject re-sends from n8n's Retry on Fail. The header is structurally correct today.
Platform-side
Idempotency-Keyconsumption is live. As of platform v8.1.0, the three endpoints (/api/v1/mcp/check-input,/api/v1/audit/tool-call,/api/v1/hitl/queue) consume theIdempotency-Keyheader and deduplicate retries within 24 hours. Duplicate requests return the cached response with anIdempotent-Replayed: trueheader. See the v8.1.0 release notes for details.
Override the key only if you have a domain-specific deduplication identifier (e.g. an upstream request ID that you want to dedupe across executions).
Bearer Auth gotcha
n8n's built-in Bearer Auth class silently drops the Authorization header in some versions (n8n#15261). Credentials look configured, the test passes, every actual call returns 401.
The AxonFlowApi credential bundled with this package uses the Header Auth pattern instead — Authorization is built inline from clientId + userToken and attached to every request. You do not need to worry about the bug; it is documented here so you do not get caught by it when building a Tier 1 recipe.
Example workflow
The @axonflow/n8n-nodes-axonflow package ships an importable workflow at examples/governed-loan-workflow.json that wires up:
- HTTP trigger receives a loan request,
- Check Policy evaluates the proposed action,
- IF branch — if
allowed=true, proceeds to issue the loan; ifallowed=false, routes through Wait for Approval and aWaitnode, - After the loan-issuance HTTP node succeeds, Record Decision captures the outcome,
- Error branch routes to Audit Log with
success: false.
Import it via Workflows → Import from File and point the Issue Loan (HTTP) node at your downstream service.
Reference
- Source + readme: github.com/getaxonflow/axonflow-n8n-node · npm
- n8n Wait node ("On Webhook Call"): docs.n8n.io
- n8n verification guidelines: docs.n8n.io
- n8n Bearer Auth header-drop bug: n8n#15261
- AxonFlow agent API:
/api/v1/mcp/check-input,/api/v1/audit/tool-call,/api/v1/hitl/queue
Implementation Checklist
Before you put this integration in front of real users, connect it back to the core runtime docs:
- choose the request path in Choosing a Mode and Runtime Request Paths
- map the policies that should apply in Policy Hierarchy
- decide whether tool access belongs behind MCP Policy Enforcement
- use Community vs Evaluation vs Enterprise when approval queues, evidence export, SSO, SCIM, or longer retention become rollout requirements
