KanalKanal API
Getting started

Idempotency

How upserts use your external_id as a natural key.

The API is upsert-based. You don't need to track whether a record already exists in Kanal — you send the full object and Kanal creates or updates it.

The natural key

Every resource is uniquely identified by the pair:

(store_id, external_id)

external_id is your identifier — your order number, checkout token, or customer id. Send the same external_id again and Kanal updates the existing record instead of creating a duplicate.

For customers, external_id is optional. If you omit it, the customer's phone becomes the natural key.

Create vs. update response

POST endpoints tell you which happened via the HTTP status:

StatusMeaning
201 CreatedFirst time Kanal saw this external_id
200 OKAn existing record was updated

Both return the same JSON body, so you can safely treat them the same way.

Partial updates

POST replaces the resource. To change only a few fields on an existing order or checkout, use the PATCH endpoint instead:

Mark an order as paid
curl -X PATCH https://api.getkanal.com/api/v1/stores/123/orders/ORDER-1001 \
  -H "Authorization: Bearer 8fK2pX9mWq4Ld7Vb3Nc6Ts1Z" \
  -H "Content-Type: application/json" \
  -d '{ "financial_status": "paid" }'

PATCH accepts only the mutable fields (financial_status, fulfillment_status, tags, metadata for orders; status, recovery_url, tags, metadata for checkouts) and leaves everything else untouched.

Safe retries

Because writes are keyed on (store_id, external_id), retrying a failed or timed-out request is always safe — replaying the same payload converges to the same state and never creates a duplicate row. Retry network and 5xx errors freely with exponential backoff.

Retries never create duplicate rows, but a retried POST /orders / /checkouts can re-fire the automation triggers attached to that resource. If a slow request times out, prefer checking your side before blindly replaying, or keep automations tolerant of a repeated trigger.

Idempotency-Key header

The commerce endpoints do not read an Idempotency-Key header — their idempotency is the natural key above. The messaging endpoints (/api/v1/messages/send, /api/v1/templates) do support an optional Idempotency-Key header: replaying the same key returns 201 and does not re-send.

On this page