Skip to main content
< All Topics
Print

Workflow Adapter Integration

name: workflow-adapter-integration

description: Extend, debug, and maintain the ITI_Workflow_Adapter pattern that routes product traffic through n8n webhooks with fallback to direct Claude API. Use when integrating a new product, debugging fallback behavior, or modifying webhook paths.

Workflow Adapter Integration

Instructions

Manage the adapter pattern that connects ITI products to n8n workflows. The adapter tries n8n first and falls back to direct Claude API calls if n8n is unavailable.

Architecture:


Product Code
    │
    ▼
ITI_Workflow_Adapter.run_agent(webhook_path, query, session_id)
    │
    ├─ n8n_available()? ──YES──▶ POST /webhook/{path} ──200──▶ return JSON
    │                                                   ──fail─┐
    │                                                          │
    └─ NO ─────────────────────────────────────────────────────┘
                                                               │
                                                               ▼
                                                     fallback() → claude_api->send_message()

Platform implementations:

Platform Adapter File Health Check Webhook Call
PHP (WordPress) ITI/shared/wordpress/api-clients/class-iti-workflow-adapter.php GET /healthz via wp_remote_get POST /webhook/{path} via wp_remote_post
Swift (iOS/macOS) {product}/Services/N8nWorkflowClient.swift GET /healthz via URLSession POST /webhook/{path} via URLSession
Python (FastAPI) Inline N8nWorkflowClient class in proxy.py GET /healthz via httpx POST /webhook/{path} via httpx
Rust (Tauri) src-tauri/src/lib.rscall_n8n_webhook command GET /healthz via reqwest POST /webhook/{path} via reqwest
TypeScript (Tauri frontend) src/utils/n8nClient.ts Via Rust backend Via Rust backend (Tauri invoke)

Adding a new product (full checklist):

  1. Create n8n workflow via MCP SDK:
  • Follow the n8n-workflow-development skill build sequence
  • Webhook path: use the product slug (lowercase, hyphenated)
  • Include: Webhook trigger → AI Agent (with Claude + memory) → Respond to Webhook
  • Validate and publish
  1. Add adapter call in product code:
  • PHP: $adapter->run_agent('product-slug', $query, $session_id)
  • Swift: N8nWorkflowClient.shared.runAgent(webhookPath: "product-slug", query: query)
  • Python: n8n_client.run_agent("product-slug", query, session_id)
  • Only route non-streaming paths through the adapter; streaming stays on direct Claude
  1. Update test suite:
  • Add to WEBHOOK_ENDPOINTS in test_n8n_webhooks.py
  • Add to AI_AGENT_WORKFLOWS in test_n8n_workflows.py
  • Run smoke tests to verify
  1. Update migration document:
  • Add webhook path to Appendix E (Complete Webhook Reference)
  • Update Product Migration Matrix row

Webhook path naming conventions:

  • Use the n8n workflow’s actual webhook path, which is the product slug
  • The slug is lowercase, hyphenated, and matches the n8n workflow name
  • Product name != webhook path (e.g., “Farmers Bounty” product uses backyard-gardener webhook)
  • Always verify the actual path: curl -s "http://localhost:5678/api/v1/workflows/{id}" -H "X-N8N-API-KEY: $N8N_API_KEY" | python3 -c "import sys,json; [print(n['parameters'].get('path','?')) for n in json.load(sys.stdin)['nodes'] if 'webhook' in n['type']]"

Timeout configuration: The PHP adapter’s timeout_for_path() method sets per-webhook timeouts:

  • Default: 60 seconds (single-agent workflows)
  • Router workflows (personal-advisor, estate-advisor, travelplanner): 120 seconds
  • Sequential pipelines (consulting-pipeline): 900 seconds
  • When adding a new router or pipeline, update the arrays in timeout_for_path()

Response normalization: ITI_Workflow_Adapter::extract_output($response) normalizes diverse n8n response shapes:

  1. Checks for output key (standard AI Agent response)
  2. Checks for text key
  3. Checks for content array with text blocks (Anthropic-style)
  4. Falls back to JSON-encoding the entire response

Products that return custom JSON structures (Design QA Review, Design System Audit, Personal Advisor Router) bypass extract_output() and use the raw response.

Debugging fallback behavior:

  1. Check if n8n is reachable: curl -s http://localhost:5678/healthz
  2. If reachable, test the specific webhook: curl -s -X POST http://localhost:5678/webhook/{path} -H "Content-Type: application/json" -d '{"query":"test","sessionId":"debug"}'
  3. If webhook returns non-200: check n8n Executions for error details
  4. If adapter falls through to Claude: check PHP error log or application logs for “n8n unavailable” messages
  5. Force fallback test: docker compose stop n8n → verify product still works → docker compose start n8n

Request/response format: All adapters send:


POST /webhook/{path}
Content-Type: application/json

{"query": "User's question", "sessionId": "optional-session-id"}

n8n workflows respond with:


{"response": "Agent's answer text"}

Some workflows return custom structures; always check the specific workflow’s Respond to Webhook node configuration.

Table of Contents