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
| Object | Notes |
|---|---|
| Purchase order | The 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 order | Created by accepting the purchase order; an ordinary Kentro order from then on. |
| Fulfillment order | The part of the sales order one warehouse ships. |
| Shipment | Carrier, tracking, package and lines: the content of a ship notice. |
| Invoice | The charge for the shipped lines. |
Sequence
- Record the purchase order.
POST /v1/purchase-orders(purchase-orders-create) withchannelId,externalId, the lines (productbysku,quantity,unitPrice) andtotals. Concurrent deliveries of one purchase order resolve to one record.onDuplicate: update, the default, applies a redelivery as an update;erroranswers409 duplicateinstead. - 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). - 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 areasondeclines it. - Follow fulfillment.
GET /v1/fulfillment-orders?orderId=...(fulfillment-orders-list) shows where the sales order ships from and its status.shipNotBeforeandshipNotAftercarry the retailer's window, and the fulfillment queue filters on it withshipNotAfterFromandshipNotAfterTo(fulfillment-queue-list). - Build the ship notice. Poll
GET /v1/shipments?orderId=...&updatedSince=...(shipments-list) for carrier, tracking, package and lines. - Invoice.
POST /v1/orders/:id/invoices(invoices-from-order) invoices the order's lines;GET /v1/invoices?orderId=...reads the invoices the order has. - Cancel when the retailer cancels.
POST /v1/purchase-orders/:id/cancel(purchase-orders-cancel) works before acceptance. An accepted purchase order answers409: cancel its sales order withPOST /v1/orders/:id/cancel(orders-cancel) instead.
The request body of each step is in Common flows, section "Retailer purchase orders".
Edge cases
| Case | What happens | What to do |
|---|---|---|
| The same purchase order arrives twice | One record; onDuplicate decides between update, skip and 409 duplicate. | Keep externalId stable across redeliveries. |
| A line does not map to a product | Kept with a warning; accept answers 422 unresolved_reference. | Map the product, then accept. |
| Totals differ from the lines | Kentro recomputes totals; a mismatch is 422 totals_mismatch with the computed values. | Check the lines against the retailer's document. |
| A change before acceptance | PATCH /v1/purchase-orders/:id applies only the fields sent; version guards concurrent edits. | Apply the retailer's change, then accept. |
| A change after acceptance | The sales order is the document to change. | Use PATCH /v1/orders/:id. |
| A cancel after acceptance | 409 on the purchase order. | Cancel the sales order. |
| Ship windows | shipNotBefore and shipNotAfter are dates, anchored at noon UTC. | Send the dates exactly as the retailer sent them. |
Checklist
- The key holds
channel:readandpurchase-orders:write, plusshipments:writeandinvoices:writeif the integration records them, and is bound to the retailer's channel. externalIdis 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
updatedSincepolls 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.
Updated 1 day ago

