Architecture of an AI System for Processing Email Orders

14.10.2026
Architecture of an AI System for Processing Email Orders
Technical architecture · October 2026

From heterogeneous documents to validated ERP orders

Automating sales orders involves more than recognizing text. The system must understand the customer’s request, distinguish an order from other correspondence, identify business-party roles and products, and convert quantities into the units used by the enterprise system. Only then can it prepare an ERP-ready output. The language model acts as an interpretation component within a controlled workflow; application and database logic verify business identifiers and determine export eligibility.

The solution processes hundreds of orders per day. According to operational figures provided by the project owner, it delivers savings of approximately 2.5 FTE—the equivalent of two and a half full-time positions. Approximately 50% of orders undergo manual review. Inputs include PDF, XML, DOCX, Excel, and scans of handwritten orders, and frequently contain both formatting and factual errors. Products are checked against an internal database containing a few tens of thousands of items.

Operational characteristic Value and meaning
Daily volume Hundreds of orders processed
Staff capacity freed up Approximately 2.5 FTE
Manual review Approximately 50% of orders
Product catalog A few tens of thousands of items
Input documents Email, PDF, XML, DOCX, XLS/XLSX, and scans, including handwriting
Common complications Missing data, code–description mismatches, ambiguous units, incorrect addresses, and poor-quality source documents

The manual-review rate must not be confused with the AI error rate. Review can also be triggered by an error in the original order, an ambiguous business request, or a rule requiring human intervention. Even in reviewed cases, operators do not necessarily have to re-enter everything: they receive extracted data and validation results. These operational figures were supplied by the project owner, rather than established through an independent benchmark. Without measurements of individual activities, they do not establish a percentage improvement in processing speed or a financial return on investment.

1. System decomposition and technology stack

The system runs in Microsoft Azure within a VPN-connected environment, providing private network connectivity to the organization’s internal systems and enterprise data sources. The implementation consists of containerized services deployed through Docker Compose. The backend comprises Python services; the administrative HTTP API uses FastAPI and Uvicorn. The frontend is a React application built with Vite and served by Nginx. PostgreSQL stores processing state and results, while a remote Microsoft SQL Server provides reference business data. Microsoft Graph handles access to Exchange Online, and OpenAI models perform classification and extraction.

Backend services communicate through the Model Context Protocol (MCP), using streamable-http in the container environment. MCP standardizes tool contracts, such as retrieving a message or validating an order. An explicit workflow controls the sequence of operations. Using MCP therefore does not mean that the model can freely invoke every service or access the database directly.

Architecture: email ingestion, AI extraction, catalog validation, review, and exportGraphMCPMCPArchived sourcesModel APIDraft orderQueriesYesNoRESTApprove current revisionPersistRead/writeXML / ACKExchange OnlineMail readerConversion and OCRSource archivingSchedulerWorkflow orchestratorClassificationand extractionOpenAIPostgreSQLCatalog validationInternal MSSQLcatalogValidation findings?Manual reviewReact UIValidated orderAdmin APIExport outboxBusiness Centraland import integration
Figure 1. System architecture and integration boundaries

The diagram combines data flows and control relationships. The orchestrator coordinates handoffs between processing stages; the arrows do not represent a separate message broker. A path with no validation findings does not, by itself, bypass approval of the export revision.

Service Responsibility and main interfaces
exchange-mail-reader Message selection and detail retrieval through Graph; read_mails and read_single_mail tools
pdf-converter Input normalization, text extraction, OCR, and spatial metadata; convert_2_pdf, pdf_2_text, and convert_and_extract
mail-archiver Persistence of sources and results, processing records, claims, and shared database operations
workflow-orchestrator Coordination of classification, extraction, and validation; primary entry point process_order_email
scheduler Periodic retrieval of new messages and initiation of processing
order-extractor OpenAI Agents SDK, classification, structured extraction, and draft-result creation
catalog-validator Validation of customers, products, customer-specific relationships, and units of measure against MSSQL
admin-api REST interface for review, corrections, approval, and reprocessing
export-outbox Delivery of approved XML revisions, export tracking, and receipt acknowledgment
frontend React UI, order lists and editing; Nginx routes requests to the APIs

This decomposition allows document conversion, model configuration, and catalog queries to evolve separately. Nevertheless, it remains an interconnected application with a shared operational database and libraries, rather than a collection of fully independent microservices. At hundreds of orders per day, concurrency control, traceability, and failure recovery matter more than maximizing the number of parallel workers.

2. Message ingestion and resumable processing

The reader requests Graph identifiers with Prefer: IdType="ImmutableId". The scheduler retrieves a time window that overlaps the preceding cycle. This overlap helps capture messages at interval boundaries; the archive’s unique key (workflow, unique_email_id) handles repeated retrieval of the same email. Deduplicating a technical message is not the same as recognizing an identical business order submitted in two different emails.

A batch of orders is claimed using a database lease, a time-limited right to process a record. A worker claims the record atomically, renews its lease during longer runs, and saves the result on completion. If the process crashes, lease expiry allows another run to take over. The database model also records the next retry time, attempt count, and technical-error classification.

The public entry point process_order_email(unique_mail_id, force_reprocess=false) centralizes workflow rules. If an order already exists and a new run has not been requested, it returns the existing result without another AI call. Forced reprocessing first revokes the previous unacknowledged export. The acknowledgment history is preserved so that a new run cannot erase evidence of an integration that has already taken place.

3. Document normalization without losing source meaning

The conversion service creates a common PDF representation of the inputs and then extracts text page by page. It uses headless LibreOffice, PyMuPDF, and Tesseract. Depending on input quality, processing can use the native text layer, OCR, or model-based reading of a page image; the extractor also supports passing archived PDFs directly to the model. XML is one of the accepted business inputs, but arbitrary customer XML cannot be assumed to conform to the target integration schema.

Document geometry is an important part of this representation. Each page carries its width, height, and a list of words with bounding boxes, bbox = [x0, y0, x1, y1]. Coordinates are normalized to PDF points—1/72 of an inch—with the origin at the top left. OCR positions are converted from pixels using the applicable DPI. Each word is marked as originating from native text or ocr.

This makes it possible to distinguish adjacent “Supplier” and “Buyer” blocks even when plain-text extraction interleaves them. Conversely, text obtained from a vision model without coordinates is not assigned fabricated geometry. Missing evidence cannot be replaced with coordinates that the source does not provide.

In handwriting, a product name may be legible while a digit in the quantity is uncertain. In a table, correctly recognized text may be assigned to the wrong column. The original document, original unit, and link to the relevant attachment or page must therefore be retained. Better OCR alone does not guarantee a factually correct order.

4. Separate contracts for classification and extraction

Classification determines not only whether a message is business correspondence, but also its current ordering intent and action type. A new order, amendment, addition, and cancellation have different consequences. The implementation therefore sends amendments and cancellations for manual assessment even when their line items can be successfully validated against the catalog.

Classification and extraction models are configured separately. Basic triage need not use the same model as interpretation of a complex PDF. Prompts are external files mounted read-only into the containers. Their versions can therefore be managed independently of the service image, and the instructions behind a result can be recorded.

Extraction returns structured data whose syntax is checked by the backend. Deterministic code constructs the final XML. Model-based document interpretation and the decision to export are consequently separate operations. In the candidate-based resolver variant, the output also includes schema and prompt versions, a prompt hash, and candidate business parties with their original attributes and source evidence.

The model cannot authoritatively invent internal customer numbers. These fields are absent from the candidate schema; in the compatibility path, model-generated customer identifiers are removed and populated only after database verification. Generated XML is likewise not evidence for a new identity-resolution decision. When processing resumes, evidence is reconstructed from the archived source documents.

5. Resolving business parties and the supplier

The current extraction contract distinguishes customer roles BY (buyer), IV (invoice party), and DP (delivery party). The supplier, SU, is confirmed separately. Older documents may contain other roles, such as OB; their presence in historical XML does not mean that the current resolver generates them again.

Catalog-based party resolution combines strong identifiers, contact email, and name-and-address information. A conflicting company registration number or tax identifier cannot be overridden by a highly similar name. Delivery addresses are assessed independently: an address may identify a construction site that is not a registered customer. Failure to find an internal delivery-location number therefore does not automatically invalidate an otherwise complete address.

For fuzzy comparison of customer addresses, the implementation normalizes Unicode, case, punctuation, whitespace, and legal forms. Diacritics are removed for comparison, without overwriting the original data. The composite score is:

score = 0.45 × name + 0.25 × street + 0.15 × city + 0.15 × postal_code

Name matching combines trigram similarity, Token Set Ratio, and Jaro–Winkler; street matching also uses normalized Damerau–Levenshtein similarity. Automatic fuzzy resolution requires a score of at least 0.90, a margin of at least 0.08 over the runner-up, exact normalized postal-code and house-number matches, city similarity of at least 0.90, and a complete address. These are specific rules of the customer resolver, not universal product-search thresholds. Candidate queries are parameterized and bounded; fuzzy comparison does not load the entire customer directory.

The supplier is located within a spatially bounded block under the appropriate heading. Its name appearing in a note or signature is insufficient. If deterministic processing lacks adequate evidence, a separate model-based recognition step can be used. The model selects references to words or exact text spans in the original source. The backend verifies that those references exist, checks block boundaries, and detects conflicts. A verified reference establishes a source location; it does not automatically confirm the supplier’s identity.

The resolver supports legacy, shadow, and v2 modes. In shadow mode, new rules are evaluated and audited alongside the existing path, but they do not change the authoritative order or enable export. This allows sensitive decision logic to be introduced without immediately switching the entire production workflow. The repository alone does not establish which mode is active in a particular deployment.

6. Product validation and unit conversion

The catalog service works with internal MSSQL views for customers, contacts, products, customer-specific relationships, and units of measure. It accepts structured values through MCP rather than arbitrary model-generated SQL. The database containing a few tens of thousands of products remains the authoritative source for product identity and attributes.

Validation must distinguish product existence, its relationship to a particular customer, permitted units, and minimum sales quantities. Dimensions or materials in a description may reveal a conflict with the stated product code. A product that the customer probably intended must not be treated as a verified product without sufficient evidence.

Quantity conversion uses the catalog factor for the specific product. Language aliases such as pcs, Stk., or Stück only help locate the corresponding unit. The label box may indicate a package, but it does not authorize the system to guess how many items it contains. The original customer unit is retained in fields such as ordered_unit or customer_unit.

Consider an order for “3 packs.” If that product’s catalog entry specifies ten pieces per pack, the normalized quantity is thirty pieces. Without that catalog entry, the conversion remains unresolved. Similarly, “25 kg” in a product name must not overwrite the quantity in a separate column.

Verified units and packaging are stored in PostgreSQL as a snapshot of the catalog data for the corresponding order line. The order_item_catalog_snapshots table contains the line identity, its catalog-assignment version, and a JSONB payload. A completed snapshot is not invalidated merely by age. Changing the assigned product increments the version and makes the previous payload unavailable for the current assignment. Reopening an order therefore need not place another load on the remote MSSQL server.

7. Persistence and protection against concurrent changes

PostgreSQL separates the working order state from immutable revisions and export state. This distinction matters because an operator may edit an order while remote validation is still running.

Data structure Purpose
mail_archive_runs Archiving and processing records, classification, lease, and retry metadata
mail_orders Current working order, status, model metadata, and JSONB result
order_processing_events Append-only events for individual runs, identified by run_id
party_resolution_runs Audit trail of resolver decisions, including shadow-mode identification
order_item_catalog_snapshots Verified units and packaging tied to a particular line assignment
order_revisions Order versions subject to controlled approval and export
order_export_outbox Delivery state of a particular export revision

Validation uses an optimistic order_version check. An MSSQL result can be saved only if it still matches the version read before validation began. A late response therefore cannot overwrite a newer operator correction. The processing lease protects work ownership; version checking protects content. These mechanisms address different problems and do not replace one another.

8. Manual review and safe export

Approximately half of all orders require human assessment. Reasons include illegibility, incomplete addresses, conflicting identifiers, ambiguous packaging, and factually incorrect customer requests. The workflow routes warnings to needs_manual_review; successful extraction alone does not constitute business approval.

Workflow: validation, manual review, approval, outbox, and acknowledgmentFindingsNo findingsCorrectionResolve and validateWorking orderValidationManual reviewValidated versionApprovedcurrent revisionOutbox: readyExport claimed: leasedReceipt confirmedacknowledgedFailure reportedfailed
Figure 2. Validation, approval, and export lifecycle

This is a simplified process view. The application tracks order state, revision state, and export state separately; they cannot be collapsed into a single “done” flag.

The export service exposes POST /api/exports/read_data, which selects only orders in the approved state with a current, valid, approved revision. The saved state is not sufficient for delivery. The response includes export_id, revision_id, the source-message identifier, XML, and xml_sha256. The hash is calculated from the exact UTF-8 representation delivered, identifying the specific transferred content.

The downstream integration retrieves XML for Business Central. The endpoint POST /api/exports/{export_id}/ack acknowledges receipt idempotently: repeated acknowledgments produce the same result. Failures are reported through the corresponding failed endpoint with a machine-readable error code. The implementation marks the order as exported when read_data successfully delivers it; this state is therefore not equivalent to a confirmed ERP import. ACK history is not deleted during reprocessing.

An outbox and idempotent ACK alone do not guarantee exactly-once creation of a business order in the target ERP. The import side must recognize a previously received export by its stable identity when transmission is repeated. This is particularly important after a lost response or an administrative action that makes an order available again.

9. Operational resilience and cost control

The longest-running operation is often model-based document processing or waiting for a remote database. The MSSQL validator runs a synchronous database driver in worker threads to avoid blocking asynchronous MCP request handling. Both concurrency and admission waiting are bounded. The administrative API also configures MSSQL connection counts, waiting-request limits, maximum connection age and idle time, and validation after longer idle intervals.

Compose configuration defaults include a limit of four concurrent catalog operations, a five-second admission-wait limit, a fifteen-second MSSQL query timeout, and a three-hundred-second AI-step timeout. These are repository defaults that environment settings can override—not measured latencies or declared SLAs. Overall order and batch timeouts must account for internal limits and retry budgets.

Technical unavailability requires a different response from an incorrect order. Transient failures can be retried with bounded backoff; conflicting company identifiers or an unknown unit are generally not resolved by another identical model call. Such cases require a person. Preserving the draft result reduces repeated extraction charges when a later stage fails.

Costs are controlled through separate model selection, limits on the number and size of submitted PDFs, conditional model fallback, and token accounting. A model-operation audit can record the model, prompt version and hash, duration, outcome, and available usage information. Missing usage data after a timeout does not mean the operation incurred no cost.

10. Security boundaries and engineering lessons

User authentication and authorization are handled through the organization’s Microsoft Entra ID directory, formerly Azure Active Directory. The frontend uses MSAL integration to sign users in and obtain access tokens; the backend verifies the token and required application role. Application access is therefore tied to corporate identity and assigned permissions. Running in Azure within a VPN-connected environment provides network-layer protection, while Entra ID controls individual user access; network reachability through the VPN does not replace authorization. The export API uses separate authorization through the API_KEY header. MCP is a communication protocol, not an automatic security boundary; deployment configuration must protect internal ports, database access, and secrets.

Documents are untrusted data. Their text must not gain authority to change export rules, execute arbitrary SQL, or confirm its own business identity. Typed contracts, source-reference verification, and deterministic catalog checks make this boundary more explicit. Retention periods for attachments, personal data, and operational logs require separate management.

The value of this architecture lies in reducing repetitive human work while retaining control over ambiguous requests. Savings of approximately 2.5 FTE are compatible with manual review of roughly half of all orders: the system also prepares source material, mappings, and specific findings for those cases. The critical engineering property is traceability from the original document through catalog validation and an approved revision to an acknowledged export.


This technical description is based on the available implementation documentation, container-service definitions, frontend and API dependencies, and database migrations reviewed as of 14 October 2026. It describes implemented mechanisms rather than an audit of the live environment’s configuration. The project owner supplied the operational volume, manual-review rate, catalog size, capacity savings, and information on Azure VPN deployment and Microsoft Entra ID. The diagrams are embedded in this HTML document as vector graphics.

Share