Which IDP System Handles Inspection Notices from Multiple Sources?
If you searched for which IDP system handles inspection notices from multiple sources, you are not asking whether a demo can read one clean PDF. Fire, health, and building notices already arrive as email attachments, portal downloads, desk scanner batches, fax-style images, and phone photos of paper taped to a door. Short answer: the system that handles that mix is not a single OCR product. It is an intelligent document processing pipeline that normalizes every source into the same intake object, classifies the notice family, extracts the few fields your operational system can store, validates them against your site list, and routes a trusted record into a CMMS, EHS tool, or compliance tracker, with the original file attached for audit. Everything the page cannot support goes to a person. OCRskill covers the extraction half with POST /ocr and POST /ocr.json?fields=.... It does not ingest your mailboxes, classify notice types on its own, or replace the system of record that owns deficiency status.
This post is about multi-source intake and routing into operational systems. If your question is which OCR tool can process a scan of a fire, health, or building notice into Markdown or typed fields, use OCR for scanned inspection notice documents. If the paperwork is plant incoming inspection (CoAs, IQC check sheets, material certs), that is a different document class, covered in OCR incoming inspection.
What an IDP system does with inspection notices
Intelligent document processing is broader than character recognition. AWS describes IDP as automating data entry from paper or document images into digital form, then classifying, extracting, validating, and routing that data into other business systems. For inspection notices, that sequence is the purchase. Teams evaluating an IDP system, or a document processing API that will sit inside one, usually need five properties at once:
- Source-agnostic capture. Email PDFs, portal exports, MFP scans, fax images, and phone photos land in one staging area with the channel recorded.
- Classification. Fire, health, building, equipment, and internal EHS walkthroughs take different field lists and destinations.
- Extraction. Searchable page text for the archive, plus a short typed record for the operational system.
- Validation. Required keys, date sanity, and a match against the sites you actually operate.
- Routing. A trusted record creates a finding in the CMMS or EHS tool. An untrusted page opens a review ticket with the original attached.
Plain OCR answers only item 3. A CMMS answers item 5 after someone types the record. The IDP question is about the path between those two products.
Why multiple sources break a single-folder OCR project
A facilities or multi-site retail team rarely receives notices through one clean scan station. A typical week mixes channels that look similar only after you ignore their metadata:
| Source | What usually arrives | What you should keep besides the file |
|---|---|---|
| Agency or inspector email | PDF attachment, sometimes a forwarded thread with extra pages | Sender, received time, mailbox or shared inbox, message id |
| Permit or inspection portal | Downloaded PDF or a print-to-PDF of a results page | Portal name, retrieval time, any confirmation number shown in the UI |
| Facilities desk scanner | Multipage PDF or TIFF from a fixed profile | Operator, site hint from the scanner folder, content hash |
| Fax-to-email or analog fax | Low-contrast image wrapped as PDF | Sending number if present, received time, higher review expectation |
| Manager phone photo | JPEG or PNG of a posted notice, often skewed or washed out by glare | Photographer, site, capture time, higher review expectation |
If each channel writes into its own unofficial folder (“Monica’s email,” “the fax inbox,” “photos from store 14”), you do not have an IDP system. You have five intake accidents, and the CMMS never sees notices that arrived through the quiet channel. The system that handles multiple sources treats channel as an attribute on one object. Downstream steps should not care whether the bytes came from a mailbox or a glass platen, except when the channel itself is a quality signal. Fax images and phone photos deserve a higher review rate. Portal PDFs and desk scans usually do not.
Normalize every source into one intake object
Do this before anyone argues about models. Give every inbound notice the same shape:
bytes(the original file, never overwritten)sha256,mime_type,byte_sizesource_channel(email,portal,scan,fax,phone_photo)received_at(when you received it, which is not always the issue date printed on the page)source_hint(sender address, portal name, scanner folder, or store id baked into the upload path)
From there, the extract call looks the same for every channel. Both OCRskill endpoints accept images (PNG, JPEG, WebP, GIF, BMP, TIFF) and documents including PDF plus common Office and OpenDocument types, up to 20 MB per request. Inspection intake mostly produces JPEG or PNG photos and PDF packets; TIFF still shows up from older fax and MFP profiles. Auth is Authorization: Bearer on both routes. A free key from get-key.json is enough to test uploads. If the file already lives at a public HTTP or HTTPS URL, a paid key can send document_url instead of uploading bytes again; a free key returns 403 for that mode (OCR document URLs).
Keep received_at from the channel, not from OCR. The date printed on a notice is evidence on the page. The date the email landed is evidence about your intake. Mixing them makes overdue logic lie.
Classify the notice before you extract
Classification decides two things: which extract contract to use, and which operational system should see the result. A practical starter set:
| Notice family | Typical contents | Usual destination after review |
|---|---|---|
| Fire / life safety | Deficiency letters, sprinkler or alarm findings, posted fire marshal notices | CMMS or life-safety tracker |
| Health / food safety | Health department findings, follow-up letters, score sheets | EHS or food-safety program |
| Building / equipment | Building, elevator, boiler, or other certificate pages | CMMS or asset file |
| Internal EHS | Walkthrough sheets and corrective-action support pages | EHS, with a higher handwriting review rate |
A dedicated health-department inbox or a scanner folder named fire/ is a strong prior. When the source is a shared mailbox or a phone photo with no folder hint, classify from the page text after Markdown OCR, and send ambiguous pages to a person.
OCRskill does not expose a separate classify endpoint. Use folder and mailbox rules first, then text hints from /ocr, then human triage for packets that match more than one family or none at all. Discovery mode on /ocr.json (omit fields) can show which catalog keys are present while you learn a new agency layout. Freeze a field list before production traffic so the JSON shape stays stable. Schema details live in the form data extraction API guide and the OCR JSON API reference.
Which API can capture inspection notice fields for operational systems
Operational systems do not want every character on the page. They want a short record they can attach to a site: the inspected organization, a clear issue date when the page has one, printed address parts, a signature date when a signed line exists, and the full page text so a clerk can read findings, due dates, and inspector notes that are not catalog keys.
The documented OCRskill catalog can cover that header slice. company_name maps to the inspected organization when that name is printed clearly. invoice_date is useful when the notice carries a clear issue date the model can treat as that field. Address keys (street_name, street_number, town_or_city, region, country) fit a printed site address. signature_date fits a dated signature line. Mark anything the page often omits as optional with a trailing ?.
Be honest about what the catalog does not have. There is no deficiency_code, violation_id, inspector_name, severity, corrective_action_due_date, or finding_status field today, and unknown names return 400. Pull findings, due dates, and inspector lines from Markdown, or enter them in review. Do not invent catalog keys to satisfy a CMMS schema.
A practical header call:
curl https://api.ocrskill.com/get-key.json
export API_KEY="sk-your-key-here"
curl "https://api.ocrskill.com/ocr.json?fields=company_name,invoice_date?,street_name?,street_number?,town_or_city?,region?,country?,signature_date?" \
-H "Authorization: Bearer $API_KEY" \
-F "file=@health-notice.pdf"
Example response shape (values are fictional):
{
"company_name": "Example Foods Market",
"invoice_date": "2026-11-03",
"street_name": "Harbor Avenue",
"street_number": "1400",
"town_or_city": "Example City",
"region": "CA",
"country": "United States",
"signature_date": "2026-11-03"
}
Run Markdown on the same file so findings stay readable next to the JSON:
curl https://api.ocrskill.com/ocr \
-H "Authorization: Bearer $API_KEY" \
-F "file=@health-notice.pdf" > health-notice.md
If a required company_name is missing, /ocr.json returns 400 and includes the extracted input_text. That is the correct failure mode: better to open a review item than to create a finding under the wrong site. When the only date on the page is a handwritten follow-up stamp, keep it in Markdown and do not force invoice_date.
A practical path from five inboxes to one operational record
Keep the extract API small and the process boring:
- Capture. Accept the five channels into one staging bucket or consume folder. Virus-scan attachments. Do not overwrite the original.
- Normalize. Store hash, mime type, byte size,
source_channel,received_at, and any folder or mailbox hint. Reject files over 20 MB or split them before upload. - Classify. Apply source priors, then Markdown hints, then human triage. Choose a field list (or Markdown-only) from the notice family.
- Extract. Call
/ocrfor every page you will archive or show a reviewer. Call/ocr.json?fields=...when the header schema is stable. - Validate. Required keys, date sanity, and a lookup of
company_name(and address parts, when present) against your site directory. - Route or review. Trusted records create a finding draft in the CMMS or EHS tool and attach the original plus the Markdown. Everything else goes to a queue with the file, the raw JSON, the Markdown, the channel, and the reason.
- Close findings in the operational system. Extraction never flips a deficiency to closed.
A first automation worth shipping is not “auto-create every work order.” It is “do we know the site and the notice family before a clerk opens the page?” The sketch below reads a typed header, the Markdown, and the source channel, then returns a destination:
import json
import re
import sys
from pathlib import Path
NOTICE_HINTS = {
"fire": ("fire marshal", "life safety", "sprinkler deficiency", "fire prevention"),
"health": ("health department", "food code", "critical violation", "sanitarian"),
"building": ("building department", "certificate of occupancy", "elevator inspection"),
"internal_ehs": ("safety walkthrough", "internal inspection", "corrective action"),
}
SITE_DIR = {
"example foods market": "site-14",
"example east warehouse": "site-22",
}
CHANNEL_REVIEW = {"phone_photo", "fax"}
def classify_notice(markdown: str) -> str | None:
text = markdown.lower()
hits = [
kind
for kind, needles in NOTICE_HINTS.items()
if any(needle in text for needle in needles)
]
return hits[0] if len(hits) == 1 else None
def site_id(record: dict) -> str | None:
name = record.get("company_name") or ""
name = re.sub(r"[^a-z0-9]+", " ", name.lower()).strip()
return SITE_DIR.get(name)
def route(channel: str, record: dict, markdown: str) -> dict:
kind = classify_notice(markdown)
site = site_id(record)
issues = []
if channel in CHANNEL_REVIEW:
issues.append("source_needs_review")
if not kind:
issues.append("unclassified_or_ambiguous")
if not site:
issues.append("site_not_in_directory")
if not record.get("company_name"):
issues.append("missing_company_name")
if not markdown.strip():
issues.append("empty_markdown")
destination = "review" if issues else ("ehs" if kind == "health" else "cmms")
return {
"destination": destination,
"notice_type": kind,
"site_id": site,
"channel": channel,
"issues": issues,
}
if __name__ == "__main__":
channel = sys.argv[1]
record = json.loads(Path(sys.argv[2]).read_text())
markdown = Path(sys.argv[3]).read_text()
print(json.dumps(route(channel, record, markdown), indent=2))
Save it as route_notice.py. A portal PDF whose Markdown mentions a health department and whose JSON names a known site can route to EHS. The same header on a phone photo still goes to review, because that channel is in CHANNEL_REVIEW. An unclassified certificate, or a company_name missing from SITE_DIR, also goes to review. Tune the hint lists from your own notices; they are a starting point, not a measured model.
When you write to the operational system, keep the payload small: a finding draft against the matched site_id, notice type from classification, the original file and Markdown attached, and only validated header fields copied into columns the destination already has. Leave due dates, severity, and open or closed status to a person unless you trust a parser for that agency layout. Never write unverified OCR into a closed-deficiency status.
Limitations to plan for
- OCRskill is the extract API, not the full IDP suite. It does not watch an inbox, log into an agency portal, run a fax server, or create work orders. Your capture and routing code has to do that.
- Channel quality varies. Portal PDFs and desk scans are the strong case. Fax compression and phone photos of posted paper still fail on glare, skew, and small print. Plan a higher review rate instead of burning retries.
- Field catalog boundaries. Only documented field names are valid in
fields. Findings tables, violation codes, and due dates stay in Markdown or in review. - Classification and multipage packets are your responsibility. Mailbox names and keyword hints help; a cover email plus two notice types needs a person. If a cover letter, findings table, and certificate arrive as one PDF, decide whether one request covers the file or you split pages and merge evidence in your code.
- Not a CMMS or EHS system. No work-order workflow, no deficiency aging, and no decision about whether a finding is still open or overdue.
Do not publish a fake accuracy percentage. Measure on your own holdout set, by source channel: empty-page rate, wrong site matches, unclassified notices, and how often a clerk still edits the record before anyone trusts it.
Choosing an IDP system (or assembling one) for this job
A document processing API is only useful here if it can sit inside a multi-source path. Ask production questions:
| Question | Why it matters for multi-source notices |
|---|---|
| Will every channel I already use produce the same intake object? | Five inboxes are the problem you hired IDP to solve |
Can I keep source_channel and received_at outside the model? |
Channel metadata should not be OCR’d back off the page |
| Do I get Markdown and typed fields from one vendor? | Archive text and CMMS columns are different jobs |
Is the field catalog documented, with 400 on unknown or missing required keys? |
Invented notice schemas drift the first time an agency changes a header |
| Does routing stay in my code or in the CMMS? | Extraction should not close findings |
OCRskill’s answer for the extract slice is https://api.ocrskill.com/ocr for Markdown and https://api.ocrskill.com/ocr.json for typed headers, Bearer auth, name vs name? syntax, discovery when you omit fields, a 20 MB upload limit, and paid-only document_url fetch. Grab a key from get-key.json, lock a small header schema for the notice families you see weekly, keep findings on Markdown plus review, and let your routing layer talk to the CMMS or EHS tool you already run.
If a packaged IDP platform already watches your mailboxes and writes to your CMMS, you can still use a dedicated extract API for the notice families that platform handles poorly, as long as you keep one review queue and one site directory. The useful test is operational: after a week of real email, portal, scan, fax, and photo intake, can a clerk find every notice for site 14 and see which ones still need a person?
Conclusion
Start with one notice family and two channels you already have, often agency email plus the facilities scanner. Make those sources produce the same intake object, classify before you extract, and store only a short header (company_name, an issue date when the page supports it, address parts) in the operational system. Leave findings in Markdown until a clerk or a parser you trust promotes them. Measure wrong-site and unclassified rates separately for each channel, then promote a family to automatic routing only after clerks have stopped rewriting the header.
The win is not a prettier reading of one scan. It is a notice that reaches the right operational system before the follow-up inspection, without pretending the extract API is the system of record.
