Shipway

Shipway shipping API: HTTP Basic auth with email and licence key, REST calls for orders, AWB and label, pickup, rates, tracking, returns, NDR and webhooks.

Shipway is an Indian shipping aggregator and post purchase platform used by D2C brands and marketplace sellers. It bundles carrier aggregation (book an AWB on Delhivery, Blue Dart, XpressBees, Ecom Express, DTDC, Amazon Shipping and others under Shipway's negotiated rates, or under the seller's own carrier accounts), branded tracking pages, NDR workflows and a returns and exchange portal. Unlike most Indian carriers it publishes a complete, public API reference as a Postman collection, which makes it one of the easier Indian logistics integrations to build against.

At a glance

What it is

Shipway (Onjection Labs, Gurugram) started as a tracking and post purchase notification tool and grew into a full shipping aggregator. It is D2C oriented: the typical user is a Shopify, WooCommerce, Magento or marketplace seller who imports orders into Shipway, lets Shipway pick a courier, prints a label, and uses Shipway's tracking page, NDR calling workflow and returns portal as their post purchase stack. A separate product, Shipway Experience, is sold as tracking and notifications only.

Shipway supports two carrier modes and the distinction matters for the connector. A carrier can be a "Shipway" rate card (for example Shipway Bluedart Express (0.5kg)), where Shipway holds the carrier contract, or a carrier the seller connected with their own credentials in Settings, Manage Couriers. The getcarrier response exposes which carriers are aggregator carriers and which support reverse pickup and NDR.

API access

  • Self-serve. Register at shipway.com, complete signup, then connect a sales channel in Settings, Integrations and a courier in Settings, Manage Couriers.
  • The licence key that doubles as the API password is found in Shipway, Profile, Manage Profile.
  • Base host for all documented calls is https://app.shipway.com/api/. Order push is versioned in the path (v2orders); the remaining endpoints are unversioned.
  • No official SDK in any language. The docs are a published Postman collection with a "Run in Postman" button, and the raw collection is downloadable from apidocs.shipway.com/api/collections/14217096/TW74hQ7r.
  • No published sandbox. The collection description states explicitly that any request made with valid credentials affects real time data in the account.

Authentication

HTTP Basic, with no token exchange and no expiry. One credential pair per Shipway account, so multi seller support means storing one email and licence key per account.

  1. Take the account email and the licence key from Shipway, Profile, Manage Profile.
  2. Join them as email:licence_key.
  3. Base64 encode that string.
  4. Send it as an Authorization: Basic header carrying that value on every request.
TOKEN=$(printf '%s' "seller@example.com:3w2MX1fZl4vm9o3q4g2m0821m0j23147" | base64)
curl -X GET "https://app.shipway.com/api/getcarrier" \
  -H "Authorization: Basic $TOKEN" \
  -H "Content-Type: application/json"

The collection description also mentions passing a token as Authorization: Bearer, which appears to be legacy wording carried over from an older revision. Every worked example in the collection uses Authorization: Basic. Build against Basic and treat the Bearer note as unverified.

Documented status codes: 201 created, 400 missing parameters, 401 authentication failed, 403 forbidden, 404 not found, 405 method not allowed, 500 server error. Note that several endpoints return HTTP 200 with "success": false in the body on a logical failure, so status code alone is not enough: always read success and error.

Objects we can read

Orders

GET https://app.shipway.com/api/getorders

Returns orders in chunks of 100. Filters: orderid, awb_number, tags, date_from and date_to (yyyy-mm-dd), status, shipment_status, new_shipment_status, page.

Order status values: O new order, A processing, E manifested, G dispatched.

Shipment status values: DEL delivered, INT in transit, UND undelivered, RTO, RTD RTO delivered, CAN cancelled, SCH shipment booked, ONH on hold, OOD out for delivery, NFI status pending, NFIDS, plus a reverse set: RSCH pickup scheduled, ROOP out for pickup, RPKP picked up, RDEL return delivered, RINT return in transit, RPSH pickup rescheduled, RCAN return request cancelled, RCLO return request closed, RSMD pickup delayed, PCAN pickup cancelled, ROTH others, RPF pickup failed.

{
  "success": 1,
  "error": "",
  "message": [
    {
      "order_id": "OD-1245-SS",
      "order_total": "870.00",
      "shipping_cost": "30.00",
      "other_charges": "0.00",
      "discount": "0.00",
      "payment_id": "12",
      "payment_method": "Pre Paid",
      "b_firstname": "John",
      "b_lastname": "Doe",
      "b_address": "#321",
      "b_zipcode": "122001",
      "s_firstname": "John",
      "s_address": "#321",
      "s_city": "Gurgaon",
      "s_state": "Haryana",
      "s_zipcode": "122001",
      "s_phone": "9999999999",
      "email": "customer@email.com",
      "weight": "110",
      "box_length": "20",
      "box_breadth": "15",
      "box_height": "10",
      "status": "A",
      "invoice_number": "OD-1245-SS"
    }
  ]
}

Billing and shipping name, phone, email and full address are PII.

Pagination is page based (page=1,2,3) at 100 rows per page; there is no cursor and no total count documented, so read until an empty message array. For a delta sync use date_from and date_to plus page.

Order items

Order items are nested inside the order under products, using the same field names as the push request: product, price, product_code, product_quantity, discount, tax_rate, tax_title.

Shipments and tracking

GET https://app.shipway.com/api/tracking?awb_numbers=AWB1,AWB2&tracking_history=1

Up to about 100 AWBs per request (the docs call 100 the recommended maximum). tracking_history=0 or empty returns the current status only; 1 adds the full scan list.

[
  {
    "awb": 366193892564,
    "tracking_details": {
      "shipment_status": "SCH",
      "shipment_details": [
        {
          "courier_id": "19339",
          "courier_name": "Shipway Amazon Shipping Surface (0.5kg)",
          "order_id": "Sho1113T",
          "pickup_date": null,
          "delivered_date": null,
          "weight": "10",
          "packages": 1,
          "current_status": "Exchange",
          "delivered_to": null,
          "destination": "Gurgaon",
          "consignee_name": "Saksham Aggarwal",
          "origin": "Gurugram"
        }
      ],
      "track_url": "https://storename.shipway.com/t/366193892564"
    }
  }
]

An unknown AWB comes back as {"awb": ..., "error": "AWB not found"} inside the same array, so partial failures are per element, not per request.

Returns and cancellations

  • GET https://app.shipway.com/api/getorders?order_type=R lists returns using the same filters as orders.
  • GET https://app.shipway.com/api/Getreturnreasons lists return reasons, and the same path with POST creates or updates them. A reason id (return_reason_id) is required when creating a return.
  • GET https://app.shipway.com/api/getrejectreasons lists reject reasons, POST creates and updates them.
  • POST https://app.shipway.com/api/returnorderstatus rejects a return shipment.

NDR

  • GET https://app.shipway.com/api/ndr lists NDR shipments, with page, per_page, from, to and search (by AWB).
  • GET https://app.shipway.com/api/ndr/{awb} for one shipment, or ?awb=AWB1,AWB2,AWB3 for several.
  • POST https://app.shipway.com/api/Ndr/OrderDetails returns NDR order detail.
{
  "data": [
    {
      "id": 111111,
      "shipment_id": "90098765432",
      "awb_code": "90098765432",
      "customer_name": "Test Customer",
      "customer_phone": "8888888888",
      "customer_pincode": "654321",
      "payment_method": "cod",
      "payment_status": "pending",
      "status": "Entry Restricted Area",
      "status_code": "SHNDR10",
      "reason": "Entry Restricted Area",
      "attempts": 1,
      "ndr_raised_at": "2026-01-01 09:30:00",
      "courier": "Test Courier (0.5kg)",
      "escalation_status": "open",
      "product_name": "Test Combo Product",
      "product_price": "499.00",
      "history": [
        {
          "id": 111111,
          "ndr_reason": "Entry Restricted Area",
          "action_by": "System",
          "ndr_attempt": 1,
          "medium": "sms",
          "ndr_push_status": "success"
        }
      ]
    }
  ]
}

The NDR endpoints are documented with Authorization: Bearer {token} rather than Basic, which is the one place in the collection where the two auth notes actually conflict. Confirm with support before building the NDR read.

Carriers and rates

  • GET https://app.shipway.com/api/getcarrier, optionally ?carrierid=345, returns every carrier enabled on the account with id, name, carrier_title, reverse_status, ndr_status and aggregator_carrier.
  • GET https://app.shipway.com/api/pincodeserviceable?pincode=122018&payment_type=P returns the carriers that serve a pincode, one row per carrier and payment type (C COD, P prepaid).
  • GET https://app.shipway.com/api/getshipwaycarrierrates?fromPincode=400703&toPincode=400706&paymentType=prepaid returns a rate card.
{
  "success": "success",
  "rate_card": [
    {
      "carrier_id": 7377,
      "courier_name": "Shipway Bluedart Express (0.5kg)",
      "delivery_charge": 50,
      "rto_charge": 50,
      "charged_weight": 0.5,
      "zone": 1
    }
  ]
}

Locations

GET https://app.shipway.com/api/getwarehouses, optionally ?warehouseid=6430. Returns a map keyed by warehouse id with title, company, contact_person_name, email, phone, address_1, address_2, city, state, country, pincode, latitude, longitude, gst_no, fssai_code, default and status.

Not available

No products or listings, no inventory, no settlements and no COD remittance endpoint. Freight is visible as a rate quote (delivery_charge, rto_charge) and as shipping_cost on the order, but billed weight reconciliation and COD payouts are panel and report functions, not API objects.

Writing back: listings, price and stock

Not applicable, this is a carrier aggregator. Shipway holds no catalogue, price or stock. The write path is the shipment lifecycle.

Push an order

POST https://app.shipway.com/api/v2orders with the order as JSON. Mandatory fields include order_id, products, payment_type (P prepaid, C COD), order_total, the billing and shipping address blocks, warehouse_id and return_warehouse_id. ewaybill is required above Rs 50,000. If carrier_id is omitted, Shipway assigns its recommended carrier.

{
  "success": true,
  "message": "Order has been added successfully."
}

Book the AWB and label in one call

Send the same request with carrier_id populated and the response carries the waybill and a label PDF URL.

{
  "success": true,
  "message": "Order has been added successfully.",
  "awb_response": {
    "success": true,
    "message": "AWB No. assigned Successfully",
    "AWB": "1333110020164",
    "carrier_id": "3411",
    "shipping_url": "https://app.shipway.com/shipping_labels/a3359e03e70ae96dd4f97cfe788fa859_thermal.pdf"
  }
}

Manifest, pickup, hold, cancel

  • POST https://app.shipway.com/api/Createmanifest/ with {"order_ids": ["TEST123", "test2"]} returns a manifest id.
  • POST https://app.shipway.com/api/createpickup/ with pickup_date, pickup_time, office_close_time, package_count, carrier_id, warehouse_id, return_warehouse_id, payment_type and order_ids[]. All order ids in one call must share a payment type, belong to the given carrier, and be in Processing or Manifested state.
  • POST https://app.shipway.com/api/Onholdorders/ to hold.
  • POST https://app.shipway.com/api/Cancelorders/ with {"order_ids": [...]} cancels orders. The response is an array with one result per order id, each with its own success, error and message.
  • POST https://app.shipway.com/api/Cancel/ cancels the shipment (the booked AWB) rather than the order.

Returns

POST https://app.shipway.com/api/Createreturns creates a return, a reverse pickup, or a reverse pickup with quality check, depending on the request. return_order_status selects the flow (E for exchange in the sample), each product carries return_reason_id, optional return_products_images[], customer_notes and variants. The response nests a create_return_response object carrying rma_no.

POST https://app.shipway.com/api/Cancelreturnshipment/ cancels a return shipment.

NDR actions

POST https://app.shipway.com/api/Ndr/ReAttempt requests a reattempt and POST https://app.shipway.com/api/Ndr/RTO pushes the shipment to RTO. POST https://app.shipway.com/api/Ndr/InsertOrder registers an order into the NDR flow.

Warehouses

POST https://app.shipway.com/api/warehouse/ creates a pickup or return location.

Webhooks and notifications

Shipway has one tracking callback. It is configured in the panel, not through an API: log in, go to Track, Tracking Webhooks, add the URL. There is no subscription endpoint and no per event topic selection published.

The callback is a POST with a flat tracking body:

{
  "store_code": "1",
  "awbno": "12345678901234",
  "company_id": "99999",
  "pod": "dummy_pod",
  "epod": "dummy_epod",
  "pickupdate": "2025-01-01 10:00:00",
  "expected_delivery_date": "2025-01-05",
  "scans_current_status_time": "2025-01-04 15:00:00",
  "scans_current_status": "Delivered to consignee",
  "carrier": "DummyCarrier",
  "api_input": {
    "awbno": "12345678901234",
    "carrier_id": "99",
    "carrier": "DummyCarrier",
    "current_status": "DEL",
    "current_status_desc": "Delivered",
    "received_by": "John Doe",
    "country_code": "IN",
    "callback_url": "https://dummywebhook.com/callback",
    "webhook_sent_date": "2025-01-04 15:30:00",
    "scans": {
      "0": {
        "location": "Dummy_Location_A",
        "time": "2025-01-04 15:00:00",
        "status": "Delivered to consignee"
      }
    },
    "phone": "9999999999",
    "from": "Dummy_Location_A",
    "extra_fields": {
      "dispatchCount": "1",
      "site_url": "https://dummywebsite.com",
      "sub_carrier_id": "99",
      "StatusType": "DL"
    }
  }
}

Two things to note. scans is an object keyed by stringified index in newest first order, not a JSON array, so a naive array parser will fail. And proof of delivery arrives inline as pod and epod on the callback rather than through a separate POD endpoint.

No signature, HMAC or shared secret is documented for the callback, and no retry policy or timeout is published. Treat the endpoint as unauthenticated: use an unguessable URL path, and validate every awbno against shipments you actually created before acting on the event.

If webhooks are not used, poll GET /api/tracking with batches of AWBs. A 15 to 30 minute cadence on open shipments is reasonable, plus a getorders sweep on date_from and date_to for anything created or edited outside the connector.

Rate limits and pagination

No rate limits, quota headers or burst allowances are published anywhere in the collection, and no 429 appears in the documented status code table. The only stated volume constraints are structural:

  • getorders returns chunks of 100 per page.
  • tracking recommends a maximum of about 100 AWB numbers per request.
  • NDR list accepts page and per_page.

Pagination models: page number for orders and NDR, batch of identifiers for tracking, date window (date_from, date_to) for delta reads. There is no cursor anywhere, and no updated_at filter, only created date, which means a shipment whose status changed but whose order date is old will not resurface in a date window sweep. Rely on the webhook or on tracking the open AWB set for status changes.

Because limits are unpublished, size the sync conservatively: serialise calls, batch tracking at 100 AWBs, and back off on any 500.

Mapping to the unified model

Gaps and open questions

  • Two auth models are described in one collection. The collection header and the tracking endpoint both say Basic; the NDR list endpoint says Authorization: Bearer {token} and the header text also mentions a Bearer token format. No token issuing endpoint is documented anywhere, which suggests the Bearer wording is stale, but it needs confirming.
  • No rate limits, quota headers or retry guidance are published.
  • The tracking webhook has no signature, secret or retry policy documented. Verification method needs a support conversation before it can be trusted for anything that moves money.
  • The unit of weight on an order is not stated. The sample values ("110" with 20 by 15 by 10 cm dimensions) suggest grams, but this must be confirmed.
  • Getreturnreasons serves both GET and POST for list, create and update, differentiated only by request body, which makes the contract ambiguous from the collection alone.
  • No COD remittance, settlement or billed weight dispute endpoint. Whether these exist behind the panel only, or as a report, could not be determined.
  • The published Postman collection is the only reference. There is no OpenAPI specification, no changelog and no deprecation policy, so field additions will arrive without notice.
  • docs.shipway.com and developers.shipway.com both resolve but serve a "we were unable to find what you were looking for" stub. apidocs.shipway.com is the live portal.

Sources