Handle retailer purchase orders

Bring retailer purchase orders into Kentro from EDI or a retailer portal, accept or reject them, then follow the sales order to shipment and invoice.

Retailers order by purchase order, most often over EDI. This guide covers an integration that records those purchase orders in Kentro, accepts or rejects them, and follows the resulting sales order through shipment and invoice, so the retailer's acknowledgment, ship notice and invoice can be built from Kentro's documents.

Who this is for

  • EDI providers, VANs and translators that deliver retailer purchase orders for a Kentro tenant outside Kentro's own EDI connections.
  • Developers connecting a retailer portal (drop ship or wholesale) that publishes purchase orders by API or by file.

How it fits

The integration receives the retailer's purchase order (an EDI 850, a portal download), records it with the Channel API, and accepts or rejects it, by rule or after the tenant reviews it. Accepting creates the sales order in the same transaction; from there Kentro routes, reserves and ships it like any other order. The integration reads the shipments and invoices back to build the documents the retailer expects.

 Retailer                  Your EDI integration                       Kentro

 850 purchase order -----> POST /v1/purchase-orders -------------->  purchase order
                           POST /v1/purchase-orders/:id/accept ---->  sales order (routed,
 855 acknowledgment <----- answer of the accept or reject             reserved, shipped)
 856 ship notice    <----- GET /v1/shipments?orderId=...  <--------  shipments
 810 invoice        <----- GET /v1/invoices?orderId=...   <--------  invoices

On channels served by Kentro's own EDI connections, the 855 acknowledgment follows from the accept or reject inside Kentro. An external integration builds its own from the answer.

Objects involved

ObjectNotes
Purchase orderThe retailer's order. externalId is its identity within the channel (usually the PO number). Also purchaseOrderNumber, purchaseOrderDate, dcNumber, storeNumber, shipTo, billTo, items, totals, shipNotBefore, shipNotAfter, paymentTerms and requestedCarrier.
Sales orderCreated by accepting the purchase order; an ordinary Kentro order from then on.
Fulfillment orderThe part of the sales order one warehouse ships.
ShipmentCarrier, tracking, package and lines: the content of a ship notice.
InvoiceThe charge for the shipped lines.

Sequence

  1. Record the purchase order. POST /v1/purchase-orders (purchase-orders-create) with channelId, externalId, the lines (product by sku, quantity, unitPrice) and totals. Concurrent deliveries of one purchase order resolve to one record. onDuplicate: update, the default, applies a redelivery as an update; error answers 409 duplicate instead.
  2. Map every line. A line whose product does not resolve is kept unmapped with a warning, and accept refuses until it is mapped. Fix it with PATCH /v1/purchase-orders/:id (purchase-orders-update).
  3. Accept or reject. POST /v1/purchase-orders/:id/accept (purchase-orders-accept) creates the sales order in the same transaction; a second accept answers the same order. POST /v1/purchase-orders/:id/reject (purchase-orders-reject) with a reason declines it.
  4. Follow fulfillment. GET /v1/fulfillment-orders?orderId=... (fulfillment-orders-list) shows where the sales order ships from and its status. shipNotBefore and shipNotAfter carry the retailer's window, and the fulfillment queue filters on it with shipNotAfterFrom and shipNotAfterTo (fulfillment-queue-list).
  5. Build the ship notice. Poll GET /v1/shipments?orderId=...&updatedSince=... (shipments-list) for carrier, tracking, package and lines.
  6. Invoice. POST /v1/orders/:id/invoices (invoices-from-order) invoices the order's lines; GET /v1/invoices?orderId=... reads the invoices the order has.
  7. Cancel when the retailer cancels. POST /v1/purchase-orders/:id/cancel (purchase-orders-cancel) works before acceptance. An accepted purchase order answers 409: cancel its sales order with POST /v1/orders/:id/cancel (orders-cancel) instead.

The request body of each step is in Common flows, section "Retailer purchase orders".

Edge cases

CaseWhat happensWhat to do
The same purchase order arrives twiceOne record; onDuplicate decides between update, skip and 409 duplicate.Keep externalId stable across redeliveries.
A line does not map to a productKept with a warning; accept answers 422 unresolved_reference.Map the product, then accept.
Totals differ from the linesKentro recomputes totals; a mismatch is 422 totals_mismatch with the computed values.Check the lines against the retailer's document.
A change before acceptancePATCH /v1/purchase-orders/:id applies only the fields sent; version guards concurrent edits.Apply the retailer's change, then accept.
A change after acceptanceThe sales order is the document to change.Use PATCH /v1/orders/:id.
A cancel after acceptance409 on the purchase order.Cancel the sales order.
Ship windowsshipNotBefore and shipNotAfter are dates, anchored at noon UTC.Send the dates exactly as the retailer sent them.

Checklist

  • The key holds channel:read and purchase-orders:write, plus shipments:write and invoices:write if the integration records them, and is bound to the retailer's channel.
  • externalId is the retailer's PO identity and never changes between redeliveries.
  • Every line maps to a sellable product before accept.
  • Accept and reject run once per purchase order, with an Idempotency-Key.
  • Ship notices and invoices are built from updatedSince polls of shipments and invoices.
  • Retailer cancellations after acceptance cancel the sales order.
  • The Go-live checklist passes.

Build it with an AI agent

The prompt below starts a coding agent on this integration. Agent setup and the other prompts: Build with AI agents.

📘

Build it with an AI agent

Build an integration that creates retailer purchase orders in Kentro from EDI files or exports, then accepts or rejects them. Copy the prompt into Claude Code, Codex, Cursor or another coding agent. The prompt library explains how to run it.

You are building an integration that brings retailer purchase orders into Kentro through the Kentro Channel API, for purchase orders that arrive outside Kentro's own EDI connections (EDI files from a VAN, a retailer portal export, an email feed). Each purchase order is created in Kentro, then accepted, which turns it into a sales order Kentro routes and ships, or rejected.

Read first. Fetch https://docs.kentro.io/llms.txt and these pages, and look up every endpoint and schema in them before you use it:
- https://docs.kentro.io/docs/ai-agent-instructions.md (rules you must follow)
- https://docs.kentro.io/reference/channel-api-authentication.md
- https://docs.kentro.io/reference/channel-api-conventions.md
- https://docs.kentro.io/reference/channel-api-flows.md (section "Retailer purchase orders")
- https://docs.kentro.io/docs/guide-retailer-edi.md

Ask the user for these before writing code:
1. Whether this retailer already reaches Kentro through a Kentro EDI connection. If it does, stop and tell the user: those purchase orders already arrive in Kentro, and a second path would duplicate them.
2. Where the purchase orders come from, and a few sample files (X12 850, CSV or JSON).
3. A Kentro sandbox API key pair from Kentro OS at https://kentro.cc (`Users` > `API keys`) with `purchase-orders:write`, plus `channel:read` to read the orders they become. Ask the user to store it as KENTRO_KEY and KENTRO_SECRET in the environment, never in the chat.
4. The retailer's sales channel in Kentro, the retailer's ship-to locations as customer externalId values, and how retailer item numbers map to Kentro SKUs.
5. The acceptance rules: which purchase orders to accept automatically, and which to hold or reject.

Rules:
- Build against https://apis.kentro.dev/channel/v1 and call only the endpoints the Channel API reference documents. There is no Kentro SDK or CLI.
- Create purchase orders with onDuplicate: error, so a purchase order received twice answers 409 duplicate. Record that answer as already received, not as a failure.
- Send an Idempotency-Key on every write, derived from the purchase order number and the action (po-4500262852-create, po-4500262852-accept).
- purchaseOrderDate, shipNotBefore and shipNotAfter take YYYY-MM-DD. Money is decimal strings with currency, and totals must add up.
- Lines name sellable variants by SKU. An unknown SKU answers 422 unresolved_reference: report it to the user, never guess a mapping.
- Retry only 429, 503, 500 and network errors, with backoff that honors Retry-After. Log requestId on every failure.

Steps:
1. Parse each purchase order into the POST /v1/purchase-orders body: channelId, externalId, purchaseOrderNumber, purchaseOrderDate, provider, customer, shipTo, items, totals and the ship window.
2. Create it, then call POST /v1/purchase-orders/:id/accept, or POST /v1/purchase-orders/:id/reject with a reason, following the user's rules.
3. After acceptance, follow the order with GET /v1/orders and GET /v1/shipments?orderId=... (with updatedSince) to produce what the retailer expects back, such as ship notices and invoices.
4. Log each purchase order's outcome with its Kentro ids.

Acceptance checks. Run each one against the sandbox and show the user the requests and responses, with secrets redacted:
- A sample purchase order answers 201 created; sending it again answers 409 duplicate, and the integration records it as already received.
- Accepting it answers 2xx, and the resulting order is readable with GET /v1/orders.
- A second sample purchase order is rejected with a reason and does not become an order.
- A purchase order with an unknown SKU answers 422 unresolved_reference and is reported to the user.
- No secret appears in code, logs or the repository.

Did this page help you?