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:
| Status | Meaning |
|---|---|
201 Created | First time Kanal saw this external_id |
200 OK | An 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:
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.