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
A merchant's online store connected to the Udan Express APIPOST /parcels201 CREATED

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.

POST /parcelscURL
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
    }
  }'
201 CreatedJSON
{
  "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.
A dispatch checklist being completed before a parcel leaves

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.

Webhook deliveryHTTP
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
  }
}
Status webhooks pushed from the network to a merchant's systemparcel.picked_upparcel.in_transitparcel.delivered

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. 1

    Get a sandbox key

    Created in the merchant panel. Scoped to sandbox until you explicitly promote it.

  2. 2

    Create a test parcel

    Move it through every status from the sandbox console, including the failure paths.

  3. 3

    Wire up webhooks

    Point at a tunnel while you develop. Verify the signature before you go further.

  4. 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.
The five stages a parcel passes through, from booking to deliveryBookedPicked upIn transitOut for deliveryDelivered

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.
A ledger listing cash on delivery credits and delivery chargesMERCHANT LEDGERCOD credit+2,450Delivery fee−285COD credit+1,180Payout−3,345Balanceरु 0

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.
A rate calculator estimating a delivery chargeRATE CALCULATORFROMKathmanduTOPokharaWEIGHT2.4 kgEstimatedरु 232

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.
A phone showing a parcel's live tracking timelineUDN26H4K2M9QPArriving today, by 6 pmPicked upAt Kathmandu hubIn transitOut for delivery

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.
A shield representing parcel insurance and data security

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.
A stack of parcels of different sizes ready for dispatchkg

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.
E-commerce platforms that connect to Udan ExpressWOOSHOPMAGECSVAPIZAP

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.
The Udan Express mobile app used by riders and merchantsMY ROUTE14 stops remainingWorks offline

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.
Delivery updates queued on a phone with no signal, syncing later

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.
A handheld scanner reading a parcel barcodeUDAN EXPRESSUDN26H4K2M9QP

Questions

API questions

What engineers ask before writing the first line.

How do I get API credentials?
Open a business account, then create a key in the merchant panel under Developers. Keys are scoped to sandbox until you explicitly promote them to live, so it is not possible to create a real parcel while you are still testing.
What happens if my request times out?
Retry it with the same Idempotency-Key header. The second request returns the parcel created by the first rather than making another one. Every write endpoint accepts the header, and on money-moving operations it is required rather than optional.
Are there rate limits?
Yes — 600 requests a minute per key on read endpoints and 120 a minute on writes. The remaining budget is returned in response headers on every call, so a well-behaved client never has to guess. Bulk operations should use the batch endpoint rather than looping.
How are webhooks secured?
Every payload carries an HMAC-SHA256 signature over the raw body in the X-Udan-Signature header, computed with your endpoint secret. Verify it before trusting the body, and compare with a constant-time function rather than string equality.
What happens if my webhook endpoint is down?
We retry with exponential backoff for 24 hours, then move the event to a dead-letter queue you can replay from the panel. Events are delivered at least once, so your handler needs to be idempotent — every payload carries a unique event id for that purpose.
Is there a plugin for WooCommerce or Shopify?
Not yet — it is on the roadmap. Until then the REST API is a straightforward integration for anyone who has connected a payment gateway, and the bulk CSV importer covers teams without a developer at all.

Start in the sandbox

Open a business account, create a sandbox key, and have a test parcel moving through every status before lunch.