ILS Tracking API v1.0
← Main Documentation Postman Contact Support

ILS Tracking API

Integrate the Indian Logistics Services order-tracking experience into your mobile application. Same data your customers see on the storefront — order summary, courier timeline, line-item details — accessible through a clean JSON API.

Base URL
ils.shopiapps.in/prfiles/tracking/api/
Protocol
HTTPS only
Method
POST
Auth
Auth-Token header
Format
application/x-www-form-urlencoded
Response
application/json

What this API gives you

  • Tracking page configuration — theme, copy, step labels — so your mobile UI matches the merchant's branding.
  • Order & AWB login — verify a tracking number or look up shipments by order number with optional email/phone challenge.
  • Full tracking payload — order summary, partial-shipment-aware totals, line items with product images, and the courier's complete scan history.

Supported couriers

The API returns a normalised shipment payload across all configured couriers:

Bluedart · Delhivery · DTDC · Ecom Express · Smartr · Xpressbees · Ekart · Amazon · Intargos · Shiprocket · Xpressbees Franchise · Xpressbees Ecom · India Post · Borzo · Shree Maruti · TCI Express · Porter · FedEx · Pickrr

Authentication

Every request must include:

ParameterTypeRequiredDescription
Auth-TokenstringRequiredLong-lived per-shop API token (see Get an API Token). Sent as an HTTP header.
Example: a1b2c3...
shopstringRequiredShopify shop domain, must end in .myshopify.com. Sent in the form body.
Example: examplestore.myshopify.com

Server-side checks

On every request, the server verifies the following in order:

  1. The ILS app is installed for the shop (app.app_status = 'installed').
  2. Payment status is active (free or accepted).
  3. The shop's plan includes the Customer Tracking feature.
  4. The supplied Auth-Token matches the token stored for the shop.
CORS Access-Control-Allow-Origin: * is set, and OPTIONS preflight is handled. The endpoints can be called from a mobile WebView, React Native bridge, or web wrapper.

Get an API Token

The mobile-app access token is per-shop and issued by the ILS team. There is no self-serve generation today.

How to request a token

  1. Email support@shopiapps.in with the subject "Mobile API token request".
  2. Include the shop domain (e.g. examplestore.myshopify.com), the mobile app name/platform (e.g. Acme Shop iOS), and a contact email.
  3. ILS support verifies the shop has an active plan that includes customer_tracking and provisions the token.
  4. The token is delivered out-of-band (encrypted email or shared secret store).

Token properties

PropertyValue
FormatHex string, ~96 characters
LifetimeLong-lived (no expiry); rotated on demand
ScopeSingle Shopify shop
Server storagesettings.mobile_app_access_token

Rotation

If you suspect a token has been leaked, email support@shopiapps.in with [SECURITY] in the subject. The old token is revoked immediately on issue of a new one.

Storage guidance Never hard-code the token in your client binary or commit it to source control. Use platform secure storage — Keychain Services on iOS, EncryptedSharedPreferences / Keystore on Android.

Response Format

All responses — success and error — share the same envelope:

{
  "success": true,
  "code": "OK",
  "message": "Order tracking details found.",
  "data": { /* endpoint-specific payload */ },

  "result": "success",
  "msg": "Order tracking details found."
}
ParameterTypeRequiredDescription
successbooleanRequiredtrue for any 2xx response, false otherwise. Use this for branching.
codestringRequiredStable machine code: OK, INVALID_INPUT, UNAUTHORIZED, FORBIDDEN, PLAN_LIMIT, NOT_FOUND, SERVER_ERROR. Safe to switch on.
messagestringRequiredHuman-readable message, may be localised per merchant. Safe to display in UI.
dataobjectOptionalEndpoint-specific payload. Empty array on most error responses.
resultenumOptionalLegacy v1 alias of success — values: success / fail.
msgstringOptionalLegacy v1 alias of message.
Migration note New integrations should rely on success + code + data. The legacy result / msg keys are preserved for backward compatibility but will not be expanded.

Status Codes

HTTPCodeWhen you'll see it
200OKSuccessful response
400INVALID_INPUTMissing or malformed parameters; unknown action; field length exceeds limit
401UNAUTHORIZEDMissing / invalid Auth-Token; email-phone mismatch on order login
403FORBIDDENApp not installed for this shop, or payment status inactive
403PLAN_LIMITCustomer-tracking feature not in the shop's plan
404NOT_FOUNDOrder, AWB, or tracking record not present
500SERVER_ERRORUnexpected server error — retry, then contact support if persistent

Error response example

{
  "success": false,
  "code": "NOT_FOUND",
  "message": "Sorry we can't found your order.",
  "data": [],
  "result": "fail",
  "msg": "Sorry we can't found your order."
}

Get Tracking Settings

POST https://ils.shopiapps.in/prfiles/tracking/api/ action = get_tracking_settings

Returns the merchant's per-shop tracking-page configuration: theme colours, copy, date/time formats, login behaviour, and progress-step labels. Call this once on app launch (or when the user enters the tracking flow) and cache for the session.

Headers

ParameterTypeRequiredDescription
Auth-TokenstringRequiredPer-shop API token.
Example: a1b2c3d4...

Body parameters

ParameterTypeRequiredDescription
shopstringRequiredShopify shop domain.
Example: examplestore.myshopify.com
actionenumRequiredMust be the constant value below.
Example: get_tracking_settings

Request example

curl --location 'https://ils.shopiapps.in/prfiles/tracking/api/' \
  --header 'Auth-Token: YOUR_TOKEN' \
  --data-urlencode 'shop=examplestore.myshopify.com' \
  --data-urlencode 'action=get_tracking_settings'
const body = new URLSearchParams({
  shop: 'examplestore.myshopify.com',
  action: 'get_tracking_settings',
});

const r = await fetch('https://ils.shopiapps.in/prfiles/tracking/api/', {
  method: 'POST',
  headers: { 'Auth-Token': 'YOUR_TOKEN' },
  body,
});
const json = await r.json();
if (!json.success) throw new Error(`${json.code}: ${json.message}`);
console.log(json.data);
val body = FormBody.Builder()
    .add("shop", "examplestore.myshopify.com")
    .add("action", "get_tracking_settings")
    .build()

val req = Request.Builder()
    .url("https://ils.shopiapps.in/prfiles/tracking/api/")
    .header("Auth-Token", "YOUR_TOKEN")
    .post(body)
    .build()

client.newCall(req).execute().use { response ->
    val json = JSONObject(response.body!!.string())
    if (!json.getBoolean("success")) {
        throw IOException("${json.getString("code")}: ${json.getString("message")}")
    }
}
var req = URLRequest(url: URL(string: "https://ils.shopiapps.in/prfiles/tracking/api/")!)
req.httpMethod = "POST"
req.setValue("YOUR_TOKEN", forHTTPHeaderField: "Auth-Token")
req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
req.httpBody = "shop=examplestore.myshopify.com&action=get_tracking_settings".data(using: .utf8)

let (data, _) = try await URLSession.shared.data(for: req)
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
<?php
$ch = curl_init('https://ils.shopiapps.in/prfiles/tracking/api/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Auth-Token: YOUR_TOKEN'],
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => http_build_query([
        'shop'   => 'examplestore.myshopify.com',
        'action' => 'get_tracking_settings',
    ]),
]);
$body = curl_exec($ch);
curl_close($ch);
$json = json_decode($body, true);

Response — 200 OK

{
  "success": true,
  "code": "OK",
  "message": "OK",
  "data": {
    "track_enable": "1",
    "theme_color": "#0c4ca3",
    "font_color": "#000000",
    "button_font_color": "#ffffff",
    "additional_css": "",
    "date_formate": "F dS Y",
    "time_formate": "h:i:s a",
    "track_method": "0",
    "email_enable": "1",
    "email_required": "0",
    "login_page_description": "Enter your order details to track your shipment.",
    "login_button_text": "Track",
    "show_login_banner": "1",
    "login_banner_image_url": "https://ils.shopiapps.in/uploads/banners/123.jpg",
    "login_banner_position": "0",
    "view_content": ["orderdetail", "productlist", "trackingHistory", "trackingCompany"],
    "show_track_process_icon": "1",
    "track_process_steps": {
      "order": "Ordered",
      "ready_ship": "Ready To Ship",
      "transit": "In Transit",
      "out_delivery": "Out For Delivery",
      "delivered": "Delivered"
    },
    "show_promotion_banner": "0",
    "promotion_images": "",
    "show_track_detail_header": "0",
    "track_detail_header_content": "",
    "show_track_detail_footer": "0",
    "track_detail_footer_content": ""
  },
  "result": "success",
  "msg": "OK"
}

Response fields

ParameterTypeRequiredDescription
data.track_enableenumRequired"0" = disabled, "1" = enabled. If 0, hide the tracking entry point.
data.theme_colorstringRequiredPrimary hex colour for tracking screens.
data.font_colorstringRequiredBody text colour (hex).
data.button_font_colorstringRequiredCTA button text colour (hex).
data.additional_cssstringOptionalCSS injected on the web tracker. Mobile clients should ignore.
data.date_formatestringRequiredPHP-style date format hint (e.g. F dS Y). Prefer your platform's locale APIs for rendering.
data.time_formatestringRequiredPHP-style time format hint.
data.track_methodenumRequired"0": order number OR AWB · "1": only order number · "2": only AWB.
data.email_enableenumRequired"1" = show email/phone field on the order-login form.
data.email_requiredenumRequired"1" = email/phone is mandatory.
data.login_page_descriptionstringRequiredDisplay above the login form.
data.login_button_textstringRequiredCTA label.
data.show_login_bannerenumRequired"1" = show banner on the login screen.
data.login_banner_image_urlstringOptionalAbsolute URL of the banner image (empty when disabled).
data.login_banner_positionenumOptional"0" = left, "1" = right.
data.view_contentarrayRequiredSubset of orderdetail, productlist, trackingHistory, trackingCompany — sections to render after login.
data.show_track_process_iconenumRequired"1" = show the 5-step progress bar.
data.track_process_stepsobjectRequiredLocalised step labels: order, ready_ship, transit, out_delivery, delivered.
data.show_promotion_bannerenumOptional"1" = show promo banner carousel on the result screen.
data.promotion_imagesarrayOptionalArray of {name, link} objects (image URL + click target).
data.show_track_detail_headerenumOptional"1" = show merchant HTML above the tracking detail.
data.track_detail_header_contentstringOptionalHTML to render above the tracking detail (when enabled).
data.show_track_detail_footerenumOptional"1" = show merchant HTML below the tracking detail.
data.track_detail_footer_contentstringOptionalHTML to render below the tracking detail.

Errors

HTTPCodeWhen you'll see it
401UNAUTHORIZEDMissing / invalid Auth-Token
403FORBIDDENApp not installed
403PLAN_LIMITCustomer tracking not in plan

Login by AWB

POST https://ils.shopiapps.in/prfiles/tracking/api/ action = awb_login

Verifies that an AWB (tracking) number belongs to the shop. Use this when the user chose "Track by AWB".

Headers

ParameterTypeRequiredDescription
Auth-TokenstringRequiredPer-shop API token.

Body parameters

ParameterTypeRequiredDescription
shopstringRequiredShopify shop domain.
Example: examplestore.myshopify.com
actionenumRequiredConstant.
Example: awb_login
awb_nostringRequiredTracking number (max 64 chars).
Example: DLV1234567890

Request example

curl --location 'https://ils.shopiapps.in/prfiles/tracking/api/' \
  --header 'Auth-Token: YOUR_TOKEN' \
  --data-urlencode 'shop=examplestore.myshopify.com' \
  --data-urlencode 'action=awb_login' \
  --data-urlencode 'awb_no=DLV1234567890'
const body = new URLSearchParams({
  shop: 'examplestore.myshopify.com',
  action: 'awb_login',
  awb_no: 'DLV1234567890',
});

const r = await fetch('https://ils.shopiapps.in/prfiles/tracking/api/', {
  method: 'POST',
  headers: { 'Auth-Token': 'YOUR_TOKEN' },
  body,
});
const json = await r.json();
if (!json.success) throw new Error(`${json.code}: ${json.message}`);
console.log(json.data);
val body = FormBody.Builder()
    .add("shop", "examplestore.myshopify.com")
    .add("action", "awb_login")
    .add("awb_no", "DLV1234567890")
    .build()

val req = Request.Builder()
    .url("https://ils.shopiapps.in/prfiles/tracking/api/")
    .header("Auth-Token", "YOUR_TOKEN")
    .post(body)
    .build()

client.newCall(req).execute().use { response ->
    val json = JSONObject(response.body!!.string())
    if (!json.getBoolean("success")) {
        throw IOException("${json.getString("code")}: ${json.getString("message")}")
    }
}
var req = URLRequest(url: URL(string: "https://ils.shopiapps.in/prfiles/tracking/api/")!)
req.httpMethod = "POST"
req.setValue("YOUR_TOKEN", forHTTPHeaderField: "Auth-Token")
req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
req.httpBody = "shop=examplestore.myshopify.com&action=awb_login&awb_no=DLV1234567890".data(using: .utf8)

let (data, _) = try await URLSession.shared.data(for: req)
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
<?php
$ch = curl_init('https://ils.shopiapps.in/prfiles/tracking/api/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Auth-Token: YOUR_TOKEN'],
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => http_build_query([
        'shop'   => 'examplestore.myshopify.com',
        'action' => 'awb_login',
        'awb_no' => 'DLV1234567890',
    ]),
]);
$body = curl_exec($ch);
curl_close($ch);
$json = json_decode($body, true);

Response — 200 OK

{
  "success": true,
  "code": "OK",
  "message": "Tracking number successfully founds",
  "data": { "awb": "DLV1234567890" },
  "awb": "DLV1234567890",
  "result": "success",
  "msg": "Tracking number successfully founds"
}

Response fields

ParameterTypeRequiredDescription
data.awbstringRequiredConfirmed AWB number. Pass this to Get Tracking Details to load the full payload.
awbstringOptionalLegacy alias of data.awb.

Errors

HTTPCodeWhen you'll see it
400INVALID_INPUTawb_no missing or longer than 64 characters
404NOT_FOUNDAWB not on file for this shop

Login by Order

POST https://ils.shopiapps.in/prfiles/tracking/api/ action = awb_login_with_order

Resolves shipments for a given order number, optionally cross-checked against an email or 10-digit phone number. Use this when the user chose "Track by Order ID".

Headers

ParameterTypeRequiredDescription
Auth-TokenstringRequiredPer-shop API token.

Body parameters

ParameterTypeRequiredDescription
shopstringRequiredShopify shop domain.
Example: examplestore.myshopify.com
actionenumRequiredConstant.
Example: awb_login_with_order
order_namestringRequiredOrder number (max 64 chars). Shopify display name like #1042 or plain numeric.
Example: #1042
order_emailstringOptionalEmail or phone. Spaces, +, and - are stripped. For numerics, only the trailing 10 digits are compared. Required only when email_required = "1".
Example: jane@example.com

Request example

curl --location 'https://ils.shopiapps.in/prfiles/tracking/api/' \
  --header 'Auth-Token: YOUR_TOKEN' \
  --data-urlencode 'shop=examplestore.myshopify.com' \
  --data-urlencode 'action=awb_login_with_order' \
  --data-urlencode 'order_name=#1042' \
  --data-urlencode 'order_email=jane@example.com'
const body = new URLSearchParams({
  shop: 'examplestore.myshopify.com',
  action: 'awb_login_with_order',
  order_name: '#1042',
  order_email: 'jane@example.com',
});

const r = await fetch('https://ils.shopiapps.in/prfiles/tracking/api/', {
  method: 'POST',
  headers: { 'Auth-Token': 'YOUR_TOKEN' },
  body,
});
const json = await r.json();
if (!json.success) throw new Error(`${json.code}: ${json.message}`);
console.log(json.data);
val body = FormBody.Builder()
    .add("shop", "examplestore.myshopify.com")
    .add("action", "awb_login_with_order")
    .add("order_name", "#1042")
    .add("order_email", "jane@example.com")
    .build()

val req = Request.Builder()
    .url("https://ils.shopiapps.in/prfiles/tracking/api/")
    .header("Auth-Token", "YOUR_TOKEN")
    .post(body)
    .build()

client.newCall(req).execute().use { response ->
    val json = JSONObject(response.body!!.string())
    if (!json.getBoolean("success")) {
        throw IOException("${json.getString("code")}: ${json.getString("message")}")
    }
}
var req = URLRequest(url: URL(string: "https://ils.shopiapps.in/prfiles/tracking/api/")!)
req.httpMethod = "POST"
req.setValue("YOUR_TOKEN", forHTTPHeaderField: "Auth-Token")
req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
req.httpBody = "shop=examplestore.myshopify.com&action=awb_login_with_order&order_name=%231042&order_email=jane%40example.com".data(using: .utf8)

let (data, _) = try await URLSession.shared.data(for: req)
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
<?php
$ch = curl_init('https://ils.shopiapps.in/prfiles/tracking/api/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Auth-Token: YOUR_TOKEN'],
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => http_build_query([
        'shop'   => 'examplestore.myshopify.com',
        'action' => 'awb_login_with_order',
        'order_name' => '#1042',
        'order_email' => 'jane@example.com',
    ]),
]);
$body = curl_exec($ch);
curl_close($ch);
$json = json_decode($body, true);

Response — 200 OK

{
  "success": true,
  "code": "OK",
  "message": "tracking number successfully founds",
  "data": {
    "awb": "DLV1234567890",
    "multiple_tracking": [
      {
        "number": "DLV1234567890",
        "lineItem": ["13241324", "13241325"],
        "company": "Delhivery",
        "displayStatus": "in_progress",
        "current_order_status": "Out for delivery"
      },
      {
        "number": "DLV1234567891",
        "lineItem": ["13241326"],
        "company": "Delhivery",
        "displayStatus": "delivered",
        "current_order_status": "Delivered"
      }
    ]
  },
  "awb": "DLV1234567890",
  "multiple_tracking": [/* mirror */]
}

Response fields

ParameterTypeRequiredDescription
data.awbstringRequiredMost recent (primary) AWB for the order. Pass to Get Tracking Details.
data.multiple_tracking[]arrayRequiredAll shipments for the order — an order may ship in multiple parcels.
data.multiple_tracking[].numberstringRequiredAWB number of this shipment.
data.multiple_tracking[].lineItem[]arrayRequiredIDs of the order line-items in this parcel.
data.multiple_tracking[].companystringRequiredDisplay name of the courier (e.g. "Delhivery").
data.multiple_tracking[].displayStatusenumRequiredOne of pending, in_progress, delivered, failed, return, cancelled, RTO delivered.
data.multiple_tracking[].current_order_statusstringOptionalLatest free-text status from the courier (e.g. "Out for delivery").

Errors

HTTPCodeWhen you'll see it
400INVALID_INPUTorder_name missing or longer than 64 characters
401UNAUTHORIZEDorder_email supplied but neither email nor phone matches the order
404NOT_FOUNDOrder not found, or no packages have been manifested yet

Get Tracking Details

POST https://ils.shopiapps.in/prfiles/tracking/api/ action = get_tracking_info

Returns the full tracking payload for a confirmed AWB: order summary (customer, totals), live courier shipment history, and line items with product images and handles.

Heavy endpoint This call hits the courier's live tracking API plus Shopify GraphQL for product details. Cache the response client-side for 2–5 minutes; avoid polling under 60 seconds.

Headers

ParameterTypeRequiredDescription
Auth-TokenstringRequiredPer-shop API token.

Body parameters

ParameterTypeRequiredDescription
shopstringRequiredShopify shop domain.
Example: examplestore.myshopify.com
actionenumRequiredConstant.
Example: get_tracking_info
awb_nostringRequiredTracking number from Login by AWB or Login by Order.
Example: DLV1234567890

Request example

curl --location 'https://ils.shopiapps.in/prfiles/tracking/api/' \
  --header 'Auth-Token: YOUR_TOKEN' \
  --data-urlencode 'shop=examplestore.myshopify.com' \
  --data-urlencode 'action=get_tracking_info' \
  --data-urlencode 'awb_no=DLV1234567890'
const body = new URLSearchParams({
  shop: 'examplestore.myshopify.com',
  action: 'get_tracking_info',
  awb_no: 'DLV1234567890',
});

const r = await fetch('https://ils.shopiapps.in/prfiles/tracking/api/', {
  method: 'POST',
  headers: { 'Auth-Token': 'YOUR_TOKEN' },
  body,
});
const json = await r.json();
if (!json.success) throw new Error(`${json.code}: ${json.message}`);
console.log(json.data);
val body = FormBody.Builder()
    .add("shop", "examplestore.myshopify.com")
    .add("action", "get_tracking_info")
    .add("awb_no", "DLV1234567890")
    .build()

val req = Request.Builder()
    .url("https://ils.shopiapps.in/prfiles/tracking/api/")
    .header("Auth-Token", "YOUR_TOKEN")
    .post(body)
    .build()

client.newCall(req).execute().use { response ->
    val json = JSONObject(response.body!!.string())
    if (!json.getBoolean("success")) {
        throw IOException("${json.getString("code")}: ${json.getString("message")}")
    }
}
var req = URLRequest(url: URL(string: "https://ils.shopiapps.in/prfiles/tracking/api/")!)
req.httpMethod = "POST"
req.setValue("YOUR_TOKEN", forHTTPHeaderField: "Auth-Token")
req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
req.httpBody = "shop=examplestore.myshopify.com&action=get_tracking_info&awb_no=DLV1234567890".data(using: .utf8)

let (data, _) = try await URLSession.shared.data(for: req)
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
<?php
$ch = curl_init('https://ils.shopiapps.in/prfiles/tracking/api/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Auth-Token: YOUR_TOKEN'],
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => http_build_query([
        'shop'   => 'examplestore.myshopify.com',
        'action' => 'get_tracking_info',
        'awb_no' => 'DLV1234567890',
    ]),
]);
$body = curl_exec($ch);
curl_close($ch);
$json = json_decode($body, true);

Response — 200 OK

{
  "success": true,
  "code": "OK",
  "message": "Order tracking details found.",
  "data": {
    "process_step": "3",
    "order_data": {
      "order_name": "#1042",
      "customer_name": "Jane Doe",
      "shipping_address": "Flat 1, Main Street, Bengaluru, KA, 560001",
      "mobile": "9876543210",
      "email": "jane@example.com",
      "totalPrice": "1499.00",
      "total_tax": "228.30",
      "discount": "100.00",
      "shipping_charge": "0.00",
      "courier_name": "delhivery"
    },
    "shipment_data": {
      "pStatus": "3",
      "Origin": "BENGALURU",
      "Destination": "MUMBAI",
      "history": [
        { "date": "May 19th 2026", "time": "10:23:00 am", "status": "In Transit", "location": "Bengaluru Hub" },
        { "date": "May 18th 2026", "time": "08:11:00 pm", "status": "Picked up",  "location": "Bengaluru Warehouse" }
      ]
    },
    "lineitems_data": [
      {
        "title": "Cotton T-Shirt",
        "handle": "cotton-tshirt",
        "unit_price": "499.00",
        "total_qty": 2,
        "src": "https://cdn.shopify.com/.../tshirt.jpg"
      }
    ]
  },
  "processStep": "3",
  "order_data":    {/* mirror */},
  "shipment_data": {/* mirror */},
  "lineitems_data":[/* mirror */]
}

Response fields

ParameterTypeRequiredDescription
data.process_stepenumRequiredHigh-level step. "1" Ordered · "2" Ready to ship · "3" In transit · "4" Out for delivery · "5" Delivered. Use this to drive the progress bar.
data.order_dataobjectRequiredOrder summary — see fields below.
data.order_data.order_namestringRequiredShopify order display name (e.g. #1042).
data.order_data.customer_namestringOptionalFirst + last name. Falls back to shipping name if customer is missing.
data.order_data.shipping_addressstringOptionalSingle-line, comma-joined shipping address.
data.order_data.mobilestringOptionalBest-effort phone (may be empty).
data.order_data.emailstringOptionalBest-effort email.
data.order_data.totalPricestringRequiredTotal amount for this AWB. For partial shipments, recalculated for items in this parcel only.
data.order_data.total_taxstringRequiredTax component.
data.order_data.discountstringRequiredDiscount applied.
data.order_data.shipping_chargestringRequiredShipping cost.
data.order_data.courier_namestringRequiredInternal courier code (e.g. delhivery, bluedart).
data.shipment_dataobjectRequiredCarrier-provided shipment data.
data.shipment_data.pStatusenumRequiredMirror of process_step.
data.shipment_data.OriginstringOptionalCarrier origin hub.
data.shipment_data.DestinationstringOptionalCarrier destination hub.
data.shipment_data.history[]arrayRequiredScan events, newest first. Each has date, time, status, location. Shape is consistent across all couriers.
data.lineitems_data[]arrayRequiredLine items in this AWB.
data.lineitems_data[].titlestringRequiredProduct title.
data.lineitems_data[].handlestringOptionalShopify product handle.
data.lineitems_data[].unit_pricestringRequiredPer-unit price.
data.lineitems_data[].total_qtyintegerRequiredQuantity in this parcel.
data.lineitems_data[].srcstringRequiredImage URL. Falls back to a placeholder when unavailable.

Errors

HTTPCodeWhen you'll see it
400INVALID_INPUTawb_no missing or longer than 64 characters
404NOT_FOUNDAWB not on file, or courier returned no scan events

Recommended Client Flow

A typical mobile-app implementation:

  1. On launch, call get_tracking_settings. If track_enable = "0", hide the tracking entry point.
  2. When the user opens the tracking screen, branch on track_method:
    • "0" — show both AWB and order inputs
    • "1" — only order number (optionally with email/phone)
    • "2" — only AWB
  3. On submit, call awb_login or awb_login_with_order to validate.
  4. Pass the returned awb to get_tracking_info.
  5. Render the result; refresh on pull-to-reload, capped at one refresh per minute.

Caching guidance

ParameterTypeRequiredDescription
get_tracking_settingsstringOptionalCache per shop. TTL: 1 hour.
awb_loginstringOptionalDo not cache.
awb_login_with_orderstringOptionalDo not cache.
get_tracking_infostringOptionalCache per shop + AWB. TTL: 2–5 minutes.

Best Practices

  • Branch on code, display message. code is stable; message may be merchant-customised or localised.
  • Always check success before reading data.
  • Encode bodies as application/x-www-form-urlencoded. Multipart works but adds overhead.
  • Pin certificates at the chain root (Let's Encrypt) if your threat model warrants it.
  • Strip PII from logs. Email, mobile, address, customer name are personal data.
  • Show actionable errors. NOT_FOUND → "Couldn't find that order"; PLAN_LIMIT → "Please contact the merchant"; UNAUTHORIZED → "Please re-authenticate".

FAQ & Troubleshooting

I get 403 FORBIDDEN — App not installed!

The shop domain doesn't match an installed ILS app. Common causes: typo in the shop domain (must end in .myshopify.com); the merchant uninstalled the app; payment status lapsed.

I get 403 PLAN_LIMIT

The merchant's plan does not include Customer Tracking. Upgrade to Advanced or Gold, or contact ILS for a custom plan.

I get 401 UNAUTHORIZED with a valid-looking token

Confirm: (a) header name is exactly Auth-Token with the hyphen; (b) the token belongs to the same shop as in shop; (c) the token hasn't been rotated. Request a fresh token if unsure.

get_tracking_info returns 404 for an AWB that just shipped

Carriers can take 30–60 minutes to surface the first scan event. Show the order as "Ready to ship" and let the user retry.

Are there rate limits?

No hard per-token limits today, but please stay under 60 requests/min/shop. Abusive traffic may be throttled at the WAF.

Can I receive tracking updates via webhook?

Not on the public roadmap today. The recommended pattern is client-side polling with a 2–5 minute cadence.

Is there a sandbox environment?

Not currently. You can request a test token against a development store (*-test.myshopify.com) the same way as a production token.

Support

  • Email: support@shopiapps.in
  • Subject prefix: [Mobile API] for faster triage, [SECURITY] for verified security issues
  • Include in your message: shop domain, request action, full request body (redact the Auth-Token), the response JSON, and a timestamp with timezone

Resources