Sapa Moto

The reference is rendered from OpenAPI 3.1. Secret keys are never entered in a browser: try requests with curl or Postman and a test key. Download OpenAPI

Loading the reference…

API reference

Endpoints

Quotes

POST /v1/quotes — Price a delivery

Prices a delivery and estimates its ETA without creating an order. The quote_id holds the price for 10 minutes (valid_until): an order created with it gets exactly this price even if the tariff changed. After that the order is refused with quote_expired. Addresses outside the zones are refused with out_of_zone.
Requires the orders.write scope.

Orders

GET /v1/orders — List orders

Your orders in the key's environment, newest first, with filters. Cursor pagination: pass next_cursor of a page as cursor to get the next one; next_cursor is null on the last page.
Requires the orders.read scope.

POST /v1/orders — Create an order

Creates a delivery order. With quote_id the price is taken from the quote (valid 10 minutes, otherwise quote_expired); without it the order is priced by the current tariff.
Idempotency. Send a unique Idempotency-Key (we recommend a UUID). A retry with the same key and the same body within 24 hours returns the first response unchanged — so a timeout never creates two orders. The same key with another body is refused with 409 idempotency_conflict.
`external_id` is your order number; it is unique per organization and environment. A second order with the same external_id (even with another key) is refused with 409 duplicate_external_id and the link to the existing order.
The response carries the tracking_url for the recipient and the delivery_code the recipient tells the courier on handover. Test keys create sandbox orders driven by a virtual courier.
Requires the orders.write scope.

POST /v1/orders/batch — Create orders in a batch

Creates up to 100 orders in one request. Each order is processed independently: the result lists created with the order or error with the problem code for every item, in the request order. The batch as a whole is idempotent with Idempotency-Key.
Requires the orders.write scope.

GET /v1/orders/{id} — Get an order

The order with its status, version, courier (name, masked phone, transport), ETA, status history and price. {id} is either our order id or your external_id. Orders of other organizations are 404.
Requires the orders.read scope.

PATCH /v1/orders/{id} — Update an order

Changes an order. Until picked_up you can change the drop-off, the recipient, the time and the cash-on-delivery amount. After pickup only comment and recipient_phone can change; other fields are refused with 422 order_not_editable and details[].field. Send only the fields you change.
Requires the orders.write scope.

POST /v1/orders/{id}/cancel — Cancel an order

Cancels an order with a reason code. Free until a courier is assigned; afterwards the cancellation fee of the tariff applies and is returned in cancellation_fee (the courier is compensated). After pickup a cancellation turns into a return to the pickup point (returning). Final, failed and returning orders cannot be cancelled.
Requires the orders.write scope.

POST /v1/orders/{id}/payment — Report a Kaspi QR payment

Reports the status of the shop's own Kaspi QR payment for a cash-on-delivery order with method: kaspi_qr. The courier can hand the order over only after paid.
Requires the orders.write scope.

POST /v1/orders/{id}/ready — Mark an order ready

Signals that the order is packed and ready for the courier. Without it the courier search starts by the assembly norm of the pickup point.
Requires the orders.write scope.

Tracking & proof

GET /v1/orders/{id}/proof — Get proof of delivery

Proof of delivery: the handover method, whether the recipient's code was verified, the geopoint and time, the cash collected and a temporary link to the photo (valid 15 minutes — fetch a new one when it expires). Available once the order is delivered; 404 before that.
Requires the orders.read scope.

GET /v1/orders/{id}/track — Track the courier

The courier's last position and the ETA. Call it at most once per 10 seconds per order (otherwise 429). The position is shown while the courier carries the order; before that only the status and the ETA. The courier position is never sent by webhooks — poll this endpoint or give the recipient tracking_url.
Requires the orders.read scope.

Pickup points

GET /v1/pickup-points — List pickup points

Pickup points of your organization (managed in the cabinet). Use id as pickup.point_id.
Requires the points.read scope.

GET /v1/slots — List delivery slots

Delivery slots of a pickup point for a date. Use from of an available slot as scheduled_at with type: slot. In the sandbox there is a two-hour slot every two hours from 10:00 to 22:00.
Requires the orders.read scope.

Balance

GET /v1/balance — Get the balance

The deposit balance (prepaid) or the remaining credit limit (postpaid) of your organization. In the sandbox it is a fixed test balance of 1 000 000 ₸. Watch the balance.low webhook to top up in time.
Requires the finance.read scope.

GET /v1/statements — Get the statement of charges and cash on delivery

The statement of a period: charges for deliveries and every order with cash on delivery that finished on the local days [from, to] (Almaty, at most 92 days) — the amount to collect, what the courier accepted, the method, and how it settles. Cash is set off against your deliveries when the period closes (to_offset → offset); Kaspi QR money goes to you directly (kaspi_direct). When the cash exceeds the deliveries Sapa Moto transfers the difference to you (payable_to_client). The same data as «Сверка наложенного платежа» in the cabinet. In the sandbox the statement is empty.
Requires the finance.read scope.

Webhooks

GET /v1/webhooks — List webhook endpoints

Webhook endpoints of the key's environment. Test and live endpoints are separate.
Requires the webhooks.manage scope.

POST /v1/webhooks — Create a webhook endpoint

Adds an endpoint. The signing secret (whsec_…) is returned once — store it to verify X-Sapa-Signature. An empty event_types subscribes to every event. URLs must be https on a public host. Up to 10 endpoints per environment.
Requires the webhooks.manage scope.

PUT /v1/webhooks/{endpointId} — Update a webhook endpoint

Changes the URL, the subscribed events or the status of an endpoint. status: active re-enables a suspended endpoint: events held while it was suspended (up to 7 days) are sent in order. status: disabled stops deliveries.
Requires the webhooks.manage scope.

Webhooks

balance.low — Balance low

The deposit or the remaining credit limit fell below your threshold.

claim.updated — Claim updated

The status of a claim (damage, loss, complaint) changed.

order.courier_assigned — Courier assigned

A courier was assigned or replaced (including a handover to another courier).

order.created — Order created

An order was created — by API, in the cabinet, by import or by a dispatcher.

order.delivered — Delivered

The order was handed over — with proof and, for cash on delivery, the amount and the method. This is the signal to send the electronic fiscal receipt to the buyer.

order.eta_changed — ETA changed

The ETA moved by more than your threshold (5 minutes by default, set by your account manager).

order.failed — Not delivered

The order could not be delivered; it goes back to the pickup point.

order.price_changed — Price changed

The final price changed (waiting time, return, address change).

order.returned — Returned

The order was returned to the pickup point.

order.status_changed — Status changed

Any status transition, with the previous and the new status and the reason code. Sent for every transition in order of the version.

ping — Ping

A test event sent by the «Ping» button in the cabinet. Not subscribable; always delivered.