Skip to content

IDP Activities

Version: 1.18.0  ·  ID: zoan-packages-idp  ·  Author: Zoan Software

The IDP (Intelligent Document Processing) package reads documents whose layout changes from one issuer to the next: equipment records, invoices, certificates. The idea is to describe what each value means rather than where it sits, so one configuration handles every layout of the same kind of document.

  • No session: each activity takes the document path and works on it.
  • The route picks itself: IDP Load Document checks whether the PDF has a text layer. If it does, it reads it (free and exact); if not, it scans it with Azure Document Intelligence. Both routes return the same structure, so nothing downstream changes.
  • Coordinates are unified: every word comes back in pixels at the dpi you ask for, with a top-left origin, whichever route the document took.

Each word carries its box (x, y, width, height). That is what lets you go back to the document from an extracted value: highlight where a serial number came from, show it to a person to validate, or audit an extraction months later.

Use the same dpi in PDF To Images and the boxes land exactly on top of the rendered page, with no conversion at all. That is the foundation any visual review is drawn on.

The first two are the normal path. The ones below are still there for when you need to get in between.

ActivityTypeOutputWhat it does
IDP Classifyidp-classifyobjectReads a document and tells which kind it is. Returns the document already read
IDP Extractidp-extractobjectExtracts an extraction project’s fields, routes to the format profile and locates every value on the page
IDP Load Documentidp-load-documentobjectLoads a document and returns its text and its words with positions
IDP Build Classifieridp-build-classifierobjectBuilds the schema that identifies which kind of document it is
IDP Build Schemaidp-build-schemalistSplits a taxonomy into the schemas that fit in one call
IDP Groundidp-groundobjectLocates every extracted value and flags what needs review

Two nodes:

IDP Classify → is it the kind you want? → IDP Extract

IDP Classify returns the document already read along with the verdict, and IDP Extract reuses it: the file is read once. With a scan that is not a minor optimisation — reading it again is paying Azure twice.

They are separate on purpose. What to do with a document that does not belong is your decision, not an activity’s: one that quietly decides not to do its job hides the control flow, and it also rules out the one thing you actually need when the queue brings a mix — classify once and route to the right project.

Worth keeping apart in your head, because they protect against different things:

  • Classifying answers “is this document the kind I think it is?”. Without that step, an invoice processed as an equipment record can slip a wrong value through with high confidence.
  • Grounding answers “is this value written in the document?”. It catches the model making a value up, not the document being the wrong one.

Neither covers the other. See IDP Build Classifier for the exact case that slips through.

The scan route uses Azure Document Intelligence (the prebuilt-layout model), which reads handwriting — which matters because in many documents the part nobody ever digitised (a maintenance history, a signature, a margin note) is handwritten. It needs no training and no labelling: it works on any document from day one.

Enqueue (once per batch):

List Files path = = env("HOSPITAL_DOCS_DIR") pattern = "*.pdf" recursive → files
Foreach items = = files itemVariable = path
Queue Add Item queueName = "device-records" reference = = path
specificContent = { "path": = path }

reference = the path. It is the queue’s idempotency key and the key the document will carry in Zoan Cloud, so re-running enqueues nothing twice and you can go from the item to its document.

Process:

Queue Process queueName = "device-records" itemVariable = item onError = continue
Set Variable name = content value = = item.SpecificContent
IDP Classify path = = content["path"]
projects = ["device-record"]
credential = = credential("anthropic-key")
dpi = 200
azureCredential = = credential("azure-di-key")
→ doc
If condition = = doc["recognized"] == false
Queue Set Transaction Status item = = item status = "successful"
output = { "discarded": true, "reason": = doc["reason"] }
Continue
IDP Extract document = = doc ← already read: not read again
project = "device-record"
credential = = credential("anthropic-key")
azureCredential = = credential("azure-di-key")
→ res
PDF To Images path = = content["path"] dpi = 200 outputDir = = tmpFolder → pages
Documents Upload path = = content["path"] reference = = item.Reference
pages = = pages dpi = 200 extraction = = res
documentType = = res["documentType"] → uploaded
Queue Set Transaction Status item = = item status = "successful"
output = { "documentId": = uploaded["id"],
"needsReview": = res["needsReview"],
"values": = res["values"] }

Queue Process closes each item on its own — successful if the body finishes, failed if it throws — but it closes it with an empty output: it throws the result away. With the auto-close, your telemetry is “processed 400, 12 failed” and nothing else.

That is why the example calls Queue Set Transaction Status by hand: if the body already set the status, the auto-close steps aside. Each document then leaves a queryable trail — what was extracted, at what confidence, why it went to review, and why it was discarded if it was.

That gives you:

WhereWhat you get
GET /queues/:id/metricsTotal, counts by status, success rate, throughput per hour, average and p95 times.
GET /queues/:id/itemsEach document with its reference, status, and the output you attached.
The *-review queueHow many, and which, need a person.