An invoice reaches the ERP. The API call times out. Did the ERP reject it, or did it accept it before the connection failed? A retry could fix the problem or create a duplicate.
That is where an automation becomes a business system. It must account for what happened, what remains uncertain, and what a person is allowed to do next.
For the document workflows I am building behind Kaliits, I prefer a dedicated application core once those responsibilities become central. Business rules should be testable, cases should have explicit state, and recovery should preserve decisions already made. LangGraph can help orchestrate parts of that system. n8n can still handle useful integrations around it.
My position is an architectural choice, not a claim that custom code automatically performs better. The advantage has to show up in a concrete exception: a duplicate delivery, a partial receipt, an unavailable provider, or an approval made against an outdated document.
What n8n and LangGraph actually give you
n8n is a practical way to connect applications and build workflows visually. An inbox trigger, document-reading API, CRM update and notification can become a useful process quickly. It also allows custom logic. A visual canvas does not mean there are no business rules.
It has real operational capabilities. Its queue mode documentation describes Redis, a shared database and worker processes. Its error workflows can react to failed executions. The Wait node can pause and resume a workflow. Dismissing n8n as unsuitable for business would ignore those features.
LangGraph gives developers a graph runtime for stateful orchestration. Steps operate on explicit state and route work through the graph. Checkpointers save graph state, while interrupts support pausing for human input.
These are different starting points. n8n supplies an integration environment and visual workflow editor. LangGraph is a component you build into an application. You still need APIs, authentication, storage, deployment, monitoring and an operator interface around that component.
Neither tool decides what an accepted receipt means, who may approve a mismatch, or how your destination system prevents duplicates. Those are application responsibilities.
A successful execution is not a completed business case
Consider an illustrative supplier-invoice workflow. The purchase order requests 100 units, the accepted receipt records 80, and the invoice bills 100. These are fictional quantities, not customer results.
An extraction step can read all three documents correctly. Every technical step can succeed. The invoice still needs attention.
The application must distinguish quantities ordered, delivered and accepted. It must apply the organization's confirmed policy, preserve the supporting pages, and put the discrepancy in front of the authorized reviewer. A missing receipt must remain missing evidence, rather than becoming a convenient zero or a model-generated guess.
This is why I separate extraction from validation. A model proposes candidate facts. Deterministic rules compare those facts under an explicit policy. Human review resolves uncertainty and exceptions.
An IF node can express a simple comparison. The maintenance question is whether the same rule can be tested, versioned and reused across intake channels without drifting. When the answer becomes difficult, I want that rule in a domain module with a clear contract.
A useful test is simple: given the same accepted receipt, invoice and confirmed policy version, the rule should produce the same discrepancy. It should not depend on which connector delivered the documents.
Why I moved the business core out of n8n
OCRAgent began as an n8n document workflow. I describe that history in why I separated the core. This article focuses on the decision another team faces when its workflow starts accumulating operational responsibilities.
The boundary I wanted was straightforward. Intake adapters collect documents. Reading providers produce facts and evidence. Workflow rules interpret those facts. Review actions record a person's decision. Delivery adapters handle external systems.
That structure makes it possible to change a reading provider without rewriting a quantity rule, or change an intake connection without changing the meaning of approval. It also gives the rule a place to live outside one particular automation canvas.
This is the engineering direction behind Kaliits: build around the customer's process, its evidence and its decision owner. A framework name matters less than the behavior those boundaries make possible.
Scale is also a correctness problem
n8n can scale workflow execution with workers. A custom worker service can also distribute jobs. Neither fact answers whether two workers can safely process the same case.
More concurrency can expose problems sooner: duplicate messages, conflicting writes, provider rate limits and approval against stale evidence. I want the system to distinguish a processing attempt from the durable business case it affects.
For a document workload, I would examine four boundaries:
1. Intake must persist the case and the intention to process it without leaving an unnoticed gap between database storage and queue publication.
2. Workers must identify the case and job they own, classify failures, and make duplicate delivery safe where the operation permits it.
3. Review must bind the decision to the evidence and policy version reviewed, then detect changes before applying that decision.
4. Delivery must keep an uncertain external result visible and reconcile it before a blind retry.
These are design requirements. The third and fourth need acceptance tests in each implementation; listing them does not mean every proposed integration already satisfies them.
The inspected OCRAgent source includes a transactional outbox: case intake writes processing jobs and publication intent inside a database transaction. A dispatcher then publishes that intent to Redis. The mechanism and its limits are explained in the outbox and Redis Streams guide.
An outbox does not guarantee exactly-once delivery. A crash after publication but before recording it can cause another publication attempt. Consumers still need durable identities and guarded effects.
For scale, the useful questions are queue age, completion latency, retry volume, duplicate handling, provider limits and unresolved cases. A throughput claim needs a load test with a stated workload and failure conditions. This article makes no measured throughput comparison between OCRAgent, n8n and LangGraph.
Where LangGraph earns its place
I would consider LangGraph when the orchestration genuinely needs branching state, repeated reasoning steps, inspectable intermediate results, or a pause followed by human input. A graph can make those transitions explicit in application code.
That does not make every document pipeline an agent problem. A predictable sequence of intake, extraction, rules and review may be clearer as an ordinary application service with a job queue.
OCRAgent reflects that distinction. At the source revision inspected for this article, its earlier invoice path uses LangGraph. Its newer dossier path uses a dedicated DossierProcessor with provider and workflow contracts. The dossier path is not evidence that LangGraph powers the entire application.
Persistence also requires care. LangGraph's persistence guide explains that in-memory checkpoints disappear on restart. Durable recovery requires appropriate persistent storage and configuration.
Its interrupt documentation also explains that resuming an interrupted node reruns code from the beginning of that node. Side effects before the interrupt must be safe to repeat or separated appropriately. Checkpointing alone does not make an ERP write idempotent.
For complex stateful orchestration, LangGraph can be a better fit for my implementation style because state transitions and reusable domain modules live together in code. That benefit comes with engineering ownership: tests, deployment, migrations, security and maintenance.
When I would keep n8n
I would keep n8n when the process mainly moves information between existing applications, the exception rules remain understandable, and the team can operate failures responsibly.
An email-to-CRM handoff with a notification may benefit more from existing connectors than from a new custom service. Native ERP imports, CSV uploads or existing capture features may solve the problem with less maintenance too.
For a larger process, a hybrid can be useful. n8n collects an event and submits it to an application API. The application persists a case and returns its identifier. n8n can notify people as the case changes, while the application owns validation and approval.
In that arrangement, the API response should say whether the case was accepted for processing. It should not imply that validation, approval or ERP delivery already happened. A timeout should be reconciled using the case identity or an idempotency key.
Keep the business rules in one authoritative place. Duplicating the same approval policy in n8n and in the application creates two versions to maintain.
How to choose the architecture
Start with the failure your operator must resolve, then choose the smallest architecture that can explain and recover it.
Choose n8n when integration speed is the main benefit and the team can keep rules, state and recovery clear. Choose a dedicated application core when the workflow owns complex domain rules, evidence, permissions and durable case state. Add LangGraph when graph orchestration makes that core easier to understand and operate.
My preference for the latter is strongest when an operator needs to ask: which document supported this value, which rule caused the hold, who changed the decision, and what happened after the external call failed?
For that scope, I would rather maintain explicit application contracts and tested rules than keep expanding one workflow around every exception. That is how I want Kaliits to earn an advantage: through behavior a customer can inspect and acceptance tests they can understand.
If you are evaluating an existing workflow, bring one recurring exception, the source documents and the destination system. Discuss that workflow with Kaliits. We can determine which integrations to keep and which responsibilities need their own application boundary.
Source and scope
Vendor capabilities were checked against the linked official n8n and LangGraph documentation on 8 October 2026. The architectural recommendations are my interpretation of those capabilities.
OCRAgent implementation references use public source revision 647717e, inspected from the committed source rather than unpublished local changes. This is an architecture analysis, not a production benchmark, an accuracy measurement, or proof that every proposed destination integration is complete.