Developers
One API.
Every shipment.
Create parcels, quote rates, track status and receive webhooks. Versioned, idempotent, and with a sandbox that behaves like production because it is the same code.
- REST + JSON
- Idempotency keys
- Signed webhooks
- Sandbox environment
The contract
What you can rely on
Published limits and guarantees, so you can design your retry logic against something real.
- Reads per minute
- 600Reads per minutePer API key
- Writes per minute
- 120Writes per minutePer API key
- Webhook retry window
- 24 hWebhook retry windowThen dead-lettered
- Availability target
- 99.9%Availability targetMonthly
Create a parcel
One POST, one tracking number
The tracking number comes back in the response, ready to show the customer on your order confirmation page. No polling, no second call.
Idempotency-Key is honoured
Retry after a timeout and you get the same parcel, not a duplicate.
Charges returned at creation
The full breakdown, so you can show a landed cost immediately.
Promise date included
Computed from the destination zone, ready to display.
Validation errors are per-field
Not a single unhelpful 400 with a sentence in it.
curl -X POST https://api.udanexpress.com/api/v1/parcels \
-H "Authorization: Bearer $UDAN_API_KEY" \
-H "Idempotency-Key: order-48219" \
-H "Content-Type: application/json" \
-d '{
"service": "STANDARD",
"payment": "COD",
"codAmount": 2500,
"recipient": {
"name": "Sunita Adhikari",
"phone": "9801234567",
"district": "Kaski",
"address": "Lakeside, Ward 6",
"landmark": "opposite the school"
},
"parcel": {
"contents": "Cotton kurta set",
"weightKg": 1.2,
"dimensions": { "l": 30, "w": 25, "h": 10 },
"declaredValue": 2500
}
}'{
"success": true,
"data": {
"id": "pc_0f3a9c21",
"tracking": "UDN26H4K2M9QP",
"status": "PENDING_PICKUP",
"promisedBy": "2026-08-24T12:00:00+05:45",
"charges": {
"base": 130, "weight": 45, "service": 0,
"fuel": 7.88, "codHandling": 37.50,
"vat": 28.71, "total": 249.09
}
}
}Idempotency
A retry should never create a second parcel
Networks fail mid-request. If your client cannot tell whether a call succeeded, the safe thing must be to retry — and that is only safe if we make it safe.
- Every write accepts the header
- Use your own order id. We store the result against it for 24 hours.
- Required on money operations
- COD and settlement endpoints reject a write without one, rather than risking a double credit.
- Same key, same response
- Including the original status code, so your client logic does not have to special-case a replay.
Webhooks
We push. You do not poll.
Polling a courier API every five minutes for a status that changes twice a day wastes your infrastructure and ours. Subscribe once and receive the change when it happens.
HMAC-SHA256 signature
Over the raw body, with your endpoint secret. Verify before you trust.
At-least-once delivery
Every event carries a unique id so your handler can de-duplicate.
Exponential backoff for 24 hours
Then dead-lettered, replayable from the panel.
Subscribe per event type
Take only delivered and failed if that is all you need.
POST https://yourshop.com.np/hooks/udan
X-Udan-Signature: sha256=9f86d081884c7d659a2f...
X-Udan-Event-Id: ev_7c21f04b
{
"event": "parcel.delivered",
"occurredAt": "2026-08-23T14:32:11+05:45",
"data": {
"tracking": "UDN26H4K2M9QP",
"status": "DELIVERED",
"receivedBy": "recipient",
"codCollected": 2500
}
}Endpoints
The surface area is deliberately small
Six resources cover everything a store needs. A larger API would mostly be endpoints nobody calls.
POST /parcels
Create a shipment. Returns the tracking number, charges and promise date.
GET /parcels/{id}
Full status, charges and the complete tracking event history.
POST /rates/quote
Price a shipment without creating it. Use it to show a landed cost at checkout.
GET /tracking/{code}
Public tracking, no authentication. Safe to call from your customer-facing pages.
POST /pickups
Book a collection window, or attach parcels to an existing scheduled pickup.
GET /settlements
Weekly batches and the ledger lines behind them, for reconciliation.
Integration
An afternoon, if you have done a payment gateway
Four steps, in the order we would do them ourselves.
- 1
Get a sandbox key
Created in the merchant panel. Scoped to sandbox until you explicitly promote it.
- 2
Create a test parcel
Move it through every status from the sandbox console, including the failure paths.
- 3
Wire up webhooks
Point at a tunnel while you develop. Verify the signature before you go further.
- 4
Promote the key
Same code, live data. Nothing in your integration changes.
Sandbox
Test the failures, not just the happy path
Most integrations break on the paths nobody tested: a failed delivery, a return, a partial collection. The sandbox lets you drive a parcel into all of them on demand.
- Force any status
- Push a sandbox parcel to delivered, attempted, on hold or returned from the console.
- The same transition rules
- Illegal transitions are rejected in sandbox exactly as in production, so you build against real behaviour.
- Webhooks fire for real
- Against your endpoint, with valid signatures.
Errors
Errors that tell you which field is wrong
A 400 with a sentence in it means a developer reading logs at midnight has to guess. Validation failures come back as a list of fields with reasons.
- One envelope for every response
- Success or failure, the shape is the same. Your client parses one thing.
- Machine-readable codes
- Alongside the human message, so you can branch on the code and display the message.
- Never retry a 4xx
- The answer will not change. Our own clients treat 400, 401, 403, 404 and 422 as terminal.
Rates at checkout
Quote before you take the order
Calling the quote endpoint from your checkout means the shipping figure your customer sees is the one you will actually be charged — including surcharges and the promise date.
- Same engine as the invoice
- Quote and charge come from one implementation, so they cannot drift apart.
- Contract discount applied
- Your negotiated rate is reflected in the quote, not added later.
- Promise date returned
- Show a real delivery window at checkout instead of a vague estimate.
Public tracking
Track without making anyone log in
The tracking endpoint takes a tracking number and no credentials, so you can render live status on your own order page. Making a customer create an account to track a parcel they did not order from us is hostile.
- No authentication
- Safe to call from a browser. Rate-limited by IP rather than by key.
- Plain-language statuses
- Alongside the machine code, so you can display either.
- Full event history
- Including failed attempts and their reasons.
Security
The parts we would want to see documented
If you are about to send us customer addresses and cash amounts, you should know how they are held.
Keys scoped and rotatable
Sandbox and live are separate. Rotate a key without downtime by running two in parallel.
TLS everywhere, HSTS preloaded
No plaintext endpoint exists, including for redirects.
Signed webhook payloads
HMAC-SHA256 over the raw body. Compare in constant time.
Least-privilege API roles
A key that only creates parcels cannot read your settlements.
Audit log on every write
Which key, which IP, which payload hash.
PII minimised
We ask for what a rider needs to find a door, and nothing beyond it.
Versioning
Your integration should not break because we shipped something
The API is versioned in the path. Additive changes land on the current version; anything breaking gets a new one, and the old one keeps working for a published period.
- Additive changes are safe
- New fields may appear. Your client should ignore what it does not know.
- Twelve months of overlap
- A deprecated version keeps working for a year after its replacement ships.
- Deprecation headers
- Sunset and Deprecation headers appear on responses long before anything stops working.
Bulk
Four hundred parcels, one request
Looping a create endpoint four hundred times is how you meet a rate limit. The batch endpoint takes an array and returns per-item results, so one bad row does not fail the other 399.
- Partial success is a first-class outcome
- You get a result per item, with the successes created and the failures explained.
- One idempotency key for the batch
- Retry the whole thing safely if the connection drops mid-upload.
- Labels as one PDF
- A single follow-up call returns the print sheet for the entire batch.
No developer?
CSV import covers most of the same ground
Not every good business has an engineer. The bulk importer in the merchant panel does what the batch endpoint does, with column mapping that is remembered between uploads.
- Same validation as the API
- Bad rows are listed; the good ones are still created.
- Same data model
- Nothing is thrown away when you eventually integrate properly.
- Scheduled imports
- Drop a file on a schedule if your store can export to a folder.
Mobile
The rider app is an API client too
Every scan a rider makes is the same API your integration calls. That is deliberate: an internal shortcut would be a path where events get recorded that your webhooks never hear about.
- No private endpoints
- If the rider app can do it, the documented API can do it.
- Events fire identically
- A doorstep scan and an API status change produce the same webhook.
- Offline writes replay
- Queued on the device, de-duplicated on arrival by client event id.
Offline reality
Events can arrive out of order
A rider in a Karnali valley scans a delivery at 11:04 and syncs it at 15:20. Your handler will see it then. Design for that rather than assuming wall-clock order.
- occurredAt is the truth
- Not the delivery time of the webhook. Sort on it, never on receipt order.
- Late events are normal, not errors
- Especially on hill routes. A gap of hours is expected behaviour.
- Terminal statuses never reverse
- Delivered is final. If you see a later event, it is a correction with its own type.
Labels
Barcodes that scan on cheap hardware
Code128 at a size that reads on a Rs 8,000 Android phone in a dim warehouse, because that is what is actually on the loading bay.
- Thermal 4×6 or A4 four-up
- Whichever printer you already have.
- Human-readable underneath
- For the times somebody has to read it aloud on the phone.
- Non-sequential numbers
- So a competitor cannot infer your daily volume from two tracking codes.
Questions
API questions
What engineers ask before writing the first line.
How do I get API credentials?
What happens if my request times out?
Are there rate limits?
How are webhooks secured?
What happens if my webhook endpoint is down?
Is there a plugin for WooCommerce or Shopify?
Start in the sandbox
Open a business account, create a sandbox key, and have a test parcel moving through every status before lunch.