Overview & Architecture
The AsanShipping Developer API is a high-throughput, multi-tenant RESTful API. It is designed to act as the single source of truth for your e-commerce operations across Pakistan.
Tenant Data Isolation
Every API key is bound cryptographically to a single merchant organization. Cross-tenant access is strictly prohibited at the database engine level.
Sub-100ms Latency
Optimized indexing on MongoDB clusters, Redis caching layers, and high-performance routing ensure instantaneous order creation and inventory syncing.
Idempotency Guarantee
Network dropouts and automated retries never cause duplicate orders or phantom inventory deductions thanks to 24-hour idempotent transaction caching.
Base URLs
| Environment | Base URL | Purpose |
|---|---|---|
| Live Production | https://merchant-api.asanshipping.com/api/v1 | Production store integrations & live order processing |
| Local / Sandbox | http://localhost:5000/api/v1 | Local staging, CI/CD pipelines & test suites |
Authentication & Granular Scopes
All requests to the Developer API must be authenticated with an API key. You can pass the key in two ways:
Authorization: Bearer as_live_79a2f10b...X-API-Key: as_live_79a2f10b...Supported Key Prefixes
Live production secret key for backend server integrations.
Sandbox test key for staging environments and mock suites.
Public storefront key restricted to read-only catalog queries.
Granular Permission Scopes
| Scope Identifier | Permission & Capabilities |
|---|---|
| products:read | View catalog products, variant details, pricing, and image URLs. |
| products:write | Create, update, archive products, and upload WebP images to Backblaze B2. |
| inventory:read | Access real-time inventory ledger quantities and warehouse stock balances. |
| inventory:write | Submit authoritative stock adjustments (restocks, write-offs) and warehouse transfers. |
| orders:read | Query canonical order records, customer details, consignments, and tracking. |
| orders:write | Create canonical customer orders with automated destination validation and phone normalization. |
| orders:cancel | Cancel pending or unbooked orders and trigger automatic inventory restocks. |
| tracking:read | Fetch public and authenticated shipment tracking milestones and carrier progress. |
| locations:read | Access all available Pakistani delivery cities, provinces, tiers, and sub-areas. |
| tools:execute | Intelligently parse unstructured single or bulk address strings to extract phone numbers, cities, and sub-areas. |
| webhooks:manage | Register, update, delete webhook subscriptions, dispatch tests, and inspect audit logs. |
Idempotency Protocol
The AsanShipping API supports idempotent mutations using the Idempotency-Key HTTP header on POST /orders, POST /inventory/adjustments, and POST /products.
- Generate a unique UUIDv4 string on your client (e.g.
b84c8491-03fc-4b57-a384-93e1b00e3182) representing the transaction. - Pass this string in the
Idempotency-Keyheader of your request. - If the request succeeds, AsanShipping stores the full status code and response payload for 24 hours.
- If network failure causes your client to retry the request with the identical key, AsanShipping returns the cached response with the header
X-Cache-Lookup: IDEMPOTENT_HITwithout executing any database mutations a second time.
Rate Limits & Unified Error Protocol
Live Production Rate Limit
Burst tolerance of 30 concurrent calls. Monitored per merchant account.
Test Sandbox Rate Limit
Burst tolerance of 15 concurrent calls for staging environments.
Standardized Error Response Body
{
"success": false,
"error": {
"code": "INSUFFICIENT_STOCK",
"message": "Requested quantity (5) exceeds available stock (2) for SKU 'TS-BLK-M'",
"details": { "sku": "TS-BLK-M", "requested": 5, "available": 2 },
"timestamp": "2026-09-26T14:00:00.000Z"
},
"meta": {
"requestId": "req_01J8EF89X...",
"documentation": "https://asanshipping.com/developers#rate-limits"
}
}Error Code Catalog
| Status | Error Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing, expired, or malformed API Key or Bearer token. |
| 403 | FORBIDDEN_INSUFFICIENT_SCOPE | The provided API key does not have the required permission scope for this action. |
| 400 | INVALID_IDEMPOTENCY_KEY | Missing, malformed, or previously mismatched Idempotency-Key header. |
| 409 | IDEMPOTENCY_PAYLOAD_MISMATCH | A request with this Idempotency-Key was already executed with different parameters. |
| 429 | RATE_LIMIT_EXCEEDED | Exceeded the allowed requests per minute (120 req/min Live, 60 req/min Test). Check Retry-After header. |
| 400 | VALIDATION_ERROR | One or more request parameters failed schema validation (e.g. invalid phone or negative price). |
| 422 | INSUFFICIENT_STOCK | Requested item quantity exceeds authoritative available ledger balance. |
| 400 | ORDER_ALREADY_DISPATCHED | Cannot cancel or modify an order that has already been booked or dispatched with a courier. |
| 404 | RESOURCE_NOT_FOUND | The requested product, order, transfer, or webhook ID does not exist for this merchant. |
| 401 | WEBHOOK_VERIFICATION_FAILED | The HMAC-SHA256 signature mismatch on an inbound webhook payload. |
Store Profile
Endpoints to inspect the active merchant profile, operational currency, timezone, and configured warehouse fulfillment locations.
Get Store Profile
Retrieve store identity, default currency (PKR), timezone, operational environment, and configured warehouse facilities.
Request Headers
| Header Name | Type | Description |
|---|---|---|
| Authorization * | string | Bearer <YOUR_API_KEY> |
Catalog & Products
Manage catalog items, multi-variant SKUs, pricing, barcode attributes, and upload WebP images backed by Backblaze B2 object storage.
List Products
Retrieve a paginated, filterable list of catalog products with variant stock and media URLs.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| page | integer | Page number (default: 1) |
| limit | integer | Records per page (1-100, default: 25) |
| search | string | Search by title or SKU |
| sku | string | Filter by exact SKU |
| status | string | Product status: active | draft | archived |
| stockStatus | string | Stock filter: IN_STOCK | OUT_OF_STOCK |
| updatedSince | string (ISO) | Fetch only products modified after timestamp |
Create Product
Add a new product with variant definitions, initial inventory allocation, and dimensional weight metrics.
Request Headers
| Header Name | Type | Description |
|---|---|---|
| Idempotency-Key * | string (UUIDv4) | Unique key preventing duplicate creations |
| Content-Type * | string | application/json |
Request Body Schema
| Field | Type | Description |
|---|---|---|
| sku * | string | Unique stock keeping unit |
| title * | string | Product display title |
| price * | number | Retail selling price in PKR |
| compareAtPrice | number | Original strike-through price |
| costPrice | number | Internal unit cost for margin analytics |
| weightGrams * | integer | Parcel weight in grams (used for freight calculations) |
| inventory * | object | { warehouseId: string, quantity: number } |
Upload Product Image
Upload a JPG/PNG/WebP image (up to 10MB). The server automatically compresses and optimizes the file to high-density WebP and stores it on Backblaze B2.
Request Headers
| Header Name | Type | Description |
|---|---|---|
| Content-Type * | string | multipart/form-data |
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id * | string | Product MongoDB ID or SKU |
Authoritative Inventory Ledger
Multi-node warehouse stock synchronization. Perform atomic adjustments (restocks, cycle counts, damage writes) and cross-warehouse transfers with full audit logging.
Query Inventory Ledger
Retrieve real-time available, reserved, and incoming stock balances grouped by warehouse and SKU.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| sku | string | Filter by exact SKU |
| warehouseId | string | Filter by warehouse node |
| lowStockOnly | boolean | Filter items below reorder threshold |
Adjust Stock Ledger
Submit an atomic delta change to a SKU stock ledger (e.g. +50 for inbound supplier restock, -2 for transit damage).
Request Body Schema
| Field | Type | Description |
|---|---|---|
| sku * | string | Product SKU |
| warehouseId * | string | Target warehouse node |
| quantityDelta * | integer | Positive or negative integer delta |
| reason * | string | RESTOCK | CYCLE_COUNT | DAMAGE | RETURN | AUDIT |
| referenceId | string | Supplier PO or Inspection Ticket ID |
Canonical Orders & Idempotency
Ingest e-commerce orders, validate recipient delivery addresses, reserve stock atomically, and execute order cancellations with automatic stock restoration.
Create Canonical Order
Submit a new customer order. Automatically validates destination city & subarea against AsanShipping directory, normalizes Pakistani mobile numbers to E.164 (+92), calculates order totals, reserves inventory, and logs an idempotent transaction.
Request Headers
| Header Name | Type | Description |
|---|---|---|
| Idempotency-Key * | string (UUIDv4) | Unique idempotency key (e.g. checkout session or cart ID) |
| Content-Type * | string | application/json |
Request Body Schema
| Field | Type | Description |
|---|---|---|
| orderNumber * | string | Storefront unique order identifier |
| customer * | object | { name, phone (03xx / +92), email } |
| shippingAddress * | object | { address1, city, subarea, postalCode } |
| items * | array | Array of [{ sku, quantity, unitPrice }] |
| paymentMethod * | string | COD | PREPAID |
| shippingFee | number | Freight charged to customer |
Cancel Order
Cancel an unbooked order and immediately return reserved line item stock back to the warehouse ledger balance.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id * | string | Order ID or orderNumber |
Request Body Schema
| Field | Type | Description |
|---|---|---|
| reason * | string | CUSTOMER_REQUEST | FRAUD_SUSPECT | OUT_OF_STOCK | DUPLICATE |
| restockInventory | boolean | Whether to return reserved items to available inventory (default: true) |
Public Tracking
Public and customer-facing shipment status lookup. Provides courier journey milestones, delivery timestamps, and real-time COD collection state.
Get Tracking Milestones
Fetch the chronological courier milestone timeline, courier dispatch info, and current delivery state for a tracking number.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| trackingNumber * | string | AsanTracking number or courier CN |
Delivery Locations & Cities
Access verified delivery cities, provinces, logistics tiers, and sub-area coverage zones across Pakistan. Ideal for populating checkout city dropdowns, calculating transit times, and validating delivery availability.
Get All Delivery Cities & Areas
Retrieve a complete list of delivery cities with optional search queries, province filtering, and sub-area sector listings.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| search | string | Filter cities by name or partial string (case-insensitive) |
| province | string | Filter cities by province (e.g. Punjab, Sindh, KPK, Balochistan, ICT) |
| includeAreas | boolean | Whether to include detailed sub-areas and sectors (default: true) |
| page | number | Page number for paginated results (default: 1) |
| limit | number | Maximum cities to return per page (default: 1000, max: 2000) |
Address & Phone Reader Tool
Intelligent AI & rule-based parser that ingests raw, messy, or unstructured customer text (e.g. from WhatsApp, Instagram DMs, checkout textareas, or CSV spreadsheets) and automatically extracts normalized Pakistani phone numbers (+923...), standardized delivery cities, sub-areas, and clean street addresses with confidence scoring. Supports single strings and high-speed bulk arrays (up to 100 items per request).
Intelligent Address & Phone Parser (Single / Bulk)
Parse a single raw address string or a batch array of address strings into structured delivery parameters.
Request Body Schema
| Field | Type | Description |
|---|---|---|
| text | string | string[] | Raw address string (for single mode) or array of raw address strings (for batch mode) |
| addresses | string[] | Alternative array of raw address strings for batch parsing (max 100 per request) |
Webhooks & Real-time Events
Receive instant HTTP POST callbacks on your server when orders change state, tracking milestones update, or inventory falls below safety thresholds. Every webhook includes an HMAC-SHA256 signature header for payload verification.
Create Webhook Endpoint
Register an HTTPS webhook URL with subscribed topics and custom HMAC secret.
Request Body Schema
| Field | Type | Description |
|---|---|---|
| url * | string (HTTPS) | Your server webhook listener URL |
| events * | array | List of event names to subscribe to |
| secret | string | Custom HMAC signing secret (auto-generated if omitted) |
| description | string | Internal label for this endpoint |
Webhooks & Signature Verification
AsanShipping delivers real-time notifications via HTTPS POST to your registered webhook URL. Every webhook request contains an X-Asan-Signature header generated using HMAC-SHA256.
Supported Webhook Event Topics
| Event Topic | Trigger Condition |
|---|---|
| order.created | Fired immediately after an order is accepted into the system. |
| order.updated | Fired when customer details, delivery address, or consignments are updated. |
| order.cancelled | Fired when an order is cancelled and items are returned to inventory. |
| tracking.milestone_updated | Fired when courier pushes a new tracking scan (Booked, Transit, Out for Delivery, Delivered, RTO). |
| inventory.low_stock | Fired when stock on hand for any SKU drops below its configured reorder safety threshold. |
Verifying Webhook Signatures in Node.js
import crypto from 'crypto';
export function verifyAsanWebhook(rawBodyBuffer, signatureHeader, webhookSecret) {
// signatureHeader format: 'sha256=1727357400.99a818e7f10b2c3d4e5f6a...'
const parts = signatureHeader.split('.');
const timestamp = parts[0].replace('sha256=', '');
const signature = parts[1];
// Prevent replay attacks (reject payloads older than 5 minutes)
const fiveMinutesAgo = Math.floor(Date.now() / 1000) - 300;
if (parseInt(timestamp) < fiveMinutesAgo) {
throw new Error('Webhook timestamp too old');
}
// Compute expected HMAC-SHA256 signature
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(`${timestamp}.${rawBodyBuffer.toString('utf8')}`)
.digest('hex');
// Constant-time comparison to prevent timing attacks
const isValid = crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'));
return isValid;
}Interactive HMAC-SHA256 Signature Calculator
AsanShipping signs every outbound webhook with X-Asan-Signature: sha256=t.signature