The Gateway OCR Handler

Updated Jul 26, 2026
Hosted File Gateway

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

  1. 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.
  2. The gateway waits for the result (90 seconds by default).
  3. 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.
  4. If a script is configured, it is started with both the document and the fields.
A failed extraction fails the transfer. If the extraction errors or does not finish in time, the transfer is marked failed with the reason. Reporting it green is how a document nobody read looks identical to one that was.

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

CaseTransferWhat a person sees
Read cleanly, matches an open PO within toleranceprocessedNothing. It worked.
Read cleanly, no PO or no matchprocessedAn approval task in Approvals.
Read cleanly, over toleranceprocessedAn approval task naming both amounts.
Could not read the invoice number or totalfailedA red row in Transfers with the reason.
Extraction itself failed or timed outfailedA red row saying the extraction failed.
Note the deliberate split. A document that was read but needs a human decision is a successful transfer with an approval attached — the file transfer worked. A document that could not be read at all is a failed transfer, because there is nothing to decide about. Keeping these apart is what lets "show me today's failures" mean something.

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.
Was this page helpful?