Customer · Settings
Integration API
Get prices, create orders and track their status from your own software; the API docs, first requests, bulk orders, safe retries, webhook signatures and limits.
The Integration API lets your ordering, ERP or warehouse software connect to Courino directly. Your software can do every order task you do by hand in the portal: get a price, create, edit and cancel orders, and track the status and live location. This article is a starting guide for your software developer.
What do you need?
- The API and webhooks permission on your account. If you don't have it, ask your courier company.
- An API key. See Creating an API key for how to create one.
- A webhook subscription, if you want order statuses sent to your system automatically: Adding a webhook.
Your first order step by step
- Create a key on the Integration (API keys) card on the My details page.
- Click the API docs button at the top right of the same card. The docs open in a new tab, in the language you use the portal in (English or Turkish).
- In the docs, click Authorize, paste the key and confirm. You can now try every endpoint from the browser with Try it out, using your real account.
- Get the districts and delivery types with
GET /me/order-options. Every address in an order needs a district ID (districtId) and the order needs a delivery type (deliveryTypeId). - See the price with
POST /pricing/quote. This request saves nothing. - Create the order with
POST /orders. The response returns the order's ID (id) and number (number). - Read the status with
GET /me/orders/{id}or listen for it with a webhook.
{}) in the request.Commonly used endpoints
All paths are under /api/v1 (e.g. https://client.courino.com/api/v1/orders).
| Endpoint | What it does |
|---|---|
GET /me/order-options | Districts, delivery types and business hours |
POST /pricing/quote | Price quote (saves nothing) |
POST /orders | Creates an order |
POST /me/orders/batch | Bulk orders (up to 1000 rows) |
GET /me/orders | My orders; filter by reference, barcode, status and date, free-text search with q, amount range with minAmount/maxAmount, sorting with sort (e.g. sort=-total) |
GET /me/orders/{id} | Order details |
PUT /me/orders/{id} | Edits an order that is still in the pool |
POST /me/orders/{id}/cancel | Cancels the order |
GET /me/orders/{id}/tracking | Live location while the courier is on the way |
POST /me/orders/{id}/tracking-link | Tracking link to send to the recipient |
POST /me/orders/imports | Previews an Excel/CSV shipment file; .../confirm creates the orders |
GET /me/ecommerce/orders | Orders from your connected stores; fix, retry or skip the ones awaiting a match |
GET /me/customer/statement | Account statement for a period (PDF) |
GET /me/customer/payments | Your online payments (read only) |
GET /me/customer/messages | SMS and e-mails sent to you and your contacts |
This table only lists the most used endpoints; every billing, cash on delivery, contact and special date endpoint is in the documentation.
Safe retries
When the connection drops you can't tell whether the request arrived. When creating and cancelling orders, put a value unique to that job (e.g. your own order number) in the Idempotency-Key header. If you send it again with the same value, no second order is created and the first response is returned. Sending different content with the same value returns a 409 error. The value is kept for 24 hours.
In bulk orders each row's reference number (reference) does the same job: an order with the same reference isn't created twice. You can send the bulk request with dryRun: true first to only validate it. See Bulk shipments for details.
Verifying the webhook signature
Every webhook request comes with these headers:
X-KOS-Event: the event name (e.g.order.delivered).X-KOS-Delivery: the message ID; it doesn't change on retries. Store it and skip repeats.X-KOS-Signature: the signature in the formt=1761040000,v1=9f2c…(tis the send time,v1the signature).
To verify the signature, join the t value, a dot and the raw request body, and compute HMAC-SHA256 with the signing secret (all of it, including whsec_); compare the result with v1. Reject requests whose time is more than 5 minutes old. Reply quickly with 2xx; if you return 410, retries stop for that message. A code sample is in the docs and on the courino.com/en/developers page.
Rules and limits
- At most 120 requests per minute per key. Over that you get
429; wait as long asRetry-Afterin the response says. Every response shows your remaining allowance in theX-RateLimit-Remainingheader. - At most 5 active keys and 5 webhook subscriptions per account.
- Amounts are returned as strings (e.g.
"185.00"); take care when converting them to numbers so no cents are lost. - Errors use the
application/problem+jsonformat; thecodefield is the machine-readable name of the error, and field errors say which field is wrong. - You can send phone numbers in any format (e.g.
0532 123 45 67); responses return them as+905321234567.
Having trouble?
- I get a
401error: The key may be wrong, revoked, or closed because of a password change. Check the key's status in the portal and create a new key if needed. - I get a
403error: The account that created the key doesn't have permission for that task (e.g. placing orders); ask your courier company for the permission. Tasks that need a session, such as saved cards, support tickets and starting a payment, can never be done with a key; do them in the portal. - "District not found" or a delivery type error: Take the IDs from the
GET /me/order-optionsresponse; you can't send names. - The docs don't open: Use the API docs button from inside the customer portal; the docs are at the same address as the portal.
- If you get stuck: Send the full error response and the time of the request to destek@courino.com.