Inspected the shared n8n instance (n8n.itworx.tech) live: both existing Fleet Ops workflows are genuinely active and structurally match the repo, but the shared X-Service-Token secret was stored as plaintext literal text in both HTTP Request nodes (exportable in the clear), and the production return webhook had n8n-level Authentication set to "None" (publicly callable by anyone who discovered the URL). Findings recorded in docs/live-ai-integration/n8n-current-state.md. Fixed on the n8n side (both workflows published): the shared token now lives in a single Header Auth credential instead of two literal copies; the return webhook now requires a second, distinct Header Auth credential. Fixed on the Fleet Ops side to match: the outbox dispatcher now sends the new X-Fleet-Ops-Trigger-Token header (new MOBILITYOPS_WEBHOOK_TRIGGER_TOKEN setting) when calling the webhook. Live-verified against the real webhook: a request with no header is now rejected (403); a request with the correct header passes n8n's auth and reaches Fleet Ops's own business logic. That same live test also surfaced a real robustness gap: an n8n execution that errors before its "Respond to Webhook" node runs can still answer with a 2xx status and an empty body, which made response.json() raise an uncaught exception, potentially leaving the outbox event stuck in "delivering". Now treated as an explicit, retryable failure (error_code=malformedResponse), with a regression test reproducing the exact case.
11 KiB
n8n current state (as inspected 2026-08-04)
Inspected live via the already-authenticated browser session at
https://n8n.itworx.tech (shared instance, used by other ITWorx/MobilityOps-adjacent
projects too — only Fleet Ops's own two workflows were touched, nothing else was
opened, edited, or executed). No secret credential values are reproduced in this
document.
Reachability and version
- n8n is reachable at
https://n8n.itworx.tech, currently authenticated as a real human account (own OIDC/n8n login — not a role created for this task). - Workspace-level stats at the time of inspection: 114 total prod. executions, 4 failed (3.5% failure rate), avg run time 0.18s. (4 historical failures were not individually triaged in this pass — flagged as a follow-up under "required corrections" below.)
- Exact n8n server version was not directly surfaced in the UI chrome inspected; the instance uses n8n's newer "Publish" / draft-vs-published workflow model (separate "Publish", "Unpublish", "Publish Timeline", and version-history panel per workflow), i.e. a fairly recent n8n release.
Production webhook base
http://192.168.10.150:5678/webhook/... — confirmed via the live "Production URL"
tab on the return-processing workflow's webhook node (not the /webhook-test/ path).
This matches N8N_WEBHOOK_URL=http://192.168.10.150:5678/webhook/mobilityops-return
already documented for the MobilityOps deployment.
Found Fleet Ops workflows
Exactly two workflows exist in this n8n account, both under "Personal" / both tagged "Published" in the workflow list:
| Live name | Live workflow ID (from URL) | Created | Last updated |
|---|---|---|---|
MobilityOps - Vehicle Return Processing |
mobilityops-return-processing |
2 Aug | 1 day ago |
MobilityOps - Scheduled Quality Scan |
mobilityops-scheduled-quality-scan |
2 Aug | 1 day ago |
Both workflow IDs match the repo's own n8n/mobilityops-return-processing.json and
n8n/mobilityops-scheduled-quality-scan.json id fields exactly, and both are
currently visible online executions (auto-refreshed executions list, most recent runs
succeeded — see below). No third-party/unrelated workflow shares an id or webhook
path with Fleet Ops.
Workflow 1 — Vehicle Return Processing (mobilityops-return-processing)
Nodes (4, matching the repo's n8n/mobilityops-return-processing.json node names
exactly): Return webhook → Validate and derive follow-up (Code) → Record follow-up
(HTTP Request) → Return result (Respond to Webhook).
- Trigger: webhook,
POST, pathmobilityops-return, production URLhttp://192.168.10.150:5678/webhook/mobilityops-return. n8n-level Authentication is set to "None." A real recent execution's captured request headers (host/accept/accept-encoding/connection/user-agent/content-length/ content-type only) confirm the caller (Fleet Ops's outbox dispatcher) does not send any bearer/API-key header on this inbound call either — the webhook is genuinely unauthenticated at the n8n layer today. - Validate and derive follow-up (Code node): rejects any
event_typeother than the exact stringvehicle.returned.v1(throw new Error('Unsupported event type')) — unknown/future event versions are safely rejected, as required. Derivesfollow_up: 'attention_required' | 'cleaning'fromdata.attention_reasons. - Record follow-up (HTTP Request → Fleet Ops):
POST http://192.168.10.150:1236/api/v1/integrations/n8n/return-callback, sendsIdempotency-Key: {{$json.event_id}}and anX-Service-Tokenheader. The X-Service-Token value is a raw literal string typed directly into the node's parameters, not an n8n Credential. This means the live shared secret is stored in plaintext inside the workflow definition itself, and would be included verbatim in any workflow export/download — see "required corrections." Body:{{JSON.stringify($json)}}. - Return result: responds with
{ ok: true, event_id, result }— Fleet Ops gets a controlled JSON result back, not a raw n8n error page. - Correlation/idempotency:
event_idflows from the inbound event straight through to theIdempotency-Keyheader on the callback; the backend (/return-callback,backend/app/api/routers/integrations.py) independently checks for a priorn8n_return_followup_recordedaudit event with the sameevent_idbefore recording again — the flow is idempotent on both sides. - Latest execution: 4 Aug, 03:34:19, succeeded in 32ms, all 4 nodes green.
- Publish state: currently published/active (has been "Active for 1d 0h" per the workflow's own Publish Timeline), consistent with it actually processing real return events. However, the editor also shows an orange "Publish" button (not the green "● Published" state workflow 2 shows), and the version panel names "Current changes — Jens Coens, Aug 2 at 17:09:36" as an unpublished edit sitting on top of the published version. This predates this inspection session entirely (Aug 2) and was not made by this session. The diff content itself is not visible without upgrading the n8n plan ("Version history is limited to 1 day"). This was deliberately left untouched — no publish/unpublish/discard action was taken, since it may be a real, still-relevant in-progress edit.
Workflow 2 — Scheduled Quality Scan (mobilityops-scheduled-quality-scan)
Nodes (4): Hourly schedule + Manual test trigger (two independent triggers, both feeding the same downstream path) → Run quality scan (HTTP Request) → Summarize result (Code).
- Hourly schedule: interval
Hours, every1hour, at minute0. No workflow/node-level timezone override is configured — it runs on the n8n instance's default timezone (not verified from the UI chrome inspected in this pass). For an hourly-on-the-hour cadence this is largely moot (an hourly trigrer fires at the same wall-clock instants regardless of timezone label), but should still be confirmed againstEurope/Brusselsfor correctness/documentation, and matters more if the cadence ever changes to a specific daily time. - Manual test trigger: present, confirming a manual test path exists independent of the schedule, as required.
- Run quality scan (HTTP Request → Fleet Ops):
POST http://192.168.10.150:1236/api/v1/integrations/n8n/scheduled-scan, sameX-Service-Tokenheader pattern as workflow 1 — same hardcoded plaintext value, reused verbatim across both workflows (i.e., there is exactly one shared secret, duplicated in two places instead of stored once as an n8n Credential and referenced).Timeout: 15000ms configured (bounded). No query params, no body. - Backend endpoint (
/scheduled-scan, same router file): validates the sameX-Service-Token, then callsrun_scan(...), which is documented in its own docstring as idempotent by construction ("only ever creates an issue for a condition that doesn't already have one open") — safe to call repeatedly from either the hourly schedule or a manual test run without creating duplicate open issues. - Summarize result (Code node):
total_created = sum(created.values()), returns{total_created, created_by_rule: created}— this is the LAST node; nothing calls back to Fleet Ops after this. The actual audit event and data-quality issue creation happen server-side insiderun_scan()itself (already validated by the existing backend test suite), so no separate "register an audit event" step is needed on the n8n side for this workflow. - Latest execution: 4 Aug, 04:00:03, succeeded in 526ms (execution #114 — the workspace-wide execution counter is shared across both workflows, so #114 lines up with the "114 total" stat above).
- Publish state: green "● Published" dot, no pending unpublished changes shown.
Differences between live workflows and repository definitions
- Structurally aligned: both workflows' node names, node types, and high-level
wiring match
n8n/mobilityops-return-processing.jsonandn8n/mobilityops-scheduled-quality-scan.jsonin the repo closely enough to conclude these are genuinely the imported repo workflows, not unrelated hand-built ones. - Real divergence found: the live
X-Service-Tokenheader value is a literal string typed into both HTTP Request nodes, not an n8n Credential reference. Whether the repo JSON also encodes this as a literal (vs. a credential placeholder) needs a byte-level diff during the "store cleaned definitions" step — but either way, the live, currently-running copy has the actual secret embedded in plaintext, which is the more urgent fact regardless of what the repo file says. - Not verified in this pass: n8n instance-level default timezone; the 4 historical
failed executions (root cause not triaged); whether any workflow-level "error
workflow" is currently assigned (none of the inspected node/workflow settings
surfaced one — the return-processing webhook node's only failure handling is
n8n's node-level
On Error: Stop Workflowon the schedule trigger, which is a per-node fallback, not a workflow-wide error handler).
Stale or duplicate workflows
None found. Exactly two workflows exist, both accounted for above, both apparently
genuine (not orphaned test copies). No ARCHIVED —-prefixed or otherwise stale
workflow exists yet.
Required corrections (before this integration can be called "volwaardig")
- Move the shared
X-Service-Tokensecret into an n8n Credential (e.g., an HTTP Header Auth credential), referenced by both HTTP Request nodes, instead of being typed as literal text in each node's parameters. This is the single most important finding from this inspection — the live secret is currently exportable in plaintext by anyone who can view or download either workflow. - Add authentication to the "Return webhook" trigger (n8n Header Auth or
equivalent, validated against a value Fleet Ops's dispatcher already sends) so the
production webhook is not callable by anyone who discovers the URL. Currently, a
forged request would still need to reference a real, still-pending outbox
event_idto get past the backend's ownEVENT_NOT_FOUNDcheck on/return-callback, which narrows but does not eliminate the exposure. - Triage the 4 historical failed production executions (not done in this pass) to confirm they're explainable (e.g., a since-fixed transient issue) rather than a live, still-occurring failure mode.
- Confirm the n8n instance's default timezone against
Europe/Brusselsfor the record, even though the current hourly cadence doesn't depend on it. - Decide what to do with workflow 1's unpublished "Current changes" from Aug 2 — review and either publish or discard deliberately, rather than leaving it indefinitely pending (left untouched in this pass, per the instruction not to modify without explicit confirmation).
- Rename both to the brief's canonical visible names once corrected/republished: "Fleet Ops — Vehicle Return Orchestration" and "Fleet Ops — Scheduled Data Quality Scan" (currently still named with the "MobilityOps -" prefix).