Appearance
The purchase-order model
Concepts
Two closely related shapes exist:
PurchaseOrder— what you read inOrderStatus.result(the stored extraction, with AIOTIC's additive fields).ErpPurchaseOrder— what your ERP receive endpoint gets (a fixed subset, with operator corrections applied).
Both follow the same rules: every key is always present, optional values are null (never omitted), dates are ISO YYYY-MM-DD, amounts are decimal numbers, quantities are whole numbers.
PurchaseOrder (in result)
json
{
"order_number": "EB2500011645",
"order_date": "2026-01-14",
"delivery_date": "2026-02-01",
"delivery_date_from": null,
"delivery_date_to": null,
"supplier": { "company": "Acme Supplies BV", "contact_person": null, "email": "orders@acme.example",
"address": { "street": "Industrieweg 5", "postal_code": "1234 AB", "city": "Amsterdam", "country": "NL" } },
"customer": { "customer_id": "58931", "company": "LUMITECH INSTALLATIES", "contact_person": "J. de Boer",
"email": "info@lumitech.example", "phone": "+31 55 123 4567", "branch": null,
"vat_id": "NL001234567B01", "iban": null, "bic": null,
"address": { "street": "Ambachtsweg 12", "postal_code": "7327 AA", "city": "Apeldoorn", "country": "NL" } },
"shipping_details": { "recipient": { "company": "LUMITECH INSTALLATIES", "contact_person": "J. de Boer", "department": null,
"email": null, "phone": null,
"address": { "street": "Ambachtsweg 12", "postal_code": "7327 AA", "city": "Apeldoorn", "country": "NL" } },
"special_instructions": null },
"items": [
{ "article_number": "PROD-001", "customer_item_number": "LT-ART-001", "description": "LED Driver 48V",
"quantity": 10, "quantity_state": "Valid", "unit": "ST", "price": 12.34, "currency": "EUR", "line_total": 123.40 }
],
"total_price": 123.40,
"currency": "EUR",
"additional_information": null
}Header
| Field | Type | Notes |
|---|---|---|
order_number | string | The customer's PO number. / is replaced by - (many ERPs cannot store it). |
order_date | string | ISO date where the document allowed it. |
delivery_date | string | null | The single date your ERP consumes. When the document states a window, AIOTIC collapses it to one end according to the tenant's preference (earliest by default). |
delivery_date_from / delivery_date_to | string | null | The window bounds, for audit. null for single-date orders. |
currency | string | null | ISO code as printed (EUR). |
total_price | number | null | The printed document total — may include VAT. When e-mail instructions changed the lines, this is the recalculated net sum and the original total is noted in additional_information. |
additional_information | string | null | Free text from document and e-mail, plus notes AIOTIC appends. |
Supplier
supplier is you. It is pinned from tenant configuration and identical on every order; it is not extracted from the document. Do not map it — it is there so the payload is self-describing.
Customer
| Field | Notes |
|---|---|
customer_id | Your customer (debtor) number, exactly as you synced it via PUT /customer/{number}. null when AIOTIC could not identify the sender with confidence — the order is then in ATTENTION. |
company, address, email, phone, vat_id | When a customer was identified with high confidence, these are canonicalised from your master record; otherwise they are as read from the document. |
branch | The issuing branch/location named on the document (chains with one debtor per branch). |
iban, bic | Rarely present on purchase orders; passed through when found. |
Shipping details
shipping_details.recipient is the ship-to block. When the document has no explicit one, AIOTIC fills it from the identified customer. special_instructions carries delivery remarks ("deliver before noon").
Line items
| Field | Notes |
|---|---|
article_number | Your SKU, after resolution through the catalog and customer item mappings (how). null = unresolved → ATTENTION. |
customer_item_number | The customer's own code as printed, when present. Useful to create a mapping afterwards. |
quantity | Whole units. null only when quantity_state is Unrecognised. Lines with a blank or zero quantity (assortment listings) are dropped before storage. |
quantity_state | Valid, Unrecognised (kept for review), or absent on older orders. |
unit | As printed (ST, PCS, Stk, m, KG…). Map it on your side (SDK sanitizer). |
price, line_total | Unit price and line amount as printed, when present. |
ErpPurchaseOrder (what your endpoint receives)
The hand-off payload is a fixed allow-list of the above:
- header:
order_number,order_date,delivery_date,currency,total_price,additional_information,supplier customer:customer_id,company,contact_person,email,phone,iban,bic,vat_id,address{street, postal_code, city, country}shipping_details.recipient:company,department,contact_person,email,phone,address{…}andspecial_instructionsitems[]:article_number,description,quantity,unit,price,currency,line_total
Not included: quantity_state, customer_item_number, branch, delivery_date_from/to. Operator edits from the AIOTIC app are merged in before sending. The full JSON Schema is in the API reference.
Working with it in Python
python
from aiotic.models import ErpReceiveRequest, PurchaseOrder
req = ErpReceiveRequest.model_validate_json(body) # in your receive endpoint
for line in req.purchase_order.items:
...
status = client.orders.get(request_id) # when polling
po: PurchaseOrder | None = status.result
if po and po.unresolved_items:
...