Sample written n8n review · internal demonstration
Validated webhook to a fictional contacts API
A focused review of a five-node training workflow. The happy path and input rejection are clear, but the workflow should not be exposed publicly until request authentication, duplicate protection, and transport-error handling are defined.
Demonstration only. This is not client work, a testimonial, or evidence of a client outcome. It reviews Argos-owned synthetic files and a local fictional API. No production system, account, credential, or personal data was accessed.
Review date21 Sep 2026
Export reviewed5 nodes
Evidence reviewed5 HTTP runs
Review decisionRevise before public use
Scope and intended behaviour
The workflow accepts a JSON contact, allows a non-empty name up to 100 raw characters and a status of new or active, then forwards the normalized object to a local fictional API. Invalid input returns HTTP 400 without calling that API.
Receive webhook ID: receive · POST
Validate name and status ID: validate · IF
POST local fictional API ID: post · HTTP Request
Return API result ID: response · Respond
False branch:Validate name and status → Reject invalid input (ID: invalid, HTTP 400).
The export, test harness, recorded HTTP results, execution summary, and pre-activation result were read together. The recorded run used Node 24.21.0 and n8n 2.38.7 on 11 Sep 2026.
Assertions passed5 / 5
API calls observed2
Objects stored1
n8n status5 “success”
Recorded case
HTTP / calls
What it establishes
Spaced valid name
201 / 1
The outbound body contains the trimmed name and accepted status.
Blank name
400 / 0
The false branch blocks a whitespace-only name.
Unknown status
400 / 0
The allow-list rejects paid.
Missing name
400 / 0
A missing name does not reach the API.
API returns 503
503 / 1
neverError: true allows the response node to relay the upstream 503.
Interpretation guardrail: all five n8n executions are marked success because each workflow path completed. That execution status does not mean five successful contact creations.
Prioritized findings
P0 · before exposure
The webhook has no request authentication
receive → validate
Evidence
Receive webhook defines POST, path, and response mode only; its options object is empty. No preceding node validates a signature, token, timestamp, or caller identity.
Suggested correction
Require a secret header or, preferably, verify an HMAC over the raw request body plus a timestamp. Reject missing, invalid, or stale signatures before the HTTP Request node. Keep the secret in n8n credentials or environment-backed configuration.
P0 · data integrity
Repeated deliveries can create duplicate contacts
receive → post
Evidence
The outbound body contains only name and status. The export has no event ID lookup, idempotency key, durable deduplication step, or upsert operation. The test sends each valid payload once, so duplicates are untested.
Suggested correction
Require an immutable event ID. Create a durable processing record atomically, perform the side effect, then mark it succeeded. Do not mark the event permanently processed before the side effect succeeds. If the outcome is uncertain, keep a recoverable state and reconcile it before retrying. Alternatively, pass the event ID to an API that guarantees idempotent creation. Define retention and responses for in-progress, succeeded, and retryable events.
P1 · reliability
Transport failures have no explicit response path
post → response
Evidence
POST local fictional API uses a 5-second timeout and neverError: true, but has only its normal output connected. The evidence covers an HTTP 503 response from a reachable server. It does not cover connection refusal, DNS failure, TLS failure, invalid JSON, or timeout.
Suggested correction
Route the HTTP Request node's error output inside the same execution to a Respond to Webhook node that returns a stable 502 or 504 response. Use a separate error workflow only for logging or alerting; it is not a guaranteed response path for the original webhook request. Decide retry policy only after idempotency exists, because a timed-out write may have committed upstream.
P1 · interface
The upstream response is exposed without a boundary
response
Evidence
Return API result returns $json.body and $json.statusCode directly. Any future upstream error detail or schema change therefore becomes part of the public webhook response.
Suggested correction
Map known upstream outcomes to a small documented response schema, for example {ok, contactId} on success and a stable public error code on failure. Log internal details separately and redact credentials, headers, and payload fields.
P2 · maintainability
Validation and normalization disagree at the length boundary
validate ↔ post
Evidence
The IF node checks $json.body.name.length <= 100, while the HTTP node sends $json.body.name.trim(). A valid normalized name can be rejected solely because surrounding spaces make the raw value exceed 100 characters.
Suggested correction
Normalize once in a Set/Edit Fields or Code node, then validate and send that same normalized field. Split invalid-name and invalid-status outcomes if callers need actionable feedback.
Working as designed
The invalid-input branch prevents the side effect
validate → invalid
Three recorded invalid cases each returned HTTP 400 and made zero API calls. The explicit allow-list for status is easier to audit than a permissive string check. Preserve this fail-before-write ordering when authentication and normalization are added.
Recommended change order
Define the public contract: authentication method, stable response schema, event ID, and accepted normalized fields.
Protect the side effect: authenticate first; implement atomic processing/succeeded states and reconciliation of uncertain failures before adding retries.
Make failures deterministic: separate received HTTP errors from transport failures; map both to controlled webhook responses.
Expand the tests: duplicate event, invalid signature, stale timestamp, connection refusal, timeout after a possible write, non-JSON upstream response, and the 100-character normalized boundary.
Add operations: use a separate error workflow for redacted logging or alerts, define retention, and add a deployment check that confirms the intended workflow is active.
Release recommendation: suitable as a local learning demonstration. For an internet-facing or production workflow, resolve both P0 findings and test the transport-error path before activation.
Limits of this review
Static review of the exported JSON plus supplied local evidence; the workflow was not re-run for this document.
The target is a loopback fictional API with no credentials. Real API authentication, quotas, schema, and privacy requirements were not assessed.
No proof was supplied for concurrency, duplicate delivery, retry behaviour, connection refusal, timeout, malformed upstream responses, or public load.
The pre-activation 404 proves the production webhook was unregistered before activation in that local run. It is operational evidence, not a workflow defect.
Security observations are limited to this five-node export. Host, network, database, n8n user management, and credential-store configuration are outside scope; no conclusion is drawn about controls that may exist outside the export.
Related executed component: duplicate protection
Inspect the local HTTP + SQLite demonstration: concurrent retries, conflicting payloads and a clean restart. This separate Python component illustrates one implementation approach; it does not modify the n8n workflow reviewed above and is not an executed n8n integration.
One redacted workflow of up to 25 nodes, its expected result and an input/output example. USD 35, within two business days after scope and start date agreement; one clarification round. Review only: no production access, credentials, video call or repair.