Connect a sales channel
Build a connector that keeps a marketplace, cart or B2B portal in step with Kentro: catalog, stock, orders, shipments, invoices and payments.
A channel connector moves one sales channel's data into Kentro and Kentro's answers back out: the catalog and stock the channel sells, the orders it takes, and the shipments, invoices and payments that follow. Kentro is the system of record; the connector is a system acting for one tenant through the Channel API.
Who this is for
- Developers connecting a marketplace, a shopping cart, a B2B ordering portal or an in-house channel that Kentro's built-in integrations do not cover.
- Partners building one connector for many Kentro tenants, with one API key per tenant.
How it fits
The connector runs on your infrastructure. It listens to the channel (webhooks or polling), writes the channel's documents into Kentro, and polls Kentro with updatedSince for what happened there: shipments, status changes, payments applied in Kentro OS. It writes those back to the channel. Kentro routes each order to warehouses and creates its fulfillment orders, unless the channel ships the order itself.
Sales channel Your connector Kentro
(marketplace, cart, (server, one API key (system of record)
B2B portal) per tenant)
order placed ----------> POST /v1/orders --------------------> order, routed to
fulfillment orders
stock, prices <---------- GET /v1/catalog <-------------------- products, listings
tracking <---------- GET /v1/shipments?updatedSince=... <-- shipments made
refund issued ----------> POST /v1/invoices, POST /v1/payments -> refund documents
Objects involved
| Object | Direction | Endpoints |
|---|---|---|
| Sales channel | Read | GET /v1/channels |
| Product, variants, listings | Into Kentro, or read from Kentro | POST /v1/products, PUT /v1/products/:id/listings, GET /v1/catalog |
| Inventory level | Into Kentro when the channel owns the count; read otherwise | PUT /v1/products/:id/inventory-levels, GET /v1/catalog |
| Customer | Into Kentro, usually inline on the order | POST /v1/customers |
| Order | Into Kentro | POST /v1/orders, POST /v1/orders/import |
| Fulfillment order | Created by Kentro, or generated from the channel's own split | POST /v1/orders/:id/fulfillment-orders/generate |
| Shipment | Read from Kentro, or recorded when the channel ships | GET /v1/shipments, POST /v1/shipments |
| Invoice, payment | Into Kentro | POST /v1/invoices, POST /v1/payments |
Sequence
- Resolve the channel.
GET /v1/channels(channels-list) answers the channels the key may write into; store thechannelIdand bind the key to that channel in Kentro OS. - Sync the catalog.
POST /v1/products(products-create) withchannelId,externalId,sku,listingsandonDuplicate: update, so the same call repeated is an update. A product with variants sendsvariants[], and orders then name the variant's SKU. Listings change withPUT /v1/products/:id/listings(products-listings). - Agree who owns stock. When the channel owns the count, set it with
PUT /v1/products/:id/inventory-levels(products-inventory-levels). When Kentro owns it, readGET /v1/catalog?channelId=...&updatedSince=..., the sellable products with prices and stock, and push the changes to the channel. - Import orders.
POST /v1/orders(orders-create) with the channel's order id asexternalIdand in theIdempotency-Key, the customer inline, lines that name aproductbysku,totals, andinvoicingwhen the channel collected payment. For a backlog,POST /v1/orders/import(orders-import) takes up to 1,000 orders and answers202with an operation id to poll atGET /v1/operations/:id(operations-get). - Decide who fulfills. By default Kentro routes the order and creates its fulfillment orders. A channel that already split the order generates them from its own split with
POST /v1/orders/:id/fulfillment-orders/generate(fulfillment-orders-generate). A channel that ships itself sends the order with"fulfillment": { "skip": true }and records each shipment withPOST /v1/shipments(shipments-create). - Write shipments back. Poll
GET /v1/shipments?channelId=...&updatedSince=...(shipments-list) and send the carrier and tracking to the channel.GET /v1/shipments/:id/tracking(shipments-tracking) answers the tracking Kentro stores. - Invoice and pay separately when needed. For net terms, deposits and B2B, leave
invoicingoff the order and create documents as they happen:POST /v1/invoices(invoices-create) andPOST /v1/payments(payments-create) withapplication.mode: auto. - Cancel and refund.
POST /v1/orders/:id/cancel(orders-cancel) releases the reservations and cancels the open fulfillment orders. A refund is an invoice oftype: refundplus a payment oftype: refund, both with positive amounts. - Stay in sync. Poll
GET /v1/orders?updatedSince=<last updatedAt>&include=itemsfor changes made in Kentro. Pages are newest first: walk to the end before advancing the watermark. Every step has a worked body in Common flows.
Edge cases
| Case | What Kentro answers | What to do |
|---|---|---|
| A line names a product group instead of a variant | 422 unresolved_reference, details[0].code: group_container | Send the variant's SKU. |
| A line names an unknown SKU | 422 unresolved_reference, details[0].code: not_found | Create the product first, or send the line without product as a custom line (a fee, gift wrap), which never moves stock. |
| Totals do not add up | 422 totals_mismatch, with the sent and computed amounts in details | Send what the channel charged, split into subtotal, shipping, taxes and discount. |
| The channel redelivers a webhook | The Idempotency-Key replays the first answer; onDuplicate: update answers unchanged | Nothing: no second order and no second event. |
| Two buyers share an email address | Customers match by externalId, then email, then phone | Send X-Customer-Match: externalId to match by id only. |
| The order is canceled after units shipped | 409 state_conflict, details[0].code: line_shipped | Cancel the open fulfillment orders with POST /v1/orders/:id/fulfillment-orders/cancel and record a refund. |
| A payment Kentro charged must be refunded | 409 state_conflict, provider_refund_required | Refund it in Kentro OS, not through the API. |
| An order waits in stock review | status: stock-review | The tenant decides in Kentro, or the connector calls POST /v1/orders/:id/stock-review/accept or /reject. |
| The rate limit is reached | 429 rate_limited with Retry-After | Wait, and move backlogs to the import endpoint. |
Checklist
- The key holds
channel:write, orchannel:readplus the write scopes it uses, and is bound to the channel. - Every write sends an
Idempotency-Keybuilt from the channel's own ids. - Every create sends
externalIdand an explicitonDuplicate. - Money travels as decimal strings with
currency, and totals add up. - Order lines name sellable variants.
- History was loaded with
X-Import-Mode: historical. - Shipments and status changes flow back through
updatedSincepolling. 429and503are retried with backoff, and every refusal'srequestIdis logged.- The Go-live checklist passes.
Build it with an AI agent
The prompt below starts a coding agent on this connector. Agent setup and the other prompts: Build with AI agents.
Build it with an AI agent
Build a connector that imports a channel's products, orders and customers into Kentro and sends shipments and stock back. Copy the prompt into Claude Code, Codex, Cursor or another coding agent. The prompt library explains how to run it.
You are building a connector between a sales channel (a marketplace or a shopping cart platform) and Kentro, using the Kentro Channel API. Kentro is the system of record for orders, inventory and fulfillment. The connector moves the channel's products, customers and orders into Kentro and sends shipments, tracking and stock back to the channel.
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-getting-started.md
- 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
- https://docs.kentro.io/docs/guide-sales-channel.md
Ask the user for these before writing code:
1. The channel platform, its API documentation and where its credentials are stored.
2. A Kentro sandbox API key pair, created in Kentro OS at https://kentro.cc under `Users` > `API keys`, with a permission group holding `channel:write` (or only the write scopes the connector needs) and bound to the target sales channel. Ask the user to store it in the environment variables KENTRO_KEY and KENTRO_SECRET. Never ask the user to paste a secret into the chat.
3. The Kentro sales channel id. If the user does not know it, call GET https://apis.kentro.dev/channel/v1/channels and ask the user to pick one.
4. Which system owns stock (the channel or Kentro), and which documents flow in each direction.
Rules:
- Build against https://apis.kentro.dev/channel/v1. Production is https://apis.kentro.live/channel/v1 with a production key, used only when the user asks.
- Call only the endpoints the Channel API reference documents. There is no Kentro SDK or CLI; use plain HTTPS.
- Send an Idempotency-Key on every write, derived from the channel's own ids (for example `<channel>-order-<id>-create`), and send the same key when you retry that write.
- Send externalId on every create, address documents as `ext:<channelId>:<externalId>`, and keep onDuplicate at update so a replayed feed never duplicates.
- Money is decimal strings in currency units ("12.50") with currency, and totals must add up. Order lines name the sellable variant, never the parent product.
- Retry only 429, 503, 500 and network errors, with backoff that honors Retry-After. Log requestId on every failure.
Steps:
1. Products: POST /v1/products with variants and the channel listing inline. When the channel owns stock, set it with PUT /v1/products/:id/inventory-levels.
2. Orders: POST /v1/orders with the customer inline, the lines, the totals and, when the channel collected payment, invoicing. Backfill history with X-Import-Mode: historical, and send large backfills to POST /v1/orders/import.
3. Fulfillment back to the channel: poll GET /v1/shipments?updatedSince=... and GET /v1/orders?updatedSince=..., walk every page to nextCursor null before advancing the watermark, and push tracking to the channel.
4. Cancellations and refunds: POST /v1/orders/:id/cancel, and refunds as the flows page describes.
5. Configuration for ids and keys (nothing hard-coded), scheduling and structured logs.
Acceptance checks. Run each one against the sandbox and show the user the requests and responses, with secrets redacted:
- GET /v1/channels answers 200 and includes the bound sales channel.
- One product POST answers 201 with outcome created; the same request again answers 200 with outcome updated or unchanged.
- One order POST with X-Dry-Run: true answers 2xx with the X-Dry-Run: applied header; the real POST then answers 201; repeating it with the same Idempotency-Key answers with Idempotent-Replayed: true.
- GET `/v1/orders/ext:<channelId>:<externalId>?include=items,customer` returns the order with the expected lines and totals.
- A shipment recorded for the order appears in GET /v1/shipments?orderId=..., and the connector pushes its tracking number to the channel.
- No secret appears in code, logs or the repository.
Updated about 12 hours ago

