TekipostAPI
Guide

Migrating from the old API

Every api-* route on app.tekipost.com, and what replaces it here. Each row was checked against both implementations — the old controller and the new one — so the differences below are the real ones, not a summary.

2 of these have no replacement yet. They are the B2B / heavy-shipment routes. Nothing on this API creates, prices, books or labels a B2B consignment today — the booking service refuses them outright. If your integration ships B2B, talk to us before you migrate anything else.
Old routeReplacementWhat changes
POST /api-login
Exchange e-mail + password for a bearer token.
POST {gotrue}/token?grant_type=password
Contract changed
The CREDENTIAL is unchanged — the same token works. The CALL is not: a different host, and GoTrue answers {access_token, refresh_token, expires_in} where CI wrapped it as {data:{token,…}}. Any client parsing data.token must change. CI also threw a 429 with Retry-After; GoTrue does not.
POST /api-b2c-single-order
Create a B2C order, and ship it unless `isorder` was set.
POST /v1/intake/order → POST /v1/shipping/book
Splits into two calls
One call becomes two, plus a poll. The body is restructured from ~30 flat fields into {customer_order_no, receiver:{}, product:{}, order:{}}. isorder:1 (create only) maps cleanly to just the first call. Booking answers 202, not an AWB — collect it from GET /v1/orders/status. order.pickup_address_id is now required and validated; CI tolerated an empty one. Multi-SKU productdetatis[] has no equivalent: one product block.
POST /api-b2c-quick-shipment
The same handler as api-b2c-single-order, with auto-ship on.
POST /v1/intake/order → POST /v1/shipping/book/sync
Splits into two calls
Use /book/sync rather than /book if you need the AWB inline as CI returned it. It BLOCKS for the courier call — 30–40s is normal, so set your client timeout to 45s+.
POST /api-bulk-b2c-order
Post a JSON array of orders; get a result map keyed by your reference.
Loop POST /v1/intake/order
Contract changed
There is no JSON-array bulk endpoint on the new API. POST /v1/intake/bulk exists but is a spreadsheet upload answering a job_id you poll — a different workflow, not a drop-in. For an array, loop the single-order endpoint and handle each result.
POST /api-b2b-single-order
Create a B2B / heavy multi-box consignment.
none yet
No replacement
Nothing on the new API creates, prices, books or labels a B2B shipment. The booking worker refuses them outright (unsupported_channel: B2B stays on CI) and the rate engine reads the B2C card only. If you ship B2B through the API, talk to us before migrating anything.
POST /api-b2b-quick-shipment
The same handler, with auto-ship on.
none yet
No replacement
Same gap as api-b2b-single-order. No replacement exists.
POST /api-calculate-price
Quote a lane — B2C, or multi-box when `multiChecked` was set.
GET /v1/shipping/serviceable
Contract changed
B2C only. Field names all change: pickupPinCode→from_pin, deliveryPinCode→to_pin, apprWeight→physical_weight, declaredValue→collectable_amount, paymentMode→order_type. Volumetric weight is now yours to compute; CI derived it from L×W×H/5000. 🔴 The multiChecked multi-box branch has no replacement — see the B2B rows. In exchange you get dropped_couriers with reasons, an RTO leg and per-lane performance.
POST /api-logistic-price/{order_id}
Price an order you had already created, across its couriers.
GET /v1/shipping/serviceable
Contract changed
There is no "price this existing order id" call. Read the order back, then assemble the pins, weight and COD for serviceable yourself.
POST /api-ship-order
Book an order you had created. `logistic_id:0` meant auto.
POST /v1/shipping/book
Drop-in
Closest match on the list — same order_id semantics, and omitting logistic_id is the same AUTO fall-through. Two changes: it answers 202 (use /book/sync for inline), and ownership is now enforced — CI never checked that the order was yours.
POST /api-delete-order
Cancel a shipment with the courier, by AWB.
POST /v1/shipping/cancel
Contract changed
🔴 If you were sent to /v1/orders/{id}/delete-return or /hide by an earlier migration note, that is wrong and it matters: neither contacts the courier or refunds the wallet. /hide is a view preference. The real replacement is /v1/shipping/cancel, which takes {order_id, logistic_id} — not an AWB — so resolve it first with GET /v1/orders?awb_number=…. It also refunds, which CI did not.
GET /api-tracking-details/{awb}
Scan history for an AWB.
GET /v1/orders/{id}/tracking
Contract changed
Keyed on the order id, not the AWB. If you hold only an AWB — the common case, since it is what booking returned — resolve it first with GET /v1/orders?awb_number=…. CI put no account predicate on this query at all, so it would return anyone's AWB; the new one is scoped to you.
POST /api-order-shipment-detail
Look up tracking number and label by your own reference.
GET /v1/orders/status?order_no=…
Drop-in
Strictly better. CI could only ever see booked rows, so a booking still in flight or failed looked identical to "no record found". The new one reports state — booked, booking_in_progress, failed (with failure_reason) or not_shipped — and a real 404 for a reference that is not yours.
GET /api-shipping-label-slip/{id}
The label PDF, with no credential at all.
GET /v1/documents/label/{orderId}
Contract changed
Same key, but it now requires your bearer token and 404s an order that is not yours. 🔴 If you stored CI label URLs, e-mailed them, or render them in a customer-facing page, those links will stop working — they were reachable by anyone who had them. For a shareable link use the signed GET /v1/documents/label/t/{token} instead.
POST /api-ndr-list
List failed deliveries, paged with `page` / `pageRow`.
GET /v1/ndr?tab=…
Contract changed
tab is required and has no default. Pagination is cursors, not page numbers. Field names are corrected (CI's reciver_* typo is gone). You also gain NDR actions, which CI had no API for.
POST /api-warehouse-add
Create a warehouse, or update one by passing `id`.
POST /v1/warehouses · PATCH /v1/warehouses/{id}
Splits into two calls
The upsert splits into two verbs. A return address is now required — send is_return_same: true to mirror the pickup address, or a full return_* block. CI silently renamed a duplicate warehouse name; the new API rejects it with a 400.

Two things that are new

GET /v1/orders/status has no old equivalent and is now a required step: booking answers 202, and this is how you collect the AWB. The old API returned the AWB inline, so integrations that never polled anything will need to.

GET /v1/shipping/couriers is also new. Previously the only way to see your couriers was as a side-effect of asking for a price.

Credentials do not change

The bearer token you use today keeps working — it is the same GoTrue credential, verified the same way. Only the host and the login response envelope differ. See Authentication.