Getting started

Hubbazzar Shipping API

Everything you can do in the Shipping Panel — book shipments with Delhivery, Shadowfax, Amazon Shipping and Nimbus Post, print labels, track parcels, handle NDRs, cancel with automatic refunds, read your wallet and COD remittances, and receive real-time webhooks — over one JSON REST API.

Base URL
https://hubbazzar.com/api/v1/shipping
Auth
Authorization: Bearer <token>
Rate limit
600 requests / minute
⬇ Postman collection ⬇ OpenAPI 3 (Swagger) Import either file to get every endpoint pre-filled, or generate a client in any language.

Your first shipment in 5 steps

  1. 1. Get a token. Panel → API Settings → generate token (or POST /auth/login). Export it: export TOKEN=…
  2. 2. Add a pickup warehouse. POST /warehouses → note its pick_address_id. (Nimbus Post users: also verify the OTP once.)
  3. 3. Pick a courier. GET /couriers?express_type=surface&weight=0.5&payment_mode=COD&order_amount=1499 → choose a courier_code.
  4. 4. Create the shipment. POST /shipments with the consignee, pick_address_id and courier_code → you get the AWB (waybill) instantly.
  5. 5. Print & track. GET /shipments/{id}/label → PDF. Track via your webhook (order.status_updated) or GET /shipments/{id}/track?sync=1.
# 1 — set your token export TOKEN="paste-your-token" BASE="https://hubbazzar.com/api/v1/shipping" # 2 — list your warehouses curl -s "$BASE/warehouses" -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" # 3 — which couriers can carry a 0.5 kg COD parcel? curl -s "$BASE/couriers?express_type=surface&weight=0.5&payment_mode=COD&order_amount=1499" \ -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" # 4 — create the shipment (see "Create shipment" for every field) curl -s -X POST "$BASE/shipments" -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" -H "Content-Type: application/json" \ -d '{"client_order_id":"MY-ORD-1","consignee_name":"Rahul Sharma","consignee_mobile":"9876543210", "consignee_address1":"Flat 12, Green Park","consignee_pincode":"122001","consignee_city":"Gurugram", "consignee_state":"Haryana","payment_mode":"COD","order_amount":1499,"pick_address_id":"Main Warehouse", "express_type":"surface","courier_code":"Delhivery","shipment_weight":0.5}' # 5 — download the label curl -s "$BASE/shipments/1024/label" -H "Authorization: Bearer $TOKEN" -o label.pdf

Authentication

Send a Bearer token on every request (except /auth/login) and ask for JSON:

Authorization: Bearer 12|AbCdEfGhIjKlMnOp… Accept: application/json
  • Static token — generate in Panel → API Settings. Best for a backend service. Regenerating it invalidates the old one.
  • Login token — POST /auth/login. Revocable with /auth/logout, replaceable with /auth/token/rotate.
  • Keep tokens server-side. Never ship them in a browser or mobile app.
  • Every call only ever sees your own warehouses, shipments, wallet and NDRs.

Integration workflow

How a typical store/ERP uses the API from checkout to settlement:

StageWhat you doEndpoint(s)
Setup (once)Store pickup addresses; verify Nimbus Post OTP if used; set webhookPOST /warehouses, /warehouses/{id}/nimbuspost/*, POST /webhook
CheckoutCheck delivery is possible & quote priceGET /pincode/{pin}, GET /couriers, POST /calculate-rate
FulfilmentBook, print label, hand overPOST /shipments (or /bulk), GET /shipments/{id}/label, POST /shipments/labels
In transitKeep customer informedWebhook order.status_updated, GET /shipments/{id}/track
ExceptionsFailed delivery? choose action. Changed mind? cancel.GET /ndr, POST /ndr/{id}/action, POST /shipments/{id}/cancel
MoneyWatch balance; reconcile CODGET /wallet, /wallet/transactions, /cod-remittances
Wallet: shipping is prepaid from your wallet. A booking is rejected with 402 if wallet_balance + approved credit is below the charge. Cancelling refunds the freight to the wallet. Recharge from the panel (Wallet → Add Money).

Couriers guide

One API, four couriers. You never call the courier yourself — pick a courier_code from GET /couriers and the API handles the courier-specific payloads, labels, tracking, cancellation and NDR calls.

CourierBefore you shipNotesLabel
DelhiveryWarehouse is registered automatically on create/sync.Bulk tracking, NDR actions, pincode serviceability (this API's pincode endpoints use Delhivery data).Label generated from your label template.
ShadowfaxUses your warehouse pick_address_id as the pickup code; phone is sanitised to digits.Weight is sent in grams automatically. NDR actions supported.Label generated from your label template.
Amazon ShippingAddresses are split into ≤60-char lines automatically; add consignee_email.Cancellation uses Amazon's shipment id (stored for you). NDR actions supported.Amazon's own label PDF is stored at booking and returned as-is.
Nimbus PostWarehouse must be OTP-verified once (see Warehouses → Nimbus Post).Cancellation by AWB. NDR actions supported.Label generated from your label template.
  • courier_code values are specific to your account and chosen express_type — always read them from GET /couriers rather than hard-coding.
  • A courier is offered only when your rate card has a slab for exactly the weight you ask about; use a weight that appears in GET /rate-card (weight_slabs).
  • If the courier does not return an AWB the order is not created and nothing is charged.
  • Tracking, cancellation, labels and NDR actions are routed automatically from the shipment's courier — the same endpoints work for all four.

Errors, limits & pagination

Response envelope

{ "success": true, "message": "…", "data": { … } } { "success": false, "message": "Human-readable reason", "errors": { "field": ["…"] } }
HTTPMeaningWhat to do
200/201OK / created—
401Missing or invalid tokenCheck the Authorization header; regenerate if needed.
402Insufficient wallet balanceRecharge, then retry.
403Account inactive / not allowedContact support.
404Not found (or not yours)Check the id.
409Duplicate client_order_idTreat as "already booked"; use the returned shipment.
422Validation or courier rejectionFix the input — message/errors say what.
429Rate limit (600/min)Back off using Retry-After.
5xxServer or courier outageRetry with the same client_order_id — duplicates are safe.

Pagination

List endpoints take per_page (max 100, default 20) and page, and return:

{ "data": { "items": [ … ], "total": 250, "per_page": 20, "current_page": 1, "last_page": 13 } }

Idempotency

Always send your own client_order_id on POST /shipments. If a network error leaves you unsure whether the order was booked, repeat the call: you'll get 409 with the existing shipment instead of a second booking and a second charge.

Endpoints

Authentication

POST /auth/login Login (get token)

Exchange your panel email + password for a Bearer token. Use this for server-to-server integrations that prefer revocable tokens; otherwise copy the static token from Panel → API Settings.

No token required.

Parameters

NameInTypeDescription
email body string required Panel login email. e.g. user@example.com
password body string required Panel password. e.g. secret
device_name body string optional Label for this token. Logging in again with the same name replaces the old token. e.g. my-erp

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/auth/login" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "secret",
    "device_name": "my-erp"
}'

Response 200

{
    "success": true,
    "message": "Login successful.",
    "data": {
        "token": "12|AbCdEf…",
        "token_type": "Bearer",
        "user": {
            "id": 42,
            "name": "Acme Store",
            "email": "user@example.com",
            "wallet_balance": 1500,
            "wallet_credit": 0,
            "status": "active"
        }
    }
}

Errors

  • 401 — Invalid credentials.
  • 403 — Account inactive.
  • 422 — Validation failed.
GET /auth/profile Get profile

Returns the account the token belongs to. A cheap way to verify a token works.

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/auth/profile" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "id": 42,
        "name": "Acme Store",
        "email": "user@example.com",
        "phone": "9811122233",
        "wallet_balance": 1500,
        "wallet_credit": 0,
        "status": "active",
        "created_at": "2026-01-10 09:00:00"
    }
}
POST /auth/token/rotate Rotate token

Revokes the token used for this call and returns a new one. Store the new token immediately — the old one stops working at once.

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/auth/token/rotate" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "message": "Token rotated successfully.",
    "data": {
        "token": "13|XyZ…",
        "token_type": "Bearer"
    }
}
POST /auth/logout Logout (revoke token)

Revokes the token used for this call.

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/auth/logout" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "message": "Logged out successfully."
}

Warehouses (pickup addresses)

GET /warehouses List warehouses

All your pickup/return addresses. pick_address_id is what you pass when creating shipments.

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/warehouses" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": [
        {
            "id": 7,
            "address_title": "Main Warehouse",
            "sender_name": "Acme Store",
            "full_address": "Plot 4, Industrial Area Phase 2",
            "phone": "9811122233",
            "email": "ops@acme.com",
            "pincode": "110020",
            "city": "New Delhi",
            "state": "Delhi",
            "pick_address_id": "Main Warehouse",
            "delhivery_synced": true,
            "nimbuspost_synced": false,
            "created_at": "2026-09-01 10:00:00"
        }
    ]
}
POST /warehouses Create warehouse

Saves the address and registers it with Delhivery. If the courier call fails the warehouse is still saved and the response carries warning: true — retry with the sync endpoint.

How to use

  1. Create each pickup location once, then reuse its pick_address_id.
  2. Check delhivery_synced in the response (or GET /warehouses/{id}); only synced warehouses can book Delhivery shipments.
  3. For Nimbus Post, also run the two OTP steps below once per warehouse.

Parameters

NameInTypeDescription
address_title body string required Unique name for the pickup point (max 100). Becomes pick_address_id. e.g. Main Warehouse
sender_name body string required Contact / seller name printed on labels (max 100). e.g. Acme Store
full_address body string required Street address, without city/state/pincode. e.g. Plot 4, Industrial Area Phase 2
phone body string required 10–15 digits, no spaces or +. e.g. 9811122233
email body string optional Pickup contact email.
pincode body string required 6 digits. e.g. 110020
city body string required City. e.g. New Delhi
state body string required State. e.g. Delhi

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/warehouses" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "address_title": "Main Warehouse",
    "sender_name": "Acme Store",
    "full_address": "Plot 4, Industrial Area Phase 2",
    "phone": "9811122233",
    "email": "ops@acme.com",
    "pincode": "110020",
    "city": "New Delhi",
    "state": "Delhi"
}'

Response 201

{
    "success": true,
    "message": "Warehouse created and synced with courier.",
    "data": {
        "id": 7,
        "address_title": "Main Warehouse",
        "sender_name": "Acme Store",
        "full_address": "Plot 4, Industrial Area Phase 2",
        "phone": "9811122233",
        "email": "ops@acme.com",
        "pincode": "110020",
        "city": "New Delhi",
        "state": "Delhi",
        "pick_address_id": "Main Warehouse",
        "delhivery_synced": true,
        "nimbuspost_synced": false,
        "created_at": "2026-09-01 10:00:00"
    }
}

Errors

  • 422 — Validation failed.
GET /warehouses/{id} Get warehouse

Parameters

NameInTypeDescription
id path integer required Warehouse id. e.g. 7

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/warehouses/7" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "id": 7,
        "address_title": "Main Warehouse",
        "sender_name": "Acme Store",
        "full_address": "Plot 4, Industrial Area Phase 2",
        "phone": "9811122233",
        "email": "ops@acme.com",
        "pincode": "110020",
        "city": "New Delhi",
        "state": "Delhi",
        "pick_address_id": "Main Warehouse",
        "delhivery_synced": true,
        "nimbuspost_synced": false,
        "created_at": "2026-09-01 10:00:00"
    }
}

Errors

  • 404 — Warehouse not found.
PUT /warehouses/{id} Update warehouse

Updates the address on our side only. Changes are not pushed to the courier automatically — call the sync endpoint afterwards.

Parameters

NameInTypeDescription
id path integer required Warehouse id. e.g. 7
address_title body string required Unique name for the pickup point (max 100). Becomes pick_address_id. e.g. Main Warehouse
sender_name body string required Contact / seller name printed on labels (max 100). e.g. Acme Store
full_address body string required Street address, without city/state/pincode. e.g. Plot 4, Industrial Area Phase 2
phone body string required 10–15 digits, no spaces or +. e.g. 9811122233
email body string optional Pickup contact email.
pincode body string required 6 digits. e.g. 110020
city body string required City. e.g. New Delhi
state body string required State. e.g. Delhi

Code samples

cURLPHPNode.jsPython
Copy
curl -X PUT "https://hubbazzar.com/api/v1/shipping/warehouses/7" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "address_title": "Main Warehouse",
    "sender_name": "Acme Store",
    "full_address": "Plot 4, Industrial Area Phase 2",
    "phone": "9811122233",
    "email": "ops@acme.com",
    "pincode": "110020",
    "city": "New Delhi",
    "state": "Delhi"
}'

Response 200

{
    "success": true,
    "message": "Warehouse updated. Note: Changes are NOT automatically synced to the courier. Use the /sync endpoint if needed.",
    "data": {
        "id": 7,
        "address_title": "Main Warehouse",
        "sender_name": "Acme Store",
        "full_address": "Plot 4, Industrial Area Phase 2",
        "phone": "9811122233",
        "email": "ops@acme.com",
        "pincode": "110020",
        "city": "New Delhi",
        "state": "Delhi",
        "pick_address_id": "Main Warehouse",
        "delhivery_synced": true,
        "nimbuspost_synced": false,
        "created_at": "2026-09-01 10:00:00"
    }
}

Errors

  • 404 — Warehouse not found.
  • 422 — Validation failed.
DELETE /warehouses/{id} Delete warehouse

Removes the address from your account. Existing shipments are not affected.

Parameters

NameInTypeDescription
id path integer required Warehouse id. e.g. 7

Code samples

cURLPHPNode.jsPython
Copy
curl -X DELETE "https://hubbazzar.com/api/v1/shipping/warehouses/7" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "message": "Warehouse deleted successfully."
}
POST /warehouses/{id}/sync Sync warehouse with Delhivery

Re-registers the warehouse with Delhivery. Use after a failed create or after editing the address.

Parameters

NameInTypeDescription
id path integer required Warehouse id. e.g. 7

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/warehouses/7/sync" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "message": "Warehouse synced with courier successfully.",
    "data": {
        "id": 7,
        "address_title": "Main Warehouse",
        "sender_name": "Acme Store",
        "full_address": "Plot 4, Industrial Area Phase 2",
        "phone": "9811122233",
        "email": "ops@acme.com",
        "pincode": "110020",
        "city": "New Delhi",
        "state": "Delhi",
        "pick_address_id": "Main Warehouse",
        "delhivery_synced": true,
        "nimbuspost_synced": false,
        "created_at": "2026-09-01 10:00:00"
    }
}

Errors

  • 502 — Courier sync failed — message explains why.
POST /warehouses/{id}/nimbuspost/send-otp Nimbus Post — send OTP (step 1)

Nimbus Post verifies the pickup phone by OTP before accepting a warehouse. This sends the OTP to the warehouse phone.

How to use

  1. Needed once per warehouse, only if you ship with Nimbus Post.
  2. Call this endpoint, read the OTP from the SMS, then call step 2.

Parameters

NameInTypeDescription
id path integer required Warehouse id. e.g. 7

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/warehouses/7/nimbuspost/send-otp" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "message": "OTP sent to 9811122233."
}

Errors

  • 404 — Warehouse not found.
  • 422 — Nimbus Post OTP send failed.
POST /warehouses/{id}/nimbuspost/sync Nimbus Post — verify OTP & register (step 2)

Parameters

NameInTypeDescription
id path integer required Warehouse id. e.g. 7
otp body string required OTP received on the warehouse phone. e.g. 482913

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/warehouses/7/nimbuspost/sync" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "otp": "482913"
}'

Response 200

{
    "success": true,
    "message": "Warehouse synced with Nimbus Post."
}

Errors

  • 422 — OTP verification or registration failed.

Couriers, Rates & Serviceability

GET /couriers List available couriers + price

The same list the panel shows on "Create Order". Returns every courier you can book for a given mode and weight, with the exact amount that will be debited. Use the returned courier_code in POST /shipments.

How to use

  1. Call it with the parcel's express_type, weight, payment_mode and order_amount.
  2. Show the options to your user (or pick the cheapest total) and keep the chosen courier_code.
  3. A courier only appears when your rate card has a slab for exactly that weight. Empty list = no rate card / no slab for that weight.

Parameters

NameInTypeDescription
express_type query enum optional surface (default) or air. e.g. surface
weight query number optional KG, default 0.5. e.g. 0.5
payment_mode query enum optional Prepaid (default) or COD. e.g. COD
order_amount query number optional Needed for COD % fee. e.g. 1499

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/couriers?express_type=surface&weight=0.5&payment_mode=COD&order_amount=1499" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "express_type": "surface",
        "weight_kg": 0.5,
        "couriers": [
            {
                "courier_name": "Delhivery",
                "courier_code": "Delhivery",
                "weight_slab": "0.5 KG",
                "freight": 60,
                "cod_charge": 30,
                "cod_percentage": 0,
                "gst": 16.2,
                "total": 106.2
            }
        ]
    },
    "note": "Only couriers with an active rate-card slab for exactly this weight are listed."
}
GET /rate-card Get rate card

Your full active rate card(s): weight slabs, zones, forward/RTO rates and COD fees, grouped by courier.

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/rate-card" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": [
        {
            "rate_card_id": 3,
            "courier": "Delhivery",
            "name": "Delhivery",
            "weight_slabs": [
                {
                    "id": 1,
                    "label": "0.5 KG",
                    "weight": 0.5
                }
            ],
            "zones": [
                {
                    "id": 1,
                    "zone_name": "Zone A"
                }
            ],
            "rates": {
                "surface": {
                    "forward": {
                        "0.5 KG": {
                            "Zone A": {
                                "base_rate": 60,
                                "additional_rate": 50
                            }
                        }
                    }
                }
            },
            "cod_rates": {
                "surface": {
                    "forward": {
                        "All Slabs": {
                            "cod_charge": 30,
                            "cod_percentage": 0
                        }
                    }
                }
            }
        }
    ]
}
POST /calculate-rate Calculate rate

Price one courier for one parcel. Weights above the largest slab are charged per extra 0.5 KG.

Parameters

NameInTypeDescription
courier_code body string required Courier code or name. e.g. Delhivery
weight body number required KG, min 0.1. e.g. 0.5
express_type body enum required surface or air. e.g. surface
payment_mode body enum required Prepaid or COD. e.g. COD
order_amount body number optional Required for COD. e.g. 1499

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/calculate-rate" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "courier_code": "Delhivery",
    "weight": 0.5,
    "express_type": "surface",
    "payment_mode": "COD",
    "order_amount": 1499
}'

Response 200

{
    "success": true,
    "data": {
        "courier": "Delhivery",
        "weight_kg": 0.5,
        "mode": "surface",
        "payment_mode": "COD",
        "slab_matched": "0.5 KG",
        "breakdown": {
            "base_freight": 60,
            "cod_charge": 30,
            "taxable": 90,
            "gst_18pct": 16.2,
            "total_charge": 106.2
        },
        "note": "All amounts in INR. GST @ 18% is included in the total."
    }
}

Errors

  • 404 — No rate card / slab / rate entry for that courier or weight.
  • 422 — Validation failed.
GET /pincode/{pincode} Check pincode serviceability

Delhivery serviceability for a pincode (prepaid / COD / pickup / reverse). Use it as a pre-check before offering shipping at checkout.

Parameters

NameInTypeDescription
pincode path string required 6 digits. e.g. 122001

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/pincode/122001" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "pincode": "122001",
    "success": true,
    "serviceable": true,
    "message": "Serviceable"
}

Errors

  • 422 — Invalid pincode.
POST /pincode/bulk Bulk pincode check

Parameters

NameInTypeDescription
pincodes body array required 1–50 six-digit pincodes.

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/pincode/bulk" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "pincodes": [
        "110001",
        "400001"
    ]
}'

Response 200

{
    "success": true,
    "data": {
        "total": 2,
        "serviceable": 2,
        "not_serviceable": 0,
        "results": [
            {
                "pincode": "110001",
                "success": true,
                "serviceable": true
            }
        ]
    }
}

Shipments

POST /shipments Create shipment

Books the shipment with the courier, debits your wallet and returns the AWB. This runs the exact same logic as the panel's Create Order, so wallet/credit checks, commission and courier rules behave identically.

How to use

  1. Have a synced warehouse (pick_address_id) — see Warehouses.
  2. Call GET /couriers for this weight/mode to get a valid courier_code and the price.
  3. POST /shipments. On success you get waybill (AWB) immediately and the wallet is debited shipping_charge.total.
  4. Fetch the label with GET /shipments/{id}/label and give the parcel to the courier.
  5. Track with webhooks (recommended) or GET /shipments/{id}/track.
  6. No AWB from the courier means no order and no charge — the call fails with 422.
  7. Safe retries: always send your own client_order_id. If the first attempt actually succeeded, the retry returns 409 with the existing shipment instead of booking twice.

Parameters

NameInTypeDescription
consignee_name body string required Receiver name (max 200). e.g. Rahul Sharma
consignee_mobile body string required Receiver phone, 10 digits (max 15 chars). e.g. 9876543210
consignee_address1 body string required Address line 1. e.g. Flat 12, Green Park
consignee_address2 body string optional Address line 2 / landmark.
consignee_pincode body string required Exactly 6 digits. e.g. 122001
consignee_city body string optional Strongly recommended — some couriers reject blank city. e.g. Gurugram
consignee_state body string optional Strongly recommended — some couriers reject blank state. e.g. Haryana
consignee_email body string optional Valid email. Recommended for Amazon Shipping.
payment_mode body enum required Prepaid or COD (case-sensitive). e.g. COD
order_amount body number required Invoice value in INR. For COD this is also the amount collected. e.g. 1499
pick_address_id body string required pick_address_id of one of your warehouses (GET /warehouses). e.g. Main Warehouse
return_address_id body string optional Warehouse to receive RTOs. Defaults to the pickup warehouse.
express_type body enum required surface or air. Must match the courier option you pick. e.g. surface
courier_code body string required A courier_code from GET /couriers for this express_type and weight. e.g. Delhivery
shipment_weight body number required Dead weight in KG, min 0.1. Must match a slab on your rate card. e.g. 0.5
shipment_length body number optional cm. e.g. 20
shipment_width body number optional cm. e.g. 15
shipment_height body number optional cm. e.g. 10
client_order_id body string optional Your own order number (max 100). Must be unique among your non-cancelled shipments — a duplicate returns 409, which makes retries safe. Auto-generated (API-XXXXXXXX) when omitted. e.g. MY-ORD-2001
invoice_number body string optional Printed on label/invoice (max 100).
address_type body enum optional Home, Office or Other. Default Home.
products body array optional Items: product_name (required per item), product_quantity, product_price, sku. A single "Product" line is used when omitted.

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/shipments" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "client_order_id": "MY-ORD-2001",
    "invoice_number": "INV-2001",
    "consignee_name": "Rahul Sharma",
    "consignee_mobile": "9876543210",
    "consignee_email": "rahul@example.com",
    "consignee_address1": "Flat 12, Green Park Apartments",
    "consignee_address2": "Sector 15",
    "consignee_pincode": "122001",
    "consignee_city": "Gurugram",
    "consignee_state": "Haryana",
    "address_type": "Home",
    "payment_mode": "COD",
    "order_amount": 1499,
    "pick_address_id": "Main Warehouse",
    "return_address_id": "Main Warehouse",
    "express_type": "surface",
    "courier_code": "Delhivery",
    "shipment_weight": 0.5,
    "shipment_length": 20,
    "shipment_width": 15,
    "shipment_height": 10,
    "products": [
        {
            "product_name": "Cotton T-Shirt",
            "product_quantity": 2,
            "product_price": 749.5,
            "sku": "TSH-001"
        }
    ]
}'

Response 201

{
    "success": true,
    "message": "Shipment created successfully.",
    "data": {
        "order_id": 1024,
        "client_order_id": "MY-ORD-2001",
        "waybill": "1234567891",
        "courier_code": "Delhivery",
        "order_status": "manifested",
        "shipping_charge": {
            "base": 60,
            "cod": 30,
            "gst": 16.2,
            "total": 106.2
        },
        "wallet_balance": 1393.8
    }
}

Errors

  • 402 — Insufficient wallet balance (wallet + approved credit). data.required_amount / available_amount included.
  • 403 — Account type cannot create shipments via API.
  • 409 — client_order_id already exists (existing shipment returned in data).
  • 422 — Validation failed, unknown/inactive courier_code, unknown pickup or return address, or the courier rejected the order (message explains why).
POST /shipments/bulk Bulk create (up to 50)

Creates many shipments in one call. Each order is processed independently with the same rules as single create; one failure does not stop the rest.

How to use

  1. Send orders: an array of up to 50 objects, each with the same fields as Create shipment.
  2. Read data.succeeded and data.failed (each failure has index + reason). HTTP 201 if at least one order was created, 422 if none.
  3. Always set client_order_id on each order so you can match results and retry only the failed ones.

Parameters

NameInTypeDescription
orders body array required 1–50 order objects (same schema as Create shipment).

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/shipments/bulk" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "orders": [
        {
            "client_order_id": "MY-ORD-2001",
            "invoice_number": "INV-2001",
            "consignee_name": "Rahul Sharma",
            "consignee_mobile": "9876543210",
            "consignee_email": "rahul@example.com",
            "consignee_address1": "Flat 12, Green Park Apartments",
            "consignee_address2": "Sector 15",
            "consignee_pincode": "122001",
            "consignee_city": "Gurugram",
            "consignee_state": "Haryana",
            "address_type": "Home",
            "payment_mode": "COD",
            "order_amount": 1499,
            "pick_address_id": "Main Warehouse",
            "return_address_id": "Main Warehouse",
            "express_type": "surface",
            "courier_code": "Delhivery",
            "shipment_weight": 0.5,
            "shipment_length": 20,
            "shipment_width": 15,
            "shipment_height": 10,
            "products": [
                {
                    "product_name": "Cotton T-Shirt",
                    "product_quantity": 2,
                    "product_price": 749.5,
                    "sku": "TSH-001"
                }
            ]
        }
    ]
}'

Response 201

{
    "success": true,
    "message": "1 order(s) created, 1 failed.",
    "data": {
        "succeeded": [
            {
                "order_id": 1024,
                "client_order_id": "MY-ORD-2001",
                "waybill": "1234567891"
            }
        ],
        "failed": [
            {
                "index": 1,
                "client_order_id": "MY-ORD-2002",
                "reason": "Pickup address not found. Please use a valid pick_address_id from your warehouses."
            }
        ]
    }
}
GET /shipments List shipments

Parameters

NameInTypeDescription
status query string optional Exact order_status (see Status reference). e.g. in_transit
payment_mode query enum optional Prepaid / COD.
waybill query string optional Exact AWB.
client_order_id query string optional Partial match.
date_from query date optional YYYY-MM-DD.
date_to query date optional YYYY-MM-DD.
per_page query integer optional Max 100, default 20.
page query integer optional Page number.

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/shipments?status=in_transit&per_page=20" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "items": [
            {
                "id": 1024,
                "client_order_id": "MY-ORD-2001",
                "waybill": "1234567891",
                "order_status": "in_transit",
                "payment_mode": "COD",
                "order_amount": 1499,
                "cod_amount": 1499,
                "shipping_charge": 70.8,
                "courier_code": "Delhivery",
                "express_type": "surface",
                "pick_address_id": "Main Warehouse",
                "created_at": "2026-10-01 11:20:05"
            }
        ],
        "total": 1,
        "per_page": 20,
        "current_page": 1,
        "last_page": 1
    }
}
GET /shipments/{id} Get shipment

Full details of one shipment (consignee, dimensions, products, charges, refund and delivery info). id is the order_id returned at creation.

Parameters

NameInTypeDescription
id path integer required order_id. e.g. 1024

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/shipments/1024" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "id": 1024,
        "client_order_id": "MY-ORD-2001",
        "waybill": "1234567891",
        "order_status": "in_transit",
        "payment_mode": "COD",
        "order_amount": 1499,
        "cod_amount": 1499,
        "shipping_charge": 70.8,
        "courier_code": "Delhivery",
        "express_type": "surface",
        "pick_address_id": "Main Warehouse",
        "created_at": "2026-10-01 11:20:05",
        "invoice_number": "INV-2001",
        "consignee_name": "Rahul Sharma",
        "consignee_mobile": "9876543210",
        "consignee_address1": "Flat 12, Green Park",
        "consignee_address2": "Sector 15",
        "consignee_pincode": "122001",
        "consignee_city": "Gurugram",
        "consignee_state": "Haryana",
        "shipment_weight": 0.5,
        "shipment_length": 20,
        "shipment_width": 15,
        "shipment_height": 10,
        "products": [
            {
                "product_name": "Cotton T-Shirt",
                "product_quantity": 2
            }
        ],
        "extra_charges": 30,
        "tax_amount": 16.2,
        "refunded_amount": 0,
        "cancel_reason": null,
        "cancelled_at": null,
        "picked_up_at": null,
        "delivered_at": null,
        "label_printed": false,
        "channel": "api",
        "updated_at": "2026-10-01 11:20:05"
    }
}

Errors

  • 404 — Shipment not found.
GET /shipments/{id}/track Track shipment

Status + scan history. Works for every courier (Delhivery, Shadowfax, Amazon Shipping, Nimbus Post) and returns the same shape. Without sync you get cached data (refreshed by courier webhooks); with sync=1 we fetch live from the courier first.

How to use

  1. Prefer webhooks (order.status_updated) for real-time updates and use this for on-demand lookups.
  2. Pass sync=1 to force a live refresh — this also updates the order status, NDR flags and delivery date.
  3. If the live call fails you still get the last cached data plus data.sync_warning.
  4. Event fields event_date, status_code, status_description, status_location are present for every courier; Delhivery events carry extra scan fields.

Parameters

NameInTypeDescription
id path integer required order_id. e.g. 1024
sync query boolean optional 1 = fetch live from the courier first. e.g. 1

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/shipments/1024/track?sync=1" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "order_id": 1024,
        "waybill": "1234567891",
        "courier_code": "Delhivery",
        "order_status": "in_transit",
        "is_ndr": false,
        "tracking": {
            "status": true,
            "synced_at": "2026-10-02 10:15:00",
            "current_status": {
                "status_code": "in_transit",
                "scan_type": "UD",
                "status_description": "Shipment in transit",
                "status_location": "Gurugram_Hub (Haryana)",
                "event_date": "2026-10-02T09:40:00"
            },
            "tracking_events": [
                {
                    "event_date": "2026-10-02T09:40:00",
                    "status_code": "in_transit",
                    "scan_type": "UD",
                    "status_location": "Gurugram_Hub (Haryana)",
                    "status_description": "Shipment in transit"
                },
                {
                    "event_date": "2026-10-01T18:05:00",
                    "status_code": "manifested",
                    "scan_type": "UD",
                    "status_location": "Delhi_Hub (Delhi)",
                    "status_description": "Pickup done"
                }
            ],
            "estimated_delivery_date": "2026-10-04"
        }
    }
}

Errors

  • 404 — Shipment not found.
GET /shipments/{id}/label Download label (PDF)

Returns the shipping label as a PDF file (Content-Type: application/pdf). It is the same document the panel prints — Amazon Shipping's own label for Amazon orders, your configured label template for the rest. Save the response body to a file.

Parameters

NameInTypeDescription
id path integer required order_id. e.g. 1024
format query string optional url returns JSON with a panel link instead of the PDF (needs a panel login to open).

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/shipments/1024/label" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -o document.pdf

Response 200

Content-Type: application/pdf (binary PDF — save the response body to a file)

Errors

  • 404 — Shipment not found.
  • 422 — No AWB assigned yet.
  • 502 — Label could not be generated.
POST /shipments/labels Bulk labels (one merged PDF)

Up to 100 labels merged into a single PDF. Orders without an AWB are skipped; the number included is in the X-Label-Count header.

Parameters

NameInTypeDescription
order_ids body array required 1–100 order ids.

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/shipments/labels" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "order_ids": [
        1024,
        1025
    ]
}' \
  -o document.pdf

Response 200

Content-Type: application/pdf (binary PDF — save the response body to a file)

Errors

  • 404 — None of the orders were found or have an AWB.
  • 422 — Validation failed.
GET /shipments/{id}/invoice Download invoice (PDF)

The order invoice rendered with your invoice settings.

Parameters

NameInTypeDescription
id path integer required order_id. e.g. 1024

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/shipments/1024/invoice" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -o document.pdf

Response 200

Content-Type: application/pdf (binary PDF — save the response body to a file)

Errors

  • 404 — Shipment not found.
POST /shipments/{id}/cancel Cancel shipment

Cancels with the courier first; only if the courier accepts is the order cancelled locally and the freight refunded to your wallet.

How to use

  1. Works until the courier picks the parcel up / the shipment reaches a terminal state.
  2. A courier refusal returns 422 and leaves the order untouched — nothing is refunded.
  3. On success the order.cancelled webhook fires.

Parameters

NameInTypeDescription
id path integer required order_id. e.g. 1024
reason body string optional Stored as the cancel reason. e.g. Customer changed mind

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/shipments/1024/cancel" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Customer changed mind"
}'

Response 200

{
    "success": true,
    "message": "Shipment cancelled successfully.",
    "data": {
        "order_id": 1024,
        "waybill": "1234567891",
        "refunded_amount": 106.2,
        "wallet_balance": 1500,
        "courier_cancel": true
    }
}

Errors

  • 404 — Shipment not found.
  • 422 — Not cancellable, or the courier rejected the cancellation.
POST /shipments/bulk-cancel Bulk cancel (up to 50)

Parameters

NameInTypeDescription
order_ids body array required 1–50 order ids.
reason body string optional Cancel reason.

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/shipments/bulk-cancel" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "order_ids": [
        1024,
        1025
    ],
    "reason": "Out of stock"
}'

Response 200

{
    "success": true,
    "message": "1 order(s) cancelled, 1 failed.",
    "data": {
        "succeeded": [
            {
                "order_id": 1024,
                "waybill": "1234567891",
                "refunded_amount": 106.2
            }
        ],
        "failed": [
            {
                "order_id": 1025,
                "reason": "Order is not cancellable (status: delivered)"
            }
        ],
        "wallet_balance": 1500
    }
}

NDR (Non-Delivery Reports)

GET /ndr List NDR shipments

Shipments whose last delivery attempt failed and are waiting for your decision.

How to use

  1. Poll this, or react to order.status_updated webhooks where is_ndr is true.
  2. Pick an action per shipment (re-attempt or reschedule) using the action endpoints.

Parameters

NameInTypeDescription
action_status query enum optional pending (no action yet) or actioned. e.g. pending
nsl_code query string optional Filter by NSL reason code.
date_from query date optional NDR date from.
date_to query date optional NDR date to.
per_page query integer optional Max 100.

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/ndr?action_status=pending" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "items": [
            {
                "id": 1030,
                "client_order_id": "MY-ORD-2007",
                "waybill": "1234567899",
                "courier_code": "Delhivery",
                "consignee_name": "Rahul Sharma",
                "consignee_mobile": "9876543210",
                "consignee_city": "Gurugram",
                "consignee_pincode": "122001",
                "order_status": "pending",
                "is_ndr": true,
                "ndr_reason": "Consignee unavailable",
                "nsl_code": "EOD-6",
                "ndr_at": "2026-10-02 12:00:00",
                "ndr_action_type": null,
                "ndr_upl_id": null,
                "ndr_action_at": null,
                "payment_mode": "COD",
                "order_amount": 1499
            }
        ],
        "total": 1,
        "per_page": 20,
        "current_page": 1,
        "last_page": 1
    }
}
POST /ndr/{id}/action Submit NDR action

Tells the courier what to do. Routed automatically to the shipment's courier (Delhivery, Shadowfax, Amazon Shipping, Nimbus Post).

Parameters

NameInTypeDescription
id path integer required order_id. e.g. 1030
action body enum required RE-ATTEMPT (deliver again) or PICKUP_RESCHEDULE. e.g. RE-ATTEMPT
remark body string optional Free text (max 500).

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/ndr/1030/action" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "RE-ATTEMPT"
}'

Response 200

{
    "success": true,
    "message": "NDR action submitted successfully.",
    "data": {
        "order_id": 1030,
        "waybill": "1234567899",
        "action": "RE-ATTEMPT",
        "upl_id": "UPL-8891",
        "action_at": "2026-10-02 14:00:00"
    }
}

Errors

  • 404 — Order not found.
  • 422 — No active NDR / no AWB / courier rejected the action.
POST /ndr/bulk-action Bulk NDR action (up to 100)

Parameters

NameInTypeDescription
order_ids body array required 1–100 order ids.
action body enum required RE-ATTEMPT or PICKUP_RESCHEDULE. e.g. RE-ATTEMPT

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/ndr/bulk-action" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "order_ids": [
        1030,
        1031
    ],
    "action": "RE-ATTEMPT"
}'

Response 200

{
    "success": true,
    "message": "1 action(s) submitted, 1 failed.",
    "data": {
        "succeeded": [
            {
                "order_id": 1030,
                "waybill": "1234567899",
                "upl_id": "UPL-8891"
            }
        ],
        "failed": [
            {
                "order_id": 1031,
                "waybill": "1234567800",
                "reason": "API error"
            }
        ]
    }
}
POST /ndr/sync Refresh NDR status

Re-checks your open shipments with their couriers and updates NDR flags. Delhivery shipments are refreshed in bulk; for the other couriers up to 40 shipments are refreshed per call. Normally unnecessary — courier webhooks keep this current.

Parameters

NameInTypeDescription
limit body integer optional Max shipments to scan (default 200, max 500). e.g. 200

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/ndr/sync" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "limit": 200
}'

Response 200

{
    "success": true,
    "message": "Synced 57 orders.",
    "data": {
        "synced": 57
    }
}
GET /ndr/nsl-codes NSL reason codes

Lookup table of every NDR reason code (also listed in the Reference section).

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/ndr/nsl-codes" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": [
        {
            "nsl_code": "EOD-6",
            "remark": "Consignee unavailable"
        }
    ],
    "total": 102
}

Wallet & COD

GET /wallet Wallet balance

Balance and credit limit. Recharge the wallet from the panel (Wallet → Add Money). Check available_total before bulk jobs.

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/wallet" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "wallet_balance": 1500,
        "wallet_credit": 0,
        "available_total": 1500,
        "currency": "INR",
        "note": "available_total = wallet_balance + wallet_credit. Shipping charges are deducted from available_total."
    }
}
GET /wallet/transactions Wallet transactions

Parameters

NameInTypeDescription
type query string optional Transaction type (e.g. debit, credit, refund).
payment_method query string optional Payment method.
reference query string optional Partial match on reference (e.g. an order id).
date_from query date optional YYYY-MM-DD.
date_to query date optional YYYY-MM-DD.
per_page query integer optional Max 100.

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/wallet/transactions" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "items": [
            {
                "id": 501,
                "transaction_id": "SHP-123456",
                "type": "debit",
                "payment_method": "wallet",
                "amount": 106.2,
                "balance_after": 1393.8,
                "reference": "Shipping charge for MY-ORD-2001",
                "created_at": "2026-10-01 11:20:05"
            }
        ],
        "total": 1,
        "per_page": 20,
        "current_page": 1,
        "last_page": 1
    }
}
GET /cod-remittances/summary COD summary

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/cod-remittances/summary" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "total_remitted_inr": 25000,
        "total_accruing_inr": 4200,
        "counts": {
            "pending": 2,
            "in_progress": 1,
            "completed": 14
        }
    }
}
GET /cod-remittances List COD remittances

Parameters

NameInTypeDescription
from query date optional cod_date from.
to query date optional cod_date to.
status query string optional pending, in_progress, completed, settled, failed.
per_page query integer optional Max 100.

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/cod-remittances" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "summary": {
            "total_remitted": 25000,
            "total_accruing": 4200
        },
        "items": [
            {
                "id": 9,
                "cod_date": "2026-09-28",
                "settle_at_date": "2026-10-01",
                "settlement_days": 3,
                "charge_percent": 1.5,
                "total_cod_amount": 10000,
                "total_charge_amount": 150,
                "total_net_amount": 9850,
                "adjusted_in_wallet": 9850,
                "status": "completed",
                "status_label": "Completed",
                "awb_count": 8
            }
        ],
        "total": 1,
        "per_page": 20,
        "current_page": 1,
        "last_page": 1
    }
}
GET /cod-remittances/{id} COD remittance detail

One remittance with the AWBs it covers and the wallet transactions that settled it.

Parameters

NameInTypeDescription
id path integer required Remittance id. e.g. 9

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/cod-remittances/9" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "id": 9,
        "cod_date": "2026-09-28",
        "status": "completed",
        "total_net_amount": 9850,
        "items": [
            {
                "awb": "1234567891",
                "cod_amount": 1499,
                "charge": 22.49,
                "net_amount": 1476.51
            }
        ],
        "transactions": [
            {
                "adjust_via": "wallet",
                "wallet_transaction_ref": "COD-9",
                "opening_balance": 1500,
                "closing_balance": 11350,
                "transaction_amount": 9850,
                "created_at": "2026-10-01 06:00:00"
            }
        ]
    }
}

Errors

  • 404 — COD remittance not found.

Webhook configuration

GET /webhook View webhook config

Code samples

cURLPHPNode.jsPython
Copy
curl -X GET "https://hubbazzar.com/api/v1/shipping/webhook" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "data": {
        "webhook_url": "https://yourapp.com/hooks/shipping",
        "webhook_secret": "***a1b2",
        "has_secret": true
    }
}
POST /webhook Save webhook config

Sets where events are delivered. Provide a webhook_secret (16–64 chars) or auto_secret: true to have one generated. The plaintext secret is returned only when it is set/created — store it.

Parameters

NameInTypeDescription
webhook_url body string required Public HTTPS URL (max 500). e.g. https://yourapp.com/hooks/shipping
webhook_secret body string optional 16–64 chars; used to sign deliveries.
auto_secret body boolean optional Generate a random secret when none is given. e.g. 1

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/webhook" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://yourapp.com/hooks/shipping",
    "auto_secret": true
}'

Response 200

{
    "success": true,
    "message": "Webhook configuration saved.",
    "data": {
        "webhook_url": "https://yourapp.com/hooks/shipping",
        "webhook_secret": "k3J…48chars"
    }
}
POST /webhook/test Send test event

Fires a signed webhook.test event at your URL so you can verify signature handling.

Code samples

cURLPHPNode.jsPython
Copy
curl -X POST "https://hubbazzar.com/api/v1/shipping/webhook/test" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "message": "Test webhook dispatched to https://yourapp.com/hooks/shipping"
}

Errors

  • 422 — No webhook URL configured.
DELETE /webhook Remove webhook

Code samples

cURLPHPNode.jsPython
Copy
curl -X DELETE "https://hubbazzar.com/api/v1/shipping/webhook" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN"

Response 200

{
    "success": true,
    "message": "Webhook configuration removed."
}

Webhook events

Set your URL with POST /webhook (or in API Settings). We POST JSON to it when something happens, so you don't have to poll. Respond with any 2xx quickly; process the event asynchronously. Deliveries are one-shot (no automatic retry), so also reconcile with GET /shipments now and then.

POST https://yourapp.com/hooks/shipping Content-Type: application/json X-Hub-Event: order.status_updated X-Hub-Delivery: 5f0c…-uuid X-Hub-Signature-256: sha256=<hex> (only when a secret is set) { "event": "order.status_updated", "timestamp": "2026-10-04T14:30:00+05:30", "data": { … } }

Verify the signature

Compute HMAC-SHA256(secret, raw request body) and compare in constant time. Use the raw body, not re-encoded JSON.

PHPNode.jsPython
Copy
<?php
$raw      = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $raw, getenv('WEBHOOK_SECRET'));
if (!hash_equals($expected, $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '')) { http_response_code(401); exit; }
$event = json_decode($raw, true);   // $event['event'], $event['data']
http_response_code(200);

Events

order.createdA shipment was created through the API.
{
    "event": "order.created",
    "timestamp": "2026-10-04T14:30:00+05:30",
    "data": {
        "order_id": 1024,
        "client_order_id": "MY-ORD-2001",
        "waybill": "1234567891",
        "courier_code": "Delhivery",
        "order_status": "manifested",
        "shipping_charge": {
            "base": 60,
            "cod": 30,
            "gst": 16.2,
            "total": 106.2
        },
        "wallet_balance": 1393.8
    }
}
order.status_updatedA courier pushed a status change to us (Delhivery, Shadowfax, Amazon Shipping and Nimbus Post). Fires on every real transition; a manual track?sync=1 refreshes the status but does not emit this event.
{
    "event": "order.status_updated",
    "timestamp": "2026-10-04T14:30:00+05:30",
    "data": {
        "order_id": 1024,
        "client_order_id": "MY-ORD-2001",
        "waybill": "1234567891",
        "previous_status": "in_transit",
        "new_status": "delivered",
        "status_description": "Shipment delivered",
        "status_location": "Gurugram (Haryana)",
        "event_date": "2026-10-04T14:28:00",
        "nsl_code": null,
        "is_ndr": false
    }
}
order.cancelledA shipment was cancelled via the API.
{
    "event": "order.cancelled",
    "timestamp": "2026-10-04T14:30:00+05:30",
    "data": {
        "order_id": 1024,
        "client_order_id": "MY-ORD-2001",
        "waybill": "1234567891",
        "refunded_amount": 106.2
    }
}
orders.bulk_cancelledA bulk cancel finished.
{
    "event": "orders.bulk_cancelled",
    "timestamp": "2026-10-04T14:30:00+05:30",
    "data": {
        "succeeded_count": 2,
        "failed_count": 0,
        "succeeded": [
            {
                "order_id": 1024,
                "waybill": "1234567891",
                "refunded_amount": 106.2
            }
        ]
    }
}
ndr.action_submittedAn NDR action was sent to the courier.
{
    "event": "ndr.action_submitted",
    "timestamp": "2026-10-04T14:30:00+05:30",
    "data": {
        "order_id": 1030,
        "waybill": "1234567899",
        "action": "RE-ATTEMPT",
        "upl_id": "UPL-8891",
        "nsl_code": "EOD-6",
        "ndr_reason": "Consignee unavailable"
    }
}
webhook.testTriggered by POST /webhook/test.
{
    "event": "webhook.test",
    "timestamp": "2026-10-04T14:30:00+05:30",
    "data": {
        "message": "This is a test webhook from Hubbazzar Shipping API.",
        "user_id": 42
    }
}

Order statuses

order_status uses one vocabulary for all couriers — each courier's raw statuses are normalised for you. Filter with GET /shipments?status=….

order_statusMeaningFinal?
manifestedOrder booked with the courier, pickup pending.No
not_pickedPickup attempted / not yet collected.No
in_transitMoving through the courier network.No
dispatchedHanded to the delivery agent.No
out_for_deliveryOut for delivery today.No
pendingDelivery attempt did not complete (see is_ndr).No
ndrNon-delivery report — needs your action.No
deliveredDelivered to the consignee.Yes
rtoReturn to origin initiated.No
rto_in_transitReturning to your warehouse.No
rto_returnedReturned to your warehouse.Yes
cancelledCancelled (locally code 227 on older records).Yes
lostLost / damaged in transit.Yes

Older records may still carry numeric Delhivery codes (220–238, 531; e.g. 226 delivered, 227 cancelled, 237 lost). A shipment can be cancelled until it reaches a final state (delivered, rto, cancelled, lost) — and, in practice, until the courier has picked it up.

NDR reason codes (102)

Appear as nsl_code on NDR shipments. Also available live from GET /ndr/nsl-codes.

CodeReason
EOD-53Amount disputed by customer
EOD-26Consignee unavailable
SD-107Order will be rescheduled due to low capacity - Drop
EOD-89Customer wants to reschedule beyond 3 working days
CL-102Canceled as per client's instructions
CL-101Re-Scheduled as per client's instruction
ST-6WConsignee verified cancellation
ST-120Maximum attempts reached for self collect
EOD-118Temporary stair/lift issue
ST-NIIVR call not received by customer
EOD-40Payment Mode / Amt Dispute
X-IIN3RNo Service Zone. To be RTO'd
EOD-14Office closed
DTUP-229Content Short Reported By client
EOD-147Consignee refused to Accept
EOD-56Customer phone not reachable
EOD-35Other
EOD-11Consignee Unavailable
EOD-158Failed KYC -Code not available
EOD-106Reached Maximum attempt count
X-IIN1RNo Service Zone. To be RTO'd
EOD-6Consignee refused to accept/order cancelled
DLYB2B-107Payment mode/ Amount dispute
X-IIN4RNo Service Zone. To be RTO'd
EOD-103Reached maximum attempt count
EOD-50Returned as per client instructions
EOD-114Buyer refused order
EOD-102Customer not ready for e-KYC
EOD-43Consignee will collect from branch
EOD-85Customer phone not reachable
SC-107Customer requested for self collect
COVID19-007Corona Client Closed
EOD-15Recipient wants delivery at a different address
COVID19-003Corona Police/AdministrationShut Down
ST-205Consignee unavailable - Consignee location permanently closed
EOD-70Unable to reach Customer
DLYRPC-417Address on package is different from HQ address
EOD-149On Hold. Recipient unable to Accept Delivery
RT-105Wrong pin code & address is not serviceable
COVID19-004Corona Limited Dispatch Timings
CL-108Canceled as per customer instruction
EOD-65Not attempted
DLYDC-107Office/Institute closed
COVID19-010Corona Red Zone Area
EOD-42Product Dispute
EOD-107No Service Zone
EOD-18Consignee has refused delivery of product
RT-114Address details invalid
EOD-17Non serviceable location
DLYB2B-105No Entry/ Entry restricted
EOD-51Deferred by customer
SC-104Bad Address
EOD-121Entry restricted area
EOD-74Incomplete address & contact details
EOD-8Wanted to open the package before payment
EOD-69Recipient wants open delivery
EOD-55Customer not available at home
EOD-104Entry restricted area
ST-NTNDR call not received by customer
EOD-32Consignee unavailable for extended duration
EOD-12Door locked, retrial tomorrow (Customer to be called)
EOD-148Recipient unavailable. Establishment closed
ST-109Out of service area
EOD-41Payment Mode-Amount dispute
EOD-78Cash not ready with the customer. To be attempted again
FMEOD-143Seller/Buyer cancelled order - Geofenced
X-IIN2RNo Service Zone. To be RTO'd
EOD-13No such person at the given address
DLYB2B-108Consignee not reachable/Contact details unavailable
EOD-151Consignee unavailable - Permanently closed
ST-NI6No customer response from IVR call
RT-107Unsuccessful NDR Reattempt
X-NSZNon-serviceable location
ST-102Bad/Incomplete Address
EOD-83Damaged/Used Product
EOD-67Customer phone not Reachable
SC-102ODA Shipment
FMEOD-115Temporary stairs/lift problem
EOD-72Consignee did not have cash
EOD-16Payment not ready with customer
EOD-110Customer did not show ID card
DLYLH-146Appointment awaited from consignee
EOD-45Duplicate consignment for RTO
FMEOD-118Seller/Buyer cancelled order
EOD-59Not interested
SC-103Self Collect requested by customer
EOD-3Delivery Rescheduled by Customer
EOD-138Consignee did not receive the Code
EOD-97Customer does not want to handover documents
FMEOD-150Seller/Buyer cancelled order - OTP Verified
FMEOD-138Seller/Buyer cancelled order - qr verified
DTUP-226Address changed by Customer
EOD-76Consignee not available for extended duration
DLYB2B-106Payment Issue ( Amount Dispute/ Cash/ Cheque not ready
EOD-34Other
DLYDC-132Out of Delivery Area (ODA)
EOD-73Bad/Incomplete address
EOD-6OCode verified cancellation
EOD-105Shipment seized by consignee
EOD-46Duplicate Consignment from Shipper
EOD-112Dispatch but not attempted due to rain
EOD-98No KYC request from Customer Side

Troubleshooting

GET /couriers returns an empty list
Your rate card has no slab for exactly that weight/mode. Use a weight from GET /rate-card → weight_slabs, or ask support to enable a courier.
Invalid courier_code
Use a courier_code returned by GET /couriers for the same express_type.
Pickup address not found
pick_address_id must equal a warehouse's pick_address_id from GET /warehouses (not its numeric id).
Courier rejects the order (422)
The message is the courier's own reason — typically a bad pincode, missing city/state, or an unsynced warehouse. Fix and retry; you were not charged.
Nimbus Post: "not registered yet"
Run POST /warehouses/{id}/nimbuspost/send-otp then /nimbuspost/sync with the OTP.
402 Insufficient wallet balance
Recharge in the panel; see GET /wallet → available_total.
409 on create
That client_order_id is already booked — use the returned shipment or pick a new id (cancelled shipments free their id).
Label says "No AWB assigned yet"
The order has no waybill; check GET /shipments/{id}.
Tracking looks stale
Add ?sync=1 to force a live refresh; subscribe to order.status_updated for push updates.