Zippyy is an Indian shipping aggregator and transportation management platform, operated by GoDash. A seller keeps one account and Zippyy brokers each shipment out to one of about a dozen courier networks (Delhivery, Blue Dart, DTDC, Ekart, Xpressbees, Ecom Express, Shadowfax, Amazon Shipping and others) with per carrier service levels. For a seller it is the shipping layer, not a sales channel: there is no catalogue, price or stock surface. Unusually for this segment it publishes a complete, versioned REST reference with OpenAPI specifications, a sandbox host and a real webhook subscription endpoint.
At a glance
What it is
Zippyy (zippyy.ai, branded in its own footer as "A GoDash Company") is an AI positioned logistics platform for India. It sells a transportation management system, a 3PL management layer, route optimisation, and B2B, B2C and international shipping. On top of the core aggregation it sells a set of value added modules that are unusually specific and worth knowing about when reading the data: a pre-order suite that runs AI risk checks on COD orders, an NDR suite that chases failed deliveries over WhatsApp, SMS and calls, Early COD that advances up to 80 percent of COD within two days, a branded tracking page priced at Rs 1 per status or Rs 3 for all statuses per order, delivery notifications, and shipment protection.
It is a seller side logistics tool, not a marketplace and not D2C-only. It is a small player relative to Shiprocket or Delhivery's seller product, and some of its public marketing is visibly templated: the FAQ blocks on its carrier integration and channel integration pages describe container and bill of lading tracking for international freight forwarding and carry GoDash branded testimonials, which does not match the domestic parcel product the API actually implements. Read the API, not the marketing.
The carrier and service level list published in the API introduction, as of 2026-09-22:
GET /v1/external/carrier/active returns the live version of this as a map of carrier to service array, so build against that rather than the static table.
API access
Self serve from a Zippyy account. The introduction's getting started steps are: register for an account, pick the sandbox or production host, and optionally import the full collection into Postman using the "Run in Postman" button in the docs. There is no partner tier, no approval step and no separate API key issuance: the account login credentials are the API credentials.
- The API is versioned in the path. Most endpoints are
v1, shipment creation has moved tov2(/v2/external/shipments/forward-shipment). Several calls additionally require anx-api-version: 1header, which is a second, independent version axis. - No deprecation schedule is published, and the v1 forward shipment endpoint is not shown in the current reference, so v2 should be treated as the only supported create path.
- No official SDK is published in any language. The first party artefacts are the Apidog reference, the per endpoint OpenAPI 3.0.1 specifications, the importable Postman collection, and a downloadable webhook sample body at
https://assets.zippyy.ai/in_tracking_payload.json. - The documentation carries an explicit warning that requests made with valid credentials affect real data in the account, which is why the sandbox host matters.
The host name sellingpartnerapi-in.zippyy.ai echoes Amazon's Selling Partner API naming and is not related to it. Zippyy's API has no connection to Amazon SP-API beyond offering Amazon Shipping (ATS) as one of its carriers.
Authentication
POST {base_url}/v1/external/auth/loginwith headersContent-Type: application/jsonandx-api-version: 1, and the body{"emailAddress": "...", "password": "..."}. No auth header on this call.- The response returns three JWTs:
idToken(identifies the user),accessToken(used for protected resources) andrefreshToken(used to obtain a new access token). - Send
Authorization: Bearer <accessToken>on every other call. - The access token is valid for 5 minutes. This is the single most important operational fact about this API. Any long running job must refresh on a timer rather than per session, and any retry path must assume the token may have expired mid batch.
- A
refreshTokenis issued, but no refresh endpoint is documented in the reference. Until one is confirmed, the practical approach is to re-call/v1/external/auth/loginbefore expiry and keep the credentials in a secret store.
The documentation warns that a failed login may surface as a 500 rather than a 401, so treat 500 on the login path as a possible credential problem and not only as a server fault.
curl -X POST https://sellingpartnerapi-in.zippyy.ai/v1/external/auth/login \
-H 'Content-Type: application/json' \
-H 'x-api-version: 1' \
-d '{"emailAddress":"seller@example.com","password":"<account password>"}'
curl -G 'https://sellingpartnerapi-in.zippyy.ai/v1/external/carrier/serviceability' \ --data-urlencode 'origin_pin=500032' \ --data-urlencode 'destination_pin=400001' \ -H "Authorization: Bearer $ACCESS_TOKEN"
Multi account handling is one email, password and token triple per Zippyy seller account. There is no application level delegation and no scope model. Note that the API credential is the account password, so a leak is a full account compromise; hold it in a secret manager and never log the derived token.
Objects we can read
Shipments
GET /v1/external/shipments?orderIds={id} returns an array of shipment objects. Documented fields include id, orderId, orderNumber, status, subStatus, ndr_action_requested, tenantId, orderSourceCode, shipmentSourceCode, countryCode, options.currency, a shipmentPaymentDetails block (orderType, subTotal, shippingCharges, otherCharges, discount) and a payments block (status, refId, amount, currency, updatedAt).
Shipment ids are prefixed and opaque, for example ord_64bee9263e8d31b196063c24b190e594. There is no documented list or date window query, so shipments are read by id and a local ledger of ids is mandatory.
Tracking
GET /v1/external/shipments/track accepts either orderId or trackingNumber as a query parameter. The response carries the current status and a full checkpoint history:
{
"trackingNumber": "3000180661",
"active": true,
"carrierDetails": { "carrierCode": "zippyyxekart", "carrierName": "zippyyxekart" },
"orderDetails": { "orderId": "ord_6d9f587d23d637769db516428f877243", "orderNumber": "#CZ_99657" },
"status": { "code": "236", "status": "rto", "subStatus": "out_for_delivery" },
"checkpointList": [
{
"createdAt": "1766640104159",
"courierCode": "zippyyxekart",
"checkpointEventTime": "1766659894000",
"active": true,
"address": { "rawLocation": "GandhinagarHub_DEL_PL" },
"status": {
"code": "231",
"status": "pre_transit",
"subStatus": "pickup_failed",
"message": "shipment_out_for_pickup",
"deliveryExceptionMessage": null
},
"message": "shipment_out_for_pickup"
}
]
}
Two timestamps per checkpoint matter and are different: checkpointEventTime is when the carrier scan happened, createdAt is when Zippyy ingested it. In the vendor's own sample the ingestion lag is around five hours, so always order and reconcile on checkpointEventTime and use createdAt only to detect replays.
All timestamps across the API are epoch milliseconds delivered as strings.
The status vocabulary observed across the reference and the vendor's sample body: top level status takes pre_transit, in_transit, out_for_delivery, exception, rto and (by implication) a delivered value; subStatus takes pickup_scheduled, pickup_failed, in_transit, out_for_delivery, exception. The code field is inconsistent between sources: the tracking endpoint sample shows numeric codes as strings ("231", "236"), while the webhook sample shows composite string codes ("forward|pickupfailed", "forward|deliveryattempted"). Map on status and subStatus, and treat code as opaque.
deliveryExceptionMessage carries the human readable NDR reason, for example "Receiver not available", with message carrying the carrier's own phrase, for example "CustomerUnavailable".
Rates
POST /v1/external/shipments/rates takes height, length, breadth (numbers), weight, delivery_type (default FORWARD), order_type (PREPAID or COD), pickup_pincode, drop_pincode and invoice_value, all required. It returns a rates array:
{
"rates": [
{
"id": "rate_5c032faea53a4a869b8955533c83078a",
"object": "Rate",
"carrier": "Delhivery",
"carrierService": "Express",
"service": "Express",
"type": "B2C",
"zone": "C",
"forwardFreightCharges": "129.80",
"codCharges": "0.00",
"rtoCharges": "129.80",
"rate": "129.80",
"currency": "INR",
"est_delivery_days": 3,
"min_chargeable_weight": "0.5",
"charged_weight": "1.00",
"carrier_account_id": "ca_4f2f16747aec6830001b70a93a3d2df2",
"created_at": 1767635927439
}
]
}
Forward, COD and RTO charges are broken out separately, and charged_weight against min_chargeable_weight shows the slab rounding, which is exactly what is needed to audit a freight bill later.
Serviceability
GET /v1/external/carrier/serviceability?origin_pin={pin}&destination_pin={pin} returns an array of {carrier, zone, prepaidActive, codActive, replacementActive, reversePickupActive}. This is the coverage check; rates is the price check. Both are needed because a carrier can be serviceable for prepaid and not for COD on the same lane.
GET /v1/external/carrier/active returns the carrier to service level map described above.
Locations
GET /v1/external/shipments/warehouse/list (requires x-api-version: 1) returns warehouses grouped by category, for example sender, each with a summary array of addresses carrying id (a UUID), firstName, lastName, company, emailAddress, phoneNumber, locationName, addressType and a locationDetails block with addressLine1, addressLine2, postalCode, city, state and country. GET /v1/external/shipments/warehouse fetches one by address id. The warehouse UUID is what shipment creation references as warehouseId and returnAddressId.
Documents
GET /v1/external/shipments/{orderId}/label-url returns {waybill, format, message, label_url}. A separate call returns the label binary rather than a URL, and GET invoice returns the shipment invoice. These are per shipment, not batched.
Orders, order items, products, listings, inventory, payments and settlements, customers
No standalone order object: an order exists only as the orderNumber, orderDetails and productRequestsList carried on a shipment. Listings, inventory and products do not exist at all, as expected for a carrier. There is no settlement, invoice statement, wallet or COD remittance endpoint, despite Early COD being a marketed product. Customer data is present only as the receiver block on a shipment and is returned unmasked.
Writing back: listings, price and stock
Not applicable, this is a carrier aggregator. Zippyy holds no catalogue, price or stock. The write path is the shipment lifecycle.
Create. POST /v2/external/shipments/forward-shipment. The documentation is explicit that the warehouse must be created first, then the shipment. The request body:
{
"orderNumber": "ORD1ttte55rwATKM01",
"orderCreatedAt": "1760103113424",
"channelId": "External",
"warehouseId": "1c2f6853-0084-42c5-85f8-4937eca3b6a0",
"returnAddressId": "1c2f6853-0084-42c5-85f8-4937eca3b6a0",
"receiver": { "firstName": "Aditya", "lastName": "", "email": "buyer@example.com", "phoneNumber": "9000755025", "companyName": "Example" },
"destination": { "addressLine1": "Chapel Rd", "addressLine2": "Bandra West", "city": "Mumbai", "state": "Maharashtra", "country": "India", "countryCode": "IN", "pinCode": "400050", "type": "Residential" },
"type": "Zippyy",
"sellerNote": "test comments",
"parcelAttributes": {
"dimension": { "length": 12.35, "width": 12.85, "height": 12.14, "unit": "cm" },
"weight": { "weight": 1, "unit": "kg" }
},
"tags": "test",
"productRequestsList": [
{ "productName": "PDT1", "price": "1241", "quantity": 1, "category": null, "currencyCode": "INR", "sku": "SKU-322", "taxRate": "0", "discount": "0", "description": null, "hs_code": null }
],
"shippingProperties": { "orderType": "PREPAID", "subTotal": 1241.21, "shippingCharges": 0, "otherCharges": 0, "discount": 0 },
"carrier": "Delhivery",
"service": "Surface"
}
It returns 201 with {orderId, awb, cratedAt, carrier, service, shipmentPurchaseError}. Note cratedAt is spelled that way in the specification, and shipmentPurchaseError is a separate failure channel inside a successful HTTP response, so it must be checked even on 201.
Carrier and service are chosen by the caller, using values from the rates or active carrier calls. There is no documented auto allocation rule engine on this path.
Cancel. PUT /v1/external/shipments/{orderId}/cancel returns {id, createdAt, updatedAt, refundStatus, trackingCode, ...}. The refundStatus field indicates that cancellation has a money consequence, so record it.
NDR action. PUT /v1/external/shipments/ndr with {"orderId": "...", "action": "reattempt|rto|reattemptwithaddress|reattemptwithphonenumber", "updatePhoneNumber": "..."}. Four actions, with the address and phone variants carrying corrected details. This pairs with the ndr_action_requested flag on the shipment object.
Warehouses. POST create and PUT update on the warehouse resource. These must run before shipment creation, since the shipment references warehouse UUIDs.
Pickup. The Couriers section is described as covering "request for the pickup of your order", but no separate pickup endpoint appears in the published reference. Pickup appears to be implicit in shipment creation. Confirm this before assuming a shipment will be collected.
Webhooks and notifications
Zippyy supports one webhook feed, for tracking events, and it is self registered:
POST /v1/external/shipments/track/webhook with {"url": "https://...", "enabled": true, "contentType": "application/json"} and a bearer token. It returns {endpointId, url, enabled, contentType, createdAt, updatedAt}, so subscriptions are addressable objects and presumably manageable.
Delivery specification, as published:
- Method
POST,Content-Type: application/json. - The receiving URL must respond with HTTP 200 and nothing else.
- The body is the full tracking object, not a delta. A real sample is downloadable from
https://assets.zippyy.ai/in_tracking_payload.json; the vendor's own example carries sixteen checkpoints in a single delivery.
Top level keys in the webhook body: orderId, createdAt, updatedAt, orderDetails (orderId, orderNumber), countryCode, trackingNumber, carrierTrackingNumber (the composite form, for example ats|364976648537), carrierDetails, status and checkpointList.
No signature, HMAC secret or signing header is published for the webhook, and the subscription body has no secret field. The endpoint must therefore be protected by an unguessable URL path, and every event must be treated as unverified input: re-read the shipment through GET /v1/external/shipments/track before acting on anything that has a money consequence. No retry policy, ordering guarantee or replay endpoint is published either, so also reconcile on a timer.
Because the body is a full snapshot rather than a delta, handling is simple: upsert the whole checkpoint list keyed on checkpointEventTime plus status, and let late or duplicate deliveries be idempotent.
Rate limits and pagination
The reference documents 429 Too Many Requests, API rate limit exceeded as an expected response code but publishes no numbers: no requests per second, no burst allowance, no per account or per token ceiling, and no quota headers. Treat 429 as certain to happen and implement exponential backoff with jitter from day one.
The full documented status code set is 200 and 202 for success (with the caveat that some calls still return validation errors inside a 200 body), 400, 401, 404, 405, 422, 429, and 500, 502, 503, 504.
Pagination does not arise: there is no list endpoint for shipments or tracking, and the two list style calls (warehouse list, active carriers) return bounded arrays. Consequences:
- No backfill route. Record every
orderIdandawbat creation, because there is no way to enumerate shipments later. - The 5 minute token lifetime is the real throughput constraint. A batch job should mint a token, work for under 5 minutes, and re-mint, rather than assume a session.
- Practical cadence: rely on the webhook for state, reconcile every unterminated shipment through
GET /shipments/trackonce or twice a day, cache serviceability per pincode pair, and cache rates only for minutes since they carry surge and slab effects.
Mapping to the unified model
Gaps and open questions
- The 5 minute access token lifetime is documented but the refresh mechanism is not. A
refreshTokenis returned with no endpoint to redeem it, so re-login is the only proven path and it puts the account password on the hot path of every batch. - No rate limit numbers, despite
429being documented as expected. - No signature or shared secret on the webhook, and no retry, ordering or replay policy.
status.codeis inconsistent between the tracking endpoint (numeric strings) and the webhook sample (composite strings). The full status code table is not published anywhere.- No shipment list or date window query, so there is no backfill or recovery route after a webhook outage.
- No pickup request endpoint appears in the reference even though the Couriers section says pickup is covered.
- No settlement, wallet or COD remittance API, despite Early COD being a headline product.
shipmentPurchaseErroris returned inside a201response with no documented error vocabulary.- Whether proof of delivery (signature or photograph) is retrievable is not addressed.
- The marketing site's carrier integration and channel integration pages carry templated content about international freight and container tracking that does not match the domestic parcel API, so the public carrier and channel lists on those pages should not be trusted. Use
GET /v1/external/carrier/active.
Sources
- Zippyy REST APIs, Introduction for the environments, error codes and the carrier and service level table
- Authentication API and Generate Token for the JWT flow and the 5 minute token lifetime
- Per endpoint OpenAPI 3.0.1 specifications read on 2026-09-22 for serviceability, quick quote, forward shipment v2, shipment details, label URL, cancel, tracking, webhook, NDR action, warehouse list and active couriers, all under
apidocs.zippyy.ai - Zippyy webhook sample body, the vendor's own downloadable tracking event body
- zippyy.ai for the product line, the value added modules and the GoDash ownership