# n8n integration ## Role n8n orchestrates secondary cross-system work after MobilityOps commits canonical state. It is not the domain engine. ## Required live workflow: return processing Input: `vehicle.returned.v1` webhook event. Steps: 1. validate event type and schema; 2. derive a follow-up category from the already-calculated state; 3. call the narrow MobilityOps callback endpoint with event ID and follow-up summary; 4. return a stable workflow result; 5. on errors, fail visibly so the outbox dispatcher can retry. The canonical, live-validated definition is `n8n/workflows/fleet-ops-vehicle-return.json` (see `n8n/workflows/MANIFEST.md`); it authenticates via named Header Auth credentials rather than a literal token, per the live-hardening pass documented in `docs/live-ai-integration/n8n-current-state.md`. ## Second live workflow: scheduled quality scan RAGcore is not connected in this environment, so the originally sketched "knowledge sync" workflow below remains deferred (see "Deferred: knowledge sync"). The second implemented workflow does not depend on RAGcore or MCP Hub, so it is not blocked by them. Input: hourly schedule trigger, or a manual trigger for on-demand testing. Steps: 1. call the narrow, service-token-protected `POST /api/v1/integrations/n8n/scheduled-scan` endpoint; 2. the endpoint runs the same deterministic `run_scan()` domain function the manual "Run quality scan" UI action uses, and records a `data_quality_scan_run` audit event with `actor_type=service`; 3. return counts of newly created issues per rule type. `run_scan()` only ever creates an issue for a condition that does not already have one open, so a duplicate or overlapping trigger (a manual test run firing close to the scheduled one, or a retried HTTP call) does no duplicate domain work. The canonical, live-validated definition is `n8n/workflows/fleet-ops-data-quality-scan.json` (see `n8n/workflows/MANIFEST.md`), imported and published the same way as the return workflow (see `deploy/unraid/setup-scheduled-scan.sh` and `docs/17-runbook.md`). It is active on the live instance; a fresh import ships inactive until credentials are wired up and it is deliberately published. ## RAGcore procedure sync (in progress) RAGcore is now reachable in this environment; a live inspection of its real contract is recorded in `docs/live-ai-integration/n8n-current-state.md`. Workflow 3, "Fleet Ops — RAGcore Procedure Sync", is being built against that real contract (not the sketch originally in this section) — see `n8n/workflows/MANIFEST.md` for current status. ## Workflow error handler (in progress) Workflow 4, "Fleet Ops — Workflow Error Handler", is a central technical workflow attached to workflows 1-3 via n8n's per-workflow "Error Workflow" setting, reporting bounded, secret-free failure details to Fleet Ops. See `n8n/workflows/MANIFEST.md` for status. ## Outbox dispatcher - polls pending records; - claims with `FOR UPDATE SKIP LOCKED` or equivalent; - sends event with timeout; - exponential backoff with a small maximum attempt count; - supports explicit manual retry; - preserves last error and response metadata; - does not hold a database transaction open during network I/O. ## Prepared demo failure versus real failure The demo seed deliberately plants exactly one failed delivery (`BK-H-0020`, see `seed/workflow_runs.csv`). It exists to demonstrate retry and audit, so it must never be read as evidence that the automation is unhealthy. It is distinguished by its `last_error_code`, `demoScenarioTimeout` (`app.models.outbox.DEMO_SCENARIO_ERROR_CODE`) — not by a new column, so no migration is involved. A real timeout produces `connectionError`; the two are never confused. Consequences, all enforced by tests: - `/api/v1/integrations/status` reports `failed` (everything), `unexpected_failed` (real failures only) and `demo_scenario_failed` separately. - Only `unexpected_failed` can move n8n off `operational`. A prepared failure alone leaves the integration **operational** — a staged prop may not raise a red flag. - `latest_failure_at` is a health signal and therefore ignores the prepared failure; `latest_demo_scenario_at` reports it separately. - `/api/v1/workflows` marks the run with `is_demo_scenario: true`. The Automation page labels it "Prepared demo scenario", explains that it is a simulated temporary failure, and offers a distinct "Retry demo scenario" action. - A genuine later failure of that same event overwrites the code with the real one, and from that moment it counts as a real failure — the carve-out is narrow by construction. The retry itself is real in both cases: the event goes back on the outbox and the dispatcher delivers it to the configured n8n webhook like any other, so 19 succeeded + 1 failed becomes 20 succeeded + 0 failed only when n8n genuinely accepts the delivery. Nothing is marked succeeded without a real round trip. The audit entry records `demo_scenario: true/false` so a staged retry is never mistaken for a production fix. A demo reset recreates the original 19 + 1 scenario.