n8n Quick Guide (Data Extraction)
A beginner-friendly guide to build n8n workflows for extracting, cleaning, and storing document data.
n8n is a workflow automation tool where you connect nodes like building blocks. For data extraction, it helps you move from "raw files/emails" to "clean structured data" with minimal code.
This quick guide is for junior developers and focuses on one practical goal:
- Receive a document
- Extract useful text/fields
- Normalize the output
- Save it to a database or send it to an API
1. What You Need
- Docker Engine + Docker Compose v2 (
docker compose version) - n8n running locally or on a server
- A document source (Webhook, Google Drive, S3, or email)
- One extraction service (for example, Docling, Unstructured, or Apache Tika)
- A destination (Postgres, internal API, or vector index)
2. Start n8n Locally
npm install n8n -g / npx n8n is deprecated for new installs from n8n 3.0 (launching October 2026).
Use Docker-based setup instead.
Option A: One-line setup (fastest)
curl -fsSL https://get.n8n.io | shThis creates a local ./n8n folder, writes compose.yml + .env, and starts n8n on:
http://localhost:5678
Useful day-2 commands:
# stop
docker compose -f ./n8n/compose.yml down
# start again
docker compose -f ./n8n/compose.yml up -d
# upgrade
curl -fsSL https://get.n8n.io | sh -s -- --upgradeOption B: Manual Docker Compose (more control)
Use this when you want to customize services, environment variables, and local networking.
mkdir n8n && cd n8nCreate .env:
# Pin versions
N8N_VERSION=3.x.x
N8N_SANDBOX_VERSION=x.y.z
# Required secrets (set your own strong values)
N8N_RUNNERS_AUTH_TOKEN=replace-me
SANDBOX_API_KEYS=replace-me
SANDBOX_API_RUNNER_REGISTRATION_TOKEN=replace-me
SANDBOX_API_RUNNER_API_KEY=replace-me
N8N_SANDBOX_SERVICE_API_KEY=replace-me
SEARXNG_SECRET=replace-me
# Optional AI assistant model key (set later if preferred)
# N8N_INSTANCE_AI_MODEL_API_KEY=sk-...Start services:
docker compose up -d
docker compose psHealth checks:
curl -sf http://localhost:5678/healthz
docker compose logs -f n8nOpen http://localhost:5678, complete owner onboarding, then create your workflow.
2.1 Local self-hosting notes (important)
- Default setup uses SQLite (good for local/dev).
- For team/production, switch to Postgres.
- Persist data with volumes; avoid ephemeral containers.
- Keep secrets in
.env; do not commit them. - Expose only port
5678publicly.
2.2 AI Assistant setup on self-hosted n8n
The sandbox services can run by default, but the assistant stays off until you add a model key.
Enable assistant:
- Add provider key in your n8n instance AI settings (UI), or in
.env:
N8N_INSTANCE_AI_MODEL_API_KEY=sk-...- Restart n8n:
docker compose up -d n8n- Optional web-search provider:
- Default is bundled SearXNG.
- If you prefer Brave Search, set:
INSTANCE_AI_BRAVE_SEARCH_API_KEY=BSA-...2.3 Build a local AI assistant workflow (practical)
After instance-level assistant is enabled, create a user-facing workflow assistant:
Chat TriggernodeAI AgentnodeOpenAI Chat ModelorOllama Chat Model- Tool nodes (
HTTP Request,Postgres,Call n8n Workflow Tool) Respond to Webhookor chat output
Starter flow:
Chat Trigger -> AI Agent -> Tool(s) -> Response
Tips:
- Start with read-only tools first.
- Add guardrails for tool inputs and allowed domains.
- Log prompts and outputs for debugging and evaluation.
3. Core Concepts (Beginner Version)
- Node: one step in your automation
- Trigger node: starts the workflow
- Data item: JSON object moving between nodes
- Expression: dynamic value like
{{$json.document_id}} - Execution: one workflow run (one event)
Tip: always inspect node output before moving to the next node.
4. Your First Data-Extraction Workflow
Build this pipeline:
- Trigger:
Webhook(or Drive/S3 trigger) - Route:
Switchby file type (PDF, DOCX, EML) - Extract:
HTTP Requestto extraction API - Normalize:
Codenode to map into a clean schema - Persist:
Postgres(or HTTP API)
Suggested canonical schema:
{
"document_id": "string",
"checksum": "string",
"parser_version": "v1",
"source": "webhook|drive|s3|email",
"extracted_text": "string",
"structured": {},
"extracted_at": "2026-09-27T12:00:00.000Z"
}Why this schema helps:
- Easy reprocessing
- Easy debugging
- Stable contract for downstream apps
5. Reliability Basics (Do This Early)
- Enable retry on HTTP extraction nodes (for transient failures)
- Use idempotency keys:
document_id + checksum - Add dead-letter handling for failed runs
- Track simple metrics: error rate, empty output rate, latency
These 4 steps prevent most production issues in document pipelines.
6. Common Beginner Mistakes
- Skipping output inspection between nodes
- Mixing extraction and chunking in the same logic step
- Not storing parser version (hard to debug output drift)
- No fallback parser for edge-case files
7. Practical Next Step
After completing this quick guide, follow the deeper extraction playbook in content/docs/guides/automation/02-data-extraction.mdx for:
- Importable workflow JSON examples
- Dead-letter companion workflow
- SQL schema and operations for failure management
8. 10-Minute Lab (Hands-On)
Goal: build and test a tiny extraction workflow in one sitting.
Success criteria:
- You can trigger the workflow from a webhook request
- The output matches the canonical schema shape
- The workflow is stable across repeated test runs
Step A: Create 4 Nodes
Webhook(POST) with path:lab-extractSetnode namedMock ExtractorCodenode namedNormalizeRespond to Webhook
Connect them in this order:
Webhook -> Mock Extractor -> Normalize -> Respond to Webhook
Step B: Configure Mock Extractor
In Mock Extractor, set these fields:
document_id:={{$json.document_id}}source:={{$json.source || 'webhook'}}raw_text:Invoice #INV-2026-1001\nTotal: 1450 USD\nVendor: ACME Corpparser_version:lab-v1
Step C: Configure Normalize Code
Paste this code into Normalize:
const j = $json;
return [{
json: {
document_id: j.document_id || null,
checksum: j.checksum || "demo-checksum",
parser_version: j.parser_version || "lab-v1",
source: j.source || "webhook",
extracted_text: j.raw_text || "",
structured: {
invoice_number: "INV-2026-1001",
total_amount: 1450,
currency: "USD",
vendor: "ACME Corp"
},
extracted_at: new Date().toISOString()
}
}];Step D: Test Request
Use this request body in your webhook test:
{
"document_id": "doc-001",
"checksum": "sha256-demo-001",
"source": "webhook"
}Step E: Expected Output
Your response should include:
document_id: doc-001parser_version: lab-v1structured.invoice_number: INV-2026-1001structured.total_amount: 1450extracted_atas a valid ISO timestamp
Step F: Quick Validation Checklist
- Workflow executes successfully (green check)
Normalizeoutput has all required schema fields- Missing input fields do not crash the workflow
- Output is stable across 2-3 repeated requests
If this lab works, replace Mock Extractor with your real HTTP Request extraction node and keep the same normalization step.
9. Mini-Lab: Failure Simulation and Dead-Letter Capture
Goal: intentionally fail a workflow and verify the failure is captured for triage.
Success criteria:
- A forced failure appears in your dead-letter table
- You can query unresolved incidents successfully
- After fixing the issue, new runs complete without creating a new dead-letter row
Step A: Create an Intentional Failure
In your extraction workflow, duplicate your real extraction HTTP Request node and change the URL to an invalid endpoint, for example:
https://example.invalid/extract
Run one test execution to force an error.
Step B: Enable a Dead-Letter Workflow
Create a separate workflow with these nodes:
Error TriggerCode(build a compact failure payload)Postgres(insert intoextraction_dead_letter)
Use the table and SQL patterns from content/docs/guides/automation/02-data-extraction.mdx.
Step C: Failure Payload Example
In the dead-letter Code node, keep at least these fields:
{
"failed_at": "2026-09-27T12:30:00.000Z",
"workflow_name": "Document Extraction Pipeline",
"execution_id": "1234",
"last_node": "Extract via Docling",
"message": "Request failed with status code 500"
}Step D: Verify It Worked
Run this query and confirm the new failure appears:
SELECT id, failed_at, workflow_name, execution_id, last_node, message
FROM extraction_dead_letter
WHERE resolved_at IS NULL
ORDER BY failed_at DESC
LIMIT 20;Step E: Resolve and Re-test
- Mark the incident resolved.
- Fix the broken extraction URL.
- Re-run the workflow and confirm no new dead-letter entry is created.
This mini-lab teaches the full operational loop: fail fast, capture context, fix safely, and validate recovery.