The Gateway OCR Handler
The Gateway OCR Handler
The OCR extraction handler reads a delivered document — a scanned invoice, a packing slip, a certificate — against an extraction profile, and keeps the structured fields on the transfer.
Configuring it
On a route, choose OCR extraction and give the extraction profile. Optionally name a script to hand the results to. The route is refused at save time if the profile is missing, rather than failing when a file lands.
What happens
- The document is queued to the OCR pool — extraction is CPU-bound and runs on a pool sized for it, so a gateway worker blocking for a minute per invoice would starve every other handler behind it.
- The gateway waits for the result (90 seconds by default).
- The extracted fields are written onto the transfer, where they are visible in the console and outlive the queue's short-lived result. An operator asking next week what was read off an invoice still gets an answer.
- If a script is configured, it is started with both the document and the fields.
What the script receives
Everything a normal gateway script gets, plus:
context.ocr_data // the extracted fields, as an object
context.ocr_profile_id // which profile read it
context.ocr_request_id // the extraction, for the audit trail
context.ocr_source // which engine produced the result
The script gets the document and what was read off it, rather than having to read it again.
Walkthrough: an AP invoice
A supplier drops invoices on SFTP. You want the header fields read, a sanity check against the purchase order, and anything doubtful put in front of a person.
1. The extraction profile
Create one under Extraction Profiles — call it
AP invoice — with fields for invoice_number, invoice_date,
po_number, vendor_name, total. Use auto-discover on a sample
PDF and correct what it proposes.
2. The principal and folder
Create a principal for the supplier, give it a login, and tell
them to upload to /inbound/invoices/.
3. The route
Folder pattern: /inbound/invoices/**
Handler: OCR extraction
Extraction profile: AP invoice
Script name: postApInvoice
Applies to: (the supplier's principal)
4. The gating script
function main(context) {
const d = context.ocr_data || {};
if (!d.invoice_number || !d.total) {
return { success: false,
script_message: "could not read invoice number or total from "
+ context.file_name };
}
const po = d.po_number;
const open = po ? odbc.executeQuery("open_po_by_number", { po_number: po }) : [];
if (!po || open.length === 0) {
approvals.create({
title: "AP invoice needs a PO: " + d.invoice_number,
body: "Vendor " + (d.vendor_name || "unknown")
+ " invoiced " + d.total + " with no matching open PO.",
assigneeType: "role",
assigneeId: "AP Clerk",
data: d
});
return { success: true,
script_message: "raised approval for " + d.invoice_number };
}
const expected = Number(open[0].amount);
if (Math.abs(Number(d.total) - expected) > expected * 0.02) {
approvals.create({
title: "AP invoice over tolerance: " + d.invoice_number,
body: "Invoiced " + d.total + " against PO " + po + " for " + expected + ".",
assigneeType: "role", assigneeId: "AP Clerk", data: d
});
return { success: true,
script_message: "over tolerance — sent for approval" };
}
return { success: true,
script_message: "matched PO " + po + " for " + d.total };
}
5. What each outcome looks like
| Case | Transfer | What a person sees |
|---|---|---|
| Read cleanly, matches an open PO within tolerance | processed | Nothing. It worked. |
| Read cleanly, no PO or no match | processed | An approval task in Approvals. |
| Read cleanly, over tolerance | processed | An approval task naming both amounts. |
| Could not read the invoice number or total | failed | A red row in Transfers with the reason. |
| Extraction itself failed or timed out | failed | A red row saying the extraction failed. |
Tuning
- Wait time — the route accepts a wait in seconds (90 by default). Raise it for long multi-page documents.
- File types — PDF, PNG, TIFF and JPEG are recognised from the filename.
- Throughput — extraction is CPU-bound and scales by adding OCR capacity, not by sending more at once.