Odoo and EDI
Odoo EDI integration
What Odoo calls EDI is electronic invoicing: UBL, CII and Peppol, with Odoo as its own Peppol access point. It has no X12 or EDIFACT translator, no 856 and no SSCC generator. We build the X12 side to your Odoo database: 850s into sales orders, and 855, 856 and 810 out, mapped per trading partner, hosted and run for you.
What Odoo does natively
Odoo uses the word EDI for electronic invoicing. Its documentation page "Electronic invoicing (EDI)" covers the formats per country and the Peppol network, where Odoo acts as access point and SMP itself; Peppol registration is free and available in Community. The modules in Odoo 19 follow that line: account_edi, account_edi_ubl_cii (E-FFF, UBL BIS 3, EHF3, NLCIUS, Factur-X, XRechnung), account_peppol, around forty country modules, and two for orders: sale_edi_ubl imports purchase orders in UBL BIS 3, purchase_edi_ubl_bis3 exports and imports them on the purchase side.
A search of the Odoo 19 documentation index on 3 October 2026 returns no page for X12, EDIFACT or SSCC. The list of countries eligible for Peppol registration in Odoo has 42 entries, all European; the United States is not on it. For a supplier to Walmart, Target or Home Depot the native part is therefore the records an integration writes to and reads from:
| Native part | Odoo model or field | What an EDI integration uses it for |
|---|---|---|
| Sales orders | sale.order, client_order_ref |
The 850, and the PO number every later document returns |
| Delivery contacts | res.partner of type delivery, ref |
One contact per retailer DC or store |
| Barcodes per product and per unit | product.product.barcode, product.uom.barcode |
Resolving UPC or GTIN-14 in PO1 |
| Deliveries and packages | stock.picking, stock.package |
Source of the 856 |
| Invoices | account.move from the order |
Source of the 810 |
| GS1 barcode rules | barcodes_gs1_nomenclature |
Reads an SSCC scan with AI (00); does not create one |
This page is the Odoo side of our EDI integration. How Odoo exposes data in general is on the Odoo system page.
What sits between Odoo and your trading partner
| Document | Direction | Odoo record | Source of the content |
|---|---|---|---|
| 850 | In | sale.order |
The partner's file |
| 855 | Out | sale.order |
Quantity per line after reservation |
| 856 | Out | stock.picking with its packages |
Lines, packages, carrier, carrier_tracking_ref |
| 810 | Out | account.move |
Invoiced lines, tax, freight |
| 846 | Out | product.product |
free_qty per warehouse |
| 997 | Both | None | Kept in the integration per document |
The mapping of the 850 uses PO 4500018236 from our document pages: 24 units of item 10482 and 16 of item 10499 for Harbor Retail DC 07.
The order is written over the JSON-2 API in two calls, create and then action_confirm, after the lookups for customer, delivery contact and products:
POST /json/2/sale.order/create HTTP/1.1
Authorization: bearer 7c1d...a940
Content-Type: application/json; charset=utf-8
{"vals_list": [{"partner_id": 4711, "partner_shipping_id": 4718,
"client_order_ref": "4500018236", "commitment_date": "2026-10-06 04:00:00",
"order_line": [[0, 0, {"product_id": 881, "product_uom_qty": 24, "price_unit": 12.50}],
[0, 0, {"product_id": 894, "product_uom_qty": 16, "price_unit": 8.75}]]}]}
commitment_date is a datetime that Odoo stores in UTC. Midnight on 6 October in Columbus, Ohio, during daylight saving time is 04:00 UTC; sent as 00:00:00 it lands on the evening of the 5th in the warehouse's view.
The technical reality of this pair
The Custom plan. Odoo states that data access through the external API is only available on Custom plans. Without it there is no route for an outside integration.
Keys that expire. Since Odoo 19 an API key for a normal internal user expires after at most 90 days. The integration renews its own key with res.users.apikeys/generate before the expiry date; a key that lapses stops every 850 at once.
Two transactions per order. Every JSON-2 call runs in its own database transaction, and calls cannot be chained. When create succeeds and action_confirm fails, a quotation with the PO number stays behind. Odoo does mark a draft order whose customer and client_order_ref match another order in duplicated_order_ids, but it does not block it, and the check covers only drafts. The integration looks up client_order_ref for that customer before every retry and confirms the existing quotation instead of creating a second one.
A morning file. One 850 takes about four calls: customer and delivery contact, products, create, action_confirm. A retailer that sends 400 purchase orders at 06:00 causes 1,600 calls. Odoo documents no request limit per minute, so the pace depends on the hosting and the installed apps. The integration measures it on your database, queues the orders and returns the 997 when the file has been read, not when the last order is confirmed.
What Odoo and X12 store differently
| X12 needs | In Odoo 19 | What the integration does |
|---|---|---|
| An SSCC per carton, 18 digits with a check digit | Package names from a sequence, PACK0000001 by default; valid_sscc only checks a name |
Generates the SSCC and writes it as the package name |
| Pallet, carton and item levels in the 856 | parent_package_id nests packages since Odoo 19 |
Builds the HL hierarchy from the package tree |
| The retailer's item number in PO1 | Barcode on the variant or per unit; no customer product codes | A table of the retailer's numbers per variant |
| Cases of 12 | Units relative to a reference unit (relative_factor); packagings removed in Odoo 19 |
A case unit with its own GTIN-14 in product.uom |
| The invoice for what shipped | invoice_policy defaults to ordered quantities for goods |
Sets delivered quantities for EDI items, checks the 810 against the 856 |
The SSCC. A package type can have its own sequence, but a sequence counts up with a prefix and padding; it does not compute the GS1 check digit. An SSCC built from the GS1 company prefix needs that digit, and Amazon, for example, rejects a number reused within 365 days. The SSCC label page explains the structure.
The invoice policy. With the default policy an invoice bills the ordered quantities. For PO 4500018236 that is 24 × 12.50 plus 16 × 8.75, 440.00, against 405.00 for the 24 and 12 units that shipped. With the 2% discount and 18.50 freight the 810 would say 449.70 instead of 415.40, and a retailer that matches the invoice against the ship notice rejects it or deducts the difference.
Units and barcodes. A retailer that orders 2 CA means 24 units. In Odoo 19 a case is a unit with relative_factor 12 against Units, and its GTIN-14 sits in product.uom, where Odoo enforces that one barcode belongs to one product and unit. The units of measure note covers the general case.
When it goes wrong
| What happens | What the integration does |
|---|---|
| The API key nears its expiry date | Renews it and revokes the old key |
action_confirm fails after create |
Retries on the existing quotation, found by client_order_ref |
| A UPC matches no product, or a DC code no delivery contact | Holds the order with the line or address; the rest of the file is written |
| An 856 carries a package name that is not a valid SSCC | Holds the 856 before it is sent |
| No 997 from the partner within its window | An alert with the control number that is waiting |
We build the integration to your Odoo database and your trading partners, host it and run it, for a fixed build price and a monthly fee. A new partner starts as a dry run that writes nothing to Odoo and sends nothing, and you approve the first documents. When a partner publishes a new guide or Odoo renames a field in an upgrade, we change the integration. The same work on other ERPs is under NetSuite EDI integration and Acumatica EDI integration. Tell us your Odoo version and partners.
Frequently asked questions
Does Odoo have built-in EDI?
Not for retail X12 or EDIFACT. Odoo's EDI documentation covers electronic invoicing: UBL BIS 3, Factur-X, XRechnung, NLCIUS and the country formats, sent through Peppol with Odoo as access point. The module sale_edi_ubl imports purchase orders in UBL BIS 3. A search of the Odoo 19 documentation returns nothing for X12, EDIFACT or SSCC.
Which Odoo plan does an EDI integration need?
The Custom plan. Odoo's documentation says access through the external API is only available on Custom plans, not on One App Free or Standard. An EDI integration reads and writes sales orders, deliveries and invoices from outside, which is external API use.
Can Odoo receive EDI orders through Peppol?
Odoo's Peppol support is for invoices and credit notes out and vendor bills in, in BIS Billing 3.0, XRechnung and NLCIUS. Orders in UBL can be imported by uploading the file in the sales order list. A US retailer sends X12 over AS2, SFTP or a VAN, none of which Odoo reads.
Does it work on Odoo Online?
Yes. The integration uses only the external API and the standard models, so no custom module is needed. On Odoo.sh or a self-hosted server a small module can be added, for example a field for the retailer's department number on the sales order.
What happens to the integration when we upgrade Odoo?
Field and model names change between major versions. Odoo 19 renamed the order line fields tax_id to tax_ids and product_uom to product_uom_id, replaced stock.quant.package with stock.package and removed product packagings. Before an upgrade the integration runs as a dry run against a copy of the database.
Tell us what you need
Tell us which systems you use and what needs to work better. We reply personally within one working day. We help you define the work, then agree the price and timing before you commit.
- You know the price and the timeline before you commit to anything
- We work out the connection that takes the work off your hands
- We handle the build, hosting, monitoring and maintenance
- Your team has less information to retype and fewer discrepancies to chase