Flowgrammer

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-Key header 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

  1. 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 path fg-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.
  2. File check. Allow text/plain and .txt only. Default size cap 10 MiB. PDF, PNG, JPEG, binary, and missing raw_text fail closed. Fail into the error workflow for unsupported type or size.
  3. Duplicate check. Official Crypto node hashes SHA-256 of raw_text. Optional business key is normalize(vendor) plus document number. On either hit: status duplicate, zero review, zero destination attempts. The offline harness proves that case. The prepared JSON uses $getWorkflowStaticData only 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.
  4. 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.
  5. 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.
  6. 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.
  7. Manual reviewer handoff. Every non-duplicate hits Prepare Reviewer Handoff, which writes $execution.resumeUrl onto the item, then Wait For Manual Reviewer Handoff (webhook resume, explicit POST, 24-hour limit). The prepared JSON does not send that URL. The operator copies it and POSTs decision=approve or decision=reject through 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 stays pending_review and the destination is not called. Only an explicit decision=reject rejects.
  8. Draft-only write. After approve, create a draft. The starter uses Code FakeERP. An optional HTTP Request node points at https://example.invalid/fake-erp/drafts and stays disabled until you replace that URL with a draft endpoint.
  9. Idempotent retry. Key = SHA-256 of source_sha256 + ":" + document_type + ":" + (document_number or "none"). The prepared graph uses two destination nodes, Attempt Draft Write and Retry Draft Write, with the same key and one stored write. The timeout fixture expects two attempts and one write.
  10. 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_MODE to filesystem, database, s3, or azure when 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_url from Prepare Reviewer Handoff and 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:

  1. Read the pack README when the folder is available.
  2. 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, and python3 tests/test_idempotency.py. Those commands use the Python standard library only.
  3. 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.
  4. Keep Slack, OCR, and destination HTTP credentials in n8n Credentials.
  5. Walk the five fictional cases. A clean approve may write one draft. A duplicate must write none. An empty Wait resume must stay pending.
  6. 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

  1. Write one sentence that names intake, document family, reviewer, and draft object.
  2. Lock the internal field list, the SHA-256 duplicate rule, and the idempotency key.
  3. Walk the five fictional cases on paper.
  4. Load the prepared workflow JSON. Leave paid OCR off. Record the n8n version and the import result, then test the live Wait path separately.
  5. Prove that no reviewer means zero destination attempts, and that a duplicate never opens review.
  6. Point the destination at a draft-only endpoint. Send the idempotency key.
  7. Run the five cases. Then, if you need scans, add one OCR adapter and record your own failures.
  8. 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 $getWorkflowStaticData for 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

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.