n8n Document Processing Workflow
Build an n8n document workflow with file checks, duplicate stops, an extraction adapter, human approval, and one idempotent draft write.
— Craig Major
An n8n document-processing workflow takes labelled-text JSON from a webhook or a manual fixture, rejects anything that is not text/plain labelled text, hashes the source with the official Crypto node, and stops a duplicate before review. Extraction is an adapter, not OCR. Code then checks required fields and totals. Every non-duplicate stays pending until a person POSTs an explicit approve to a resume URL that the workflow exposed before Wait. Only then does a draft-only destination write, using the same idempotency key if the first response is lost. A 24-hour Wait limit with no decision leaves the record pending. It does not reject.
Use this guide for the n8n orchestration layer. Start with Document Processing Automation if you are deciding what to automate, Invoice Processing Automation for invoice-to-draft rules, or Intelligent Document Processing for classification and extraction choices.
Who this is for
Use this when n8n is already the automation host, or the shortlist, and the first job is one document family from one intake channel to a reviewed draft.
Prerequisites:
- One n8n Cloud or self-hosted instance you already run
- One intake path for the first build. The starter ships Manual Trigger and Webhook.
- A named reviewer who can approve or reject
- A destination that can create a draft and honour an
Idempotency-Keyheader or an equivalent lookup - Fictional or approved files before any live write
Keep payment, refunds, supplier bank-detail changes, tax decisions, and high-confidence auto-release outside the first workflow.
If you still need a register rather than an orchestration engine, use Invoice Tracker.
n8n extraction is not OCR
n8n Extract From File converts supported binaries, including PDF, to JSON for later nodes. Official docs describe format extract. They do not describe a scan-OCR product. A text-layer PDF can become text. An image-only scan cannot, unless you add an external adapter.
Scan OCR and document AI are optional later nodes: HTTP Request to a vendor API, or Anthropic or Gemini Analyze Document. Those calls are paid. Document bytes leave the n8n instance. Information Extractor can structure text that already exists. It is also not OCR.
The five fictional cases below are labelled text. They do not measure OCR.
What changes when the workflow is in n8n
| Manual step | n8n node or pattern | Human responsibility |
|---|---|---|
| Watch a drop folder or inbox | Webhook POST of labelled-text JSON, or a documented Gmail, Drive, or OneDrive swap | Resolve unsupported sources. Default JSON does not read Raw body or binaries. |
| Open the file and guess the type | Code MIME and size allow-list for text/plain |
Review unsupported_type, missing_raw_text, and file_too_large. PDF and image fail in the default. |
| Remember which files already arrived | Official Crypto SHA-256, plus optional vendor plus document number | Confirm a later override policy, if you ever add one |
| Copy fields | Extraction adapter: labelled text for tests, Extract From File later for text PDFs, HTTP or LLM for scans | Check exceptions and the source file |
| Notice a total that looks wrong | Deterministic Code, not an LLM | Decide whether an exception may proceed |
| Ask someone to approve | Manual reviewer handoff that outputs $execution.resumeUrl before Wait, or Slack Approvals in production |
Copy the URL and POST decision=approve or decision=reject. Do not skip this. An empty Wait resume stays pending. |
| Re-enter a request after a destination timeout | Same Idempotency-Key on a second destination attempt |
Confirm that only one draft exists |
n8n does not ship an Idempotency node. The key is an application contract. HTTP Request can send the header. The destination must honour it.
Worked example
Cedar & Quay Fabrication Ltd is a fictional Toronto buyer. Ridgemont Industrial Supply Co sends invoice INV-TP-2401 dated 8 September 2026. Safety vests, clipboards, and fastener bins. Subtotal CAD 400.00, HST CAD 52.00, total CAD 452.00. Purchase order PO-CQ-188.
In the starter, Manual Trigger or Webhook receives labelled-text JSON. Code accepts text/plain only. The official Crypto node hashes the UTF-8 raw_text. The labelled-text adapter returns vendor, document number, date, currency, lines, and totals. Validation passes. Status becomes pending_review. No draft exists.
Prepare Reviewer Handoff writes resume_url. A person copies that URL and POSTs decision=approve. FakeERP writes one draft. If the same bytes arrive again as 02-duplicate-invoice.txt, the workflow stops. Review is not opened. The destination is not called. If the 24-hour Wait limit resumes with no decision, the invoice stays pending_review and no draft is written.
Those companies and amounts also appear in the live Invoice Processing Test Pack. They are fictional. They are not customer results. They do not measure OCR.
The n8n pipeline
- Intake. Manual Trigger for fixtures, then an Edit Fields node that supplies editable labelled-text JSON (
raw_text,filename,mime_type,source_system). pinData stays empty. Webhook POST of the same JSON on pathfg-document-intake. Raw body is off. Form, Gmail, IMAP, Drive, OneDrive, SharePoint, and Dropbox are documented sub-workflow swaps. A first-party Dropbox Trigger was not confirmed on 8 September 2026. Pair Dropbox with Schedule or Webhook. - File check. Allow
text/plainand.txtonly. Default size cap 10 MiB. PDF, PNG, JPEG, binary, and missingraw_textfail closed. Fail into the error workflow for unsupported type or size. - Duplicate check. Official Crypto node hashes SHA-256 of
raw_text. Optional business key isnormalize(vendor)plus document number. On either hit: statusduplicate, zero review, zero destination attempts. The offline harness proves that case. The prepared JSON uses$getWorkflowStaticDataonly as a demo map. Official n8n docs say static data is unavailable in manual testing, only saves when a published workflow is called by a trigger or webhook, and may be unreliable at high frequency. Prove persistence through the active webhook, or swap in Data Table, Postgres, or Redis before production. Separate Manual Trigger runs do not prove the duplicate store. - Extraction adapter. Default Code parses labelled text so the five cases run without a paid account. It does not read a PDF or image binary. Swap Extract From File later for a text-layer PDF. Swap HTTP or Analyze Document only when you intend to process scans.
- Schema and arithmetic. Required for the invoice default: vendor, document number, date, total. Line amounts should support the subtotal. Subtotal plus tax must match the printed total within 0.011. This Code node is not an LLM. A second Crypto node hashes the idempotency material.
- Exception decision. Missing number and conflicting total still go to review. They do not auto-write. Low model confidence, if you add a model later, is also review. It is never auto-release.
- Manual reviewer handoff. Every non-duplicate hits
Prepare Reviewer Handoff, which writes$execution.resumeUrlonto the item, thenWait For Manual Reviewer Handoff(webhook resume, explicit POST, 24-hour limit). The prepared JSON does not send that URL. The operator copies it and POSTsdecision=approveordecision=rejectthrough a private channel. Wait resume has no authentication in this JSON. That is not a production approval endpoint. Slack Approvals is the preferred later gate because it can capture who responded and restrict approvers. If the wait limit resumes with no decision, status stayspending_reviewand the destination is not called. Only an explicitdecision=rejectrejects. - Draft-only write. After
approve, create a draft. The starter uses Code FakeERP. An optional HTTP Request node points athttps://example.invalid/fake-erp/draftsand stays disabled until you replace that URL with a draft endpoint. - Idempotent retry. Key = SHA-256 of
source_sha256 + ":" + document_type + ":" + (document_number or "none"). The prepared graph uses two destination nodes,Attempt Draft WriteandRetry Draft Write, with the same key and one stored write. The timeout fixture expects two attempts and one write. - Audit and errors. Append events: received, validated, duplicate_stopped, exception_flagged, review_requested, review_handoff_prepared, review_decided, destination_attempt, destination_drafted, error. Set a dedicated Error Trigger workflow in Workflow Settings. The error path must not write a draft.
Official n8n limits that change the build
These are product limits from n8n docs accessed 8 September 2026. They are not Flowgrammer load-test results.
- Default binary data mode keeps files in memory and can crash on large PDFs. Set
N8N_DEFAULT_BINARY_DATA_MODEtofilesystem,database,s3, orazurewhen files are large. - Queue mode does not support filesystem binary storage. Use database or external object storage, and share the encryption key across main and workers.
- Data tables default to 200 MiB for the whole instance (
N8N_DATA_TABLES_MAX_SIZE_BYTES). That is a demo duplicate store, not a records program. - Execution pruning defaults, when enabled, include a 336-hour age and a 10000-count cap. Waiting, new, and running executions are not pruned. Annotated executions are never pruned. A Wait that is still open survives default prune. A finished execution and its binaries may not.
n8n Cloud prices and concurrency quotas can change. Check the current n8n pricing page for the plan you intend to use.
Credentials belong in n8n Credentials. They do not belong in exported JSON.
Decision table
| Situation | Automatic | Needs a person |
|---|---|---|
| Allowed type and size | Yes | No |
| Same SHA-256 as a seen file | Stop. No review. | Only if you later write an override policy and a test for it |
| Same vendor plus document number | Stop. No review. | Same override rule |
| Missing document number | Route to review | Approve override or reject |
| Conflicting total | Route to review | Approve override or reject |
| Clean required fields | Route to review on a first build | Approve before any draft |
| High model confidence | Still review on a first build | Confidence is not approval |
| Wait or Slack timeout | Stay pending. No draft. | Resume or reject later |
| Destination writes then loses the response | Retry the same idempotency key | Confirm one draft id |
| Unhandled node error | Error workflow alert | Fix the execution. Do not post from the error path |
Human gates
A person keeps these decisions:
- Copy
resume_urlfromPrepare Reviewer Handoffand POST an explicit approve or reject - Override a missing number or a conflicting total only with an explicit approve
- Confirm the destination draft id after a destination timeout retry
- Treat an empty 24-hour Wait resume as still pending, not rejected
- Choose Slack later if they want a production approval channel
- Turn on an OCR or LLM adapter, knowing bytes will leave the instance
The prepared Wait resume has no authentication, so it is a test handoff rather than a public production approval endpoint. Slack Approvals need public HTTPS and a Signature Secret. An empty approver list means anyone who can see the message can act. Put sensitive approvals in a private channel.
The starter does not treat a high confidence score as a release.
Failure paths
| Failure | Expected result | Next action |
|---|---|---|
| Unsupported MIME or extension, including PDF or image in the default | unsupported_type; error path; no draft |
Supply labelled-text JSON or add a documented adapter |
| File over 10 MiB | file_too_large; no draft |
Raise the cap only after binary storage is not in-memory |
| Duplicate SHA-256 or business key | duplicate; 0 review; 0 destination attempts |
Leave the first record alone |
| Blank invoice number | missing_invoice_no; pending until reject or override |
Collect the number from the source |
| Printed total disagrees with subtotal plus tax | conflicting_total; pending |
Check the source math |
| Reviewer never answers, or the 24-hour Wait limit resumes with no decision | timed_out; still pending_review; 0 writes |
POST a later explicit approve or reject. Do not auto-write |
| Draft write succeeds, response lost | Retry same key; writes stay 1 | Confirm the stored draft id |
| Node exception | Error workflow; 0 destination writes | Inspect the execution. Do not post from the alert |
Test cases
Walk the five labelled-text files. They are the same suite as the Invoice Processing Test Pack. They do not measure OCR.
| Case | File | Without reviewer | After simulated review |
|---|---|---|---|
| Clean | 01-clean-invoice.txt |
pending_review; 0 attempts; 0 writes |
approve → one draft |
| Duplicate | 02-duplicate-invoice.txt (same bytes as clean) |
duplicate; 0 review; 0 writes |
No review |
| Missing number | 03-missing-invoice-number.txt |
missing_invoice_no; 0 writes |
reject → no draft |
| Conflicting total | 04-conflicting-total.txt |
conflicting_total; 0 writes |
reject → no draft |
| Lost destination response | 05-ambiguous-destination-timeout.txt |
pending_review; 0 writes |
approve → attempts ≥ 2, writes 1, same draft id |
Offline Python in the starter pack proves those outcomes and the prepared JSON graph. The repository CI also imports both JSON files into n8n 2.37.11. Those checks do not execute n8n Cloud, a self-hosted workflow, Slack, the live Wait resume item shape, or Extract From File behaviour.
If you are choosing a vendor extractor rather than n8n-native text extract, use the IDP Requirements Worksheet.
What to measure
Count files received, unsupported-type stops, duplicate stops, pending reviews, reviewer response time, drafts created, timeout retries, and error-workflow fires.
Use those observed counts in the Automation ROI Calculator. Counts alone do not establish cash savings.
No Flowgrammer-measured OCR accuracy or n8n Cloud price is reported here.
How to use the starter workflow
The Flowgrammer n8n document-processing starter is available on GitHub. It includes the main and error workflows, five fictional labeled-text fixtures, expected results, setup notes, and offline validation scripts.
Use the starter as a controlled reference build:
- Read the pack README when the folder is available.
- Run
python3 tests/validate_workflows.py,python3 tests/test_wait_resume_contract.py,python3 tests/test_wait_timeout_path.py,python3 tests/test_manual_fixture_input.py,python3 tests/run_offline_harness.py, andpython3 tests/test_idempotency.py. Those commands use the Python standard library only. - Load the two workflow JSON files on an instance you already operate. Set the Error workflow in Settings. The repository CI confirms that n8n 2.37.11 accepts both files; it does not execute the workflow.
- Keep Slack, OCR, and destination HTTP credentials in n8n Credentials.
- Walk the five fictional cases. A clean approve may write one draft. A duplicate must write none. An empty Wait resume must stay pending.
- Enable Extract From File or a vendor OCR adapter only after the gates pass on labelled-text JSON.
Community template 17806 has license set to null in the templates API, so it remains an architecture reference rather than Flowgrammer source material.
Implementation sequence
- Write one sentence that names intake, document family, reviewer, and draft object.
- Lock the internal field list, the SHA-256 duplicate rule, and the idempotency key.
- Walk the five fictional cases on paper.
- Load the prepared workflow JSON. Leave paid OCR off. Record the n8n version and the import result, then test the live Wait path separately.
- Prove that no reviewer means zero destination attempts, and that a duplicate never opens review.
- Point the destination at a draft-only endpoint. Send the idempotency key.
- Run the five cases. Then, if you need scans, add one OCR adapter and record your own failures.
- Keep human review on every live write until you have those records.
If intake, reviewer, or destination is still unclear, start with a Document Workflow Opportunity Audit. That is the existing AI Success Audit with a document-workflow focus.
Realistic limitations
- Extract From File is not scan OCR.
- Labelled-text fixtures prove gates, not OCR quality.
- GitHub Actions imports both files into n8n 2.37.11. It does not execute n8n Cloud, a self-hosted workflow, or the live Wait resume path.
- Default in-memory binary storage can crash. Queue mode cannot use filesystem binary storage.
- Data tables are not a compliance archive. Official n8n docs prefer them over
$getWorkflowStaticDatafor persistence between executions. Static data is unavailable in manual testing and is not a production recommendation. - Execution prune deletes finished execution data. It does not delete waiting executions.
- The starter does not pay, refund, or post a final accounting entry.
- Use the Invoice Processing Automation guide for AP field mapping and invoice-to-draft rules.
Next step
If the intake channel, the reviewer, and the draft object are already named, scope a Document Automation System through AI Automation Systems. Bring the five fictional cases and your own redacted samples.
If several document workflows are competing, use the AI Success Audit first.
Sources
- Flowgrammer, "Document Processing Automation: What to Automate First", Flowgrammer, accessed 2026-09-08, /insights/document-processing-automation
- Flowgrammer, "Invoice Processing Automation", Flowgrammer, accessed 2026-09-08, /insights/invoice-processing-automation
- Flowgrammer, "Invoice Processing Test Pack", Flowgrammer, accessed 2026-09-08, /resources/invoice-processing-test-pack
- Flowgrammer, "Intelligent Document Processing", Flowgrammer, accessed 2026-09-08, /insights/intelligent-document-processing
- n8n, "Extract From File", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.extractfromfile/
- n8n, "Webhook", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/
- n8n, "Crypto", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.crypto/
- n8n, "Wait", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/
- n8n, "Slack Approvals", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.slack/approvals/
- n8n, "Handle errors gracefully", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/build/flow-logic/handle-errors-gracefully/
- n8n, "HTTP Request", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/
- n8n, "Handle binary data", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/deploy/host-n8n/configure-n8n/scaling/handle-binary-data/
- n8n, "Enable queue mode", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/hosting/scaling/queue-mode/
- n8n, "Data tables", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/build/work-with-data/data-tables/
- n8n, "Manage execution data", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/deploy/host-n8n/configure-n8n/scaling/manage-execution-data/
- n8n, "Create and edit credentials", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/build/understand-workflows/create-and-edit-credentials/
- n8n, "Edit Fields (Set)", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.set/
- n8n, "getWorkflowStaticData", n8n Docs, accessed 2026-09-08, https://docs.n8n.io/build/code-in-n8n/cookbook/built-in-methods-and-variables-examples/getworkflowstaticdata/
- n8n, Template API workflow 17806, accessed 2026-09-08, https://api.n8n.io/api/templates/workflows/17806
Frequently asked questions
What is an n8n document processing workflow?
It is an n8n pipeline from labelled-text JSON intake to a reviewed draft. Nodes can validate, hash, extract, and route. A person still POSTs an explicit approve before any draft write.
Is Extract From File the same as OCR?
No. Extract From File reads supported file formats into JSON. Scanned or image-only PDFs need an external OCR or document-AI adapter.
Does the starter auto-write high-confidence documents?
No. Confidence, if you add a model later, can only send a document to review. It cannot release a draft.
How do duplicates stop?
Hash the source bytes. Optionally also key vendor plus document number. On a hit, skip review and skip the destination.
What if the destination writes a draft and the response is lost?
Retry with the same idempotency key on a second destination node. The timeout fixture expects two attempts and one write. An empty Wait resume is a different timeout: the record stays pending and the destination is not called.
Where can I get the Flowgrammer n8n starter?
The starter is in the public Flowgrammer document-automation repository on GitHub. The repository tests the workflow structure and confirms that n8n accepts the JSON. Run the live Wait-resume path in your own n8n environment before using it with real documents.