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 kanal_sk_live_xxx" \
-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. Retry network and 5xx errors
freely with exponential backoff.