Table of Contents

Integration API

/api/integration — how the ERP pushes data in

How the ERP's data gets into Commerce. The ERP pushes; Commerce does not pull. This is the surface that makes the platform ERP-neutral on the way in.

Who calls it

The ERP, or the middleware in front of it. Today that is Business Central — but nothing on this surface knows that, which is the point.

Authentication Bearer JWT, on a scheme of its own
Authorization InboundPolicy, on that scheme only
Scope Bypass. A batch covers many customers
Contract stability Published. Upstream depends on it

One envelope for every write

Every endpoint here takes the same shape, so an upstream integrator implements one contract rather than seven.

{
  "source": "BC",
  "timestamp": "2026-08-19T04:00:00Z",
  "data": [
    {
      "sku": "SHIRT-BLK-M",
      "description": "Classic Shirt Black M"
    },
    {
      "sku": "SHIRT-BLK-L",
      "description": "Classic Shirt Black L"
    }
  ]
}
Field Meaning
source Required. Which upstream system sent this
timestamp Optional, informational — the sender's send time
data Required. A batch. Each item carries its own keys and is processed individually

And every write answers in the same shape — one result per item, keyed by that item's natural identifier, with optional nested results where one item produced several entities:

{
  "data": [
    {
      "key": "C00123",
      "outcome": "Upserted",
      "type": "Account",
      "children": [
        { "key": "SHIP-1", "outcome": "Upserted" }
      ]
    }
  ]
}

Why per-item results rather than one status for the batch. A batch of five hundred products where three are malformed should not fail wholesale, and the sender needs to know which three. Reporting per item makes a partial success expressible instead of forcing an all-or-nothing answer.

Note

outcome is currently always "Upserted" — the services underneath do not report whether they inserted or updated. The field exists so that "Created" and "Updated" can be distinguished later without changing the shape.

Every endpoint

Twelve, grouped by the controller that serves them. Each group is one domain.

Group Domain Endpoints
Catalogue Catalogue PUT /catalogue/products
Customer Customer PUT /customer · PUT /customer/ledger
Fulfilment Fulfilment PUT /fulfilment/postcode-routing · /freight-rates · /airbags
Inventory Inventory PUT /inventory
Pricing Pricing PUT /pricing/rules
Tax Tax PUT /tax/jurisdictions · /areas · /gst-posting-rates
Trading Trading PUT /trading/order-line-updates

All are PUT — every one is an upsert, and re-sending the same payload is safe.

Trading is the only one that flows back: once an order has been posted to the ERP, the ERP reports what was invoiced, shipped or cancelled per line. Order status in Commerce is derived from those quantities rather than stored, so a repeated update changes nothing.

Snapshot or not — the difference that bites

Most endpoints treat a payload as a snapshot: a child row stored in Commerce but absent from the payload is deleted. Omission is deletion, and there is no per-row delete signal.

Snapshot — omission deletes Customer ship-to addresses · Inventory locations · Tax jurisdiction rates and area links
Not a snapshot — omission is ignored Pricing rules, which are only ever inserted or overwritten by ruleId

The snapshot model is deliberate: upstream systems rarely emit reliable delete events for child rows, so reconciling from a snapshot moves that burden off the integrator. But Pricing does not work that way, and an integration that assumes it does will accumulate stale price rules indefinitely.

Two rules that are easy to get wrong

Audit timestamps are not part of the contract. Inbound payloads do not declare when a record was created or modified, and a value sent is ignored. Those columns record when Commerce learned something, which is a question only Commerce can answer. See Database.

Correlation travels on a header, not in the envelope. Send X-Correlation-Id and it is echoed on the response, so you can tie your own logs to a Commerce request. Note that Commerce does not currently carry it into the background work a push triggers — see Logging.

Why this surface is the ERP-neutral half

Commerce publishes a contract and waits. It does not know what wrote the payload, does not hold an ERP client to receive it, and does not care whether the sender is an ERP, a middleware bus or a script. Any system able to speak the envelope can feed the platform.

The outbound direction is not yet symmetrical — placing an order calls Business Central through a typed client, so a second ERP needs an adapter there. The asymmetry is deliberate to acknowledge rather than paper over: inbound is done, outbound is not.