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.
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:
| Parameter | Type | Required | Description |
|---|---|---|---|
Auth-Token | string | Required | Long-lived per-shop API token (see Get an API Token). Sent as an HTTP header. Example: a1b2c3... |
shop | string | Required | Shopify 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:
- The ILS app is installed for the shop (
app.app_status = 'installed'). - Payment status is active (
freeoraccepted). - The shop's plan includes the Customer Tracking feature.
- The supplied
Auth-Tokenmatches the token stored for the shop.
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
- Email support@shopiapps.in with the subject "Mobile API token request".
- Include the shop domain (e.g.
examplestore.myshopify.com), the mobile app name/platform (e.g. Acme Shop iOS), and a contact email. - ILS support verifies the shop has an active plan that includes
customer_trackingand provisions the token. - The token is delivered out-of-band (encrypted email or shared secret store).
Token properties
| Property | Value |
|---|---|
Format | Hex string, ~96 characters |
Lifetime | Long-lived (no expiry); rotated on demand |
Scope | Single Shopify shop |
Server storage | settings.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.
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."
}| Parameter | Type | Required | Description |
|---|---|---|---|
success | boolean | Required | true for any 2xx response, false otherwise. Use this for branching. |
code | string | Required | Stable machine code: OK, INVALID_INPUT, UNAUTHORIZED, FORBIDDEN, PLAN_LIMIT, NOT_FOUND, SERVER_ERROR. Safe to switch on. |
message | string | Required | Human-readable message, may be localised per merchant. Safe to display in UI. |
data | object | Optional | Endpoint-specific payload. Empty array on most error responses. |
result | enum | Optional | Legacy v1 alias of success — values: success / fail. |
msg | string | Optional | Legacy v1 alias of message. |
success + code + data.
The legacy result / msg keys are preserved for backward compatibility but will not be expanded.
Status Codes
| HTTP | Code | When you'll see it |
|---|---|---|
| 200 | OK | Successful response |
| 400 | INVALID_INPUT | Missing or malformed parameters; unknown action; field length exceeds limit |
| 401 | UNAUTHORIZED | Missing / invalid Auth-Token; email-phone mismatch on order login |
| 403 | FORBIDDEN | App not installed for this shop, or payment status inactive |
| 403 | PLAN_LIMIT | Customer-tracking feature not in the shop's plan |
| 404 | NOT_FOUND | Order, AWB, or tracking record not present |
| 500 | SERVER_ERROR | Unexpected 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
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
| Parameter | Type | Required | Description |
|---|---|---|---|
Auth-Token | string | Required | Per-shop API token. Example: a1b2c3d4... |
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
shop | string | Required | Shopify shop domain. Example: examplestore.myshopify.com |
action | enum | Required | Must 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
| Parameter | Type | Required | Description |
|---|---|---|---|
data.track_enable | enum | Required | "0" = disabled, "1" = enabled. If 0, hide the tracking entry point. |
data.theme_color | string | Required | Primary hex colour for tracking screens. |
data.font_color | string | Required | Body text colour (hex). |
data.button_font_color | string | Required | CTA button text colour (hex). |
data.additional_css | string | Optional | CSS injected on the web tracker. Mobile clients should ignore. |
data.date_formate | string | Required | PHP-style date format hint (e.g. F dS Y). Prefer your platform's locale APIs for rendering. |
data.time_formate | string | Required | PHP-style time format hint. |
data.track_method | enum | Required | "0": order number OR AWB · "1": only order number · "2": only AWB. |
data.email_enable | enum | Required | "1" = show email/phone field on the order-login form. |
data.email_required | enum | Required | "1" = email/phone is mandatory. |
data.login_page_description | string | Required | Display above the login form. |
data.login_button_text | string | Required | CTA label. |
data.show_login_banner | enum | Required | "1" = show banner on the login screen. |
data.login_banner_image_url | string | Optional | Absolute URL of the banner image (empty when disabled). |
data.login_banner_position | enum | Optional | "0" = left, "1" = right. |
data.view_content | array | Required | Subset of orderdetail, productlist, trackingHistory, trackingCompany — sections to render after login. |
data.show_track_process_icon | enum | Required | "1" = show the 5-step progress bar. |
data.track_process_steps | object | Required | Localised step labels: order, ready_ship, transit, out_delivery, delivered. |
data.show_promotion_banner | enum | Optional | "1" = show promo banner carousel on the result screen. |
data.promotion_images | array | Optional | Array of {name, link} objects (image URL + click target). |
data.show_track_detail_header | enum | Optional | "1" = show merchant HTML above the tracking detail. |
data.track_detail_header_content | string | Optional | HTML to render above the tracking detail (when enabled). |
data.show_track_detail_footer | enum | Optional | "1" = show merchant HTML below the tracking detail. |
data.track_detail_footer_content | string | Optional | HTML to render below the tracking detail. |
Errors
| HTTP | Code | When you'll see it |
|---|---|---|
| 401 | UNAUTHORIZED | Missing / invalid Auth-Token |
| 403 | FORBIDDEN | App not installed |
| 403 | PLAN_LIMIT | Customer tracking not in plan |
Login by AWB
Verifies that an AWB (tracking) number belongs to the shop. Use this when the user chose "Track by AWB".
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
Auth-Token | string | Required | Per-shop API token. |
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
shop | string | Required | Shopify shop domain. Example: examplestore.myshopify.com |
action | enum | Required | Constant. Example: awb_login |
awb_no | string | Required | Tracking 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
| Parameter | Type | Required | Description |
|---|---|---|---|
data.awb | string | Required | Confirmed AWB number. Pass this to Get Tracking Details to load the full payload. |
awb | string | Optional | Legacy alias of data.awb. |
Errors
| HTTP | Code | When you'll see it |
|---|---|---|
| 400 | INVALID_INPUT | awb_no missing or longer than 64 characters |
| 404 | NOT_FOUND | AWB not on file for this shop |
Login by 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
| Parameter | Type | Required | Description |
|---|---|---|---|
Auth-Token | string | Required | Per-shop API token. |
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
shop | string | Required | Shopify shop domain. Example: examplestore.myshopify.com |
action | enum | Required | Constant. Example: awb_login_with_order |
order_name | string | Required | Order number (max 64 chars). Shopify display name like #1042 or plain numeric.Example: #1042 |
order_email | string | Optional | Email 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
| Parameter | Type | Required | Description |
|---|---|---|---|
data.awb | string | Required | Most recent (primary) AWB for the order. Pass to Get Tracking Details. |
data.multiple_tracking[] | array | Required | All shipments for the order — an order may ship in multiple parcels. |
data.multiple_tracking[].number | string | Required | AWB number of this shipment. |
data.multiple_tracking[].lineItem[] | array | Required | IDs of the order line-items in this parcel. |
data.multiple_tracking[].company | string | Required | Display name of the courier (e.g. "Delhivery"). |
data.multiple_tracking[].displayStatus | enum | Required | One of pending, in_progress, delivered, failed, return, cancelled, RTO delivered. |
data.multiple_tracking[].current_order_status | string | Optional | Latest free-text status from the courier (e.g. "Out for delivery"). |
Errors
| HTTP | Code | When you'll see it |
|---|---|---|
| 400 | INVALID_INPUT | order_name missing or longer than 64 characters |
| 401 | UNAUTHORIZED | order_email supplied but neither email nor phone matches the order |
| 404 | NOT_FOUND | Order not found, or no packages have been manifested yet |
Get Tracking Details
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.
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
Auth-Token | string | Required | Per-shop API token. |
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
shop | string | Required | Shopify shop domain. Example: examplestore.myshopify.com |
action | enum | Required | Constant. Example: get_tracking_info |
awb_no | string | Required | Tracking 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
| Parameter | Type | Required | Description |
|---|---|---|---|
data.process_step | enum | Required | High-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_data | object | Required | Order summary — see fields below. |
data.order_data.order_name | string | Required | Shopify order display name (e.g. #1042). |
data.order_data.customer_name | string | Optional | First + last name. Falls back to shipping name if customer is missing. |
data.order_data.shipping_address | string | Optional | Single-line, comma-joined shipping address. |
data.order_data.mobile | string | Optional | Best-effort phone (may be empty). |
data.order_data.email | string | Optional | Best-effort email. |
data.order_data.totalPrice | string | Required | Total amount for this AWB. For partial shipments, recalculated for items in this parcel only. |
data.order_data.total_tax | string | Required | Tax component. |
data.order_data.discount | string | Required | Discount applied. |
data.order_data.shipping_charge | string | Required | Shipping cost. |
data.order_data.courier_name | string | Required | Internal courier code (e.g. delhivery, bluedart). |
data.shipment_data | object | Required | Carrier-provided shipment data. |
data.shipment_data.pStatus | enum | Required | Mirror of process_step. |
data.shipment_data.Origin | string | Optional | Carrier origin hub. |
data.shipment_data.Destination | string | Optional | Carrier destination hub. |
data.shipment_data.history[] | array | Required | Scan events, newest first. Each has date, time, status, location. Shape is consistent across all couriers. |
data.lineitems_data[] | array | Required | Line items in this AWB. |
data.lineitems_data[].title | string | Required | Product title. |
data.lineitems_data[].handle | string | Optional | Shopify product handle. |
data.lineitems_data[].unit_price | string | Required | Per-unit price. |
data.lineitems_data[].total_qty | integer | Required | Quantity in this parcel. |
data.lineitems_data[].src | string | Required | Image URL. Falls back to a placeholder when unavailable. |
Errors
| HTTP | Code | When you'll see it |
|---|---|---|
| 400 | INVALID_INPUT | awb_no missing or longer than 64 characters |
| 404 | NOT_FOUND | AWB not on file, or courier returned no scan events |
Recommended Client Flow
A typical mobile-app implementation:
- On launch, call
get_tracking_settings. Iftrack_enable = "0", hide the tracking entry point. - 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
- On submit, call
awb_loginorawb_login_with_orderto validate. - Pass the returned
awbtoget_tracking_info. - Render the result; refresh on pull-to-reload, capped at one refresh per minute.
Caching guidance
| Parameter | Type | Required | Description |
|---|---|---|---|
get_tracking_settings | string | Optional | Cache per shop. TTL: 1 hour. |
awb_login | string | Optional | Do not cache. |
awb_login_with_order | string | Optional | Do not cache. |
get_tracking_info | string | Optional | Cache per shop + AWB. TTL: 2–5 minutes. |
Best Practices
- Branch on
code, displaymessage.codeis stable;messagemay be merchant-customised or localised. - Always check
successbefore readingdata. - 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 theAuth-Token), the response JSON, and a timestamp with timezone