Public API V1 — Developer Guide
Unified Integration Gateway for the ShippingEyes Ecosystem
Data Integration Lifecycle
🛠️ Developer Sandbox Settings
(All cURL, PHP, and JS examples update in real-time)
📋 Public API V1 — Endpoints Summary (23 endpoints)
| Method | Endpoint | Scope | Description |
|---|---|---|---|
| GET | /api/v1/public/locations/countries | shipping:read | List delivery countries |
| GET | /api/v1/public/locations/routes | shipping:read | List all delivery routes |
| GET | /api/v1/public/locations/routes/country/{id} | shipping:read | List routes by country |
| GET | /api/v1/public/locations/cities | shipping:read | List all delivery cities |
| GET | /api/v1/public/locations/cities/country/{id} | shipping:read | List cities by country |
| GET | /api/v1/public/locations/cities/route/{id} | shipping:read | List cities by route |
| GET | /api/v1/public/locations/places | shipping:read | List all delivery places |
| GET | /api/v1/public/locations/places/city/{id} | shipping:read | List places by city |
| GET | /api/v1/public/locations/districts | shipping:read | List all delivery districts |
| GET | /api/v1/public/locations/districts/place/{id} | shipping:read | List districts by place |
| GET | /api/v1/public/shipping/prices | shipping:read | Get shipping prices |
| GET | /api/v1/public/shipping/services | shipping:read | Get shipping services |
| GET | /api/v1/public/shipping/config | shipping:read | Get configuration for order creation |
| GET | /api/v1/public/shipping/config/statuses | shipping:read | Get available order statuses |
| POST | /api/v1/public/orders/create | orders:create | Create new order |
| POST | /api/v1/public/orders/update | orders:create | Update existing order |
| DELETE | /api/v1/public/orders/delete/{order_id} | orders:create | Delete order |
| GET | /api/v1/public/orders/list/{status} | orders:read | List orders by status |
| GET | /api/v1/public/orders/info/{order_id} | orders:read | Get order details |
| POST | /api/v1/public/orders/search | orders:read | Search and Query orders (Ultra High Performance) |
| GET | /api/v1/public/orders/tracking/{order_id} | tracking:read | Get order tracking |
| POST | /api/v1/public/orders/tracking/bulk | tracking:read | Bulk order tracking |
| POST | /api/v1/public/orders/followups/bulk | orders:read | Bulk order followups and history |
🚀 Quick Start Guide
🌐 Base URL
Production:https://your-domain.com/api/v1/public
Development:{APP_URL}/api/v1/public
🔑 Authentication — API Key + Secret
Every request must include two custom headers:
| Header | Type | Required | Description |
|---|---|---|---|
| ShippingEyes-Api-Key | string | ✅ Yes | Your public API key (starts with se_live_ or se_test_) |
| ShippingEyes-Api-Secret | string | ✅ Yes | Your private API secret (64 characters). Never expose publicly. |
| ShippingEyes-Api-Nonce | string | ⚠️ Conditional | Mandatory for POST, PUT, DELETE. Optional for GET. A unique per-request UUID to prevent replay attacks. |
📝 Complete Example — Create Order
curl -X POST https://your-domain.com/api/v1/public/orders/create \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -H "ShippingEyes-Api-Key: se_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345678" \ -H "ShippingEyes-Api-Secret: your_64_char_secret_here..." \ -d '{ "shipping_service": "srv8gJ", "shipment_info": { "pieces": 1, "description": "Electronics package", "order_type": 1, "delivery_type": 1, "allow_opening": 0, "breakable": 0 }, "recipient": { "name": "Ahmed Hassan", "phone": "0910000000", "to_city": "lejRej", "to_place": "mep2bM", "address": "123 Main Street" }, "financial_info": { "amount": 250, "amount_type": 1, "delivery_cost": 50, "cod": 300, "shipping_cost_type": 1 } }'use GuzzleHttp\Client; $client = new Client();
$response = $client->post('https://your-domain.com/api/v1/public/orders/create', [ 'headers' => [ 'ShippingEyes-Api-Key' => 'se_live_...', 'ShippingEyes-Api-Secret' => 'your_secret...', 'Accept' => 'application/json', ], 'json' => [ 'shipping_service' => 'srv8gJ', 'shipment_info' => [ 'pieces' => 1, 'description' => 'Electronics package', 'order_type' => 1, 'delivery_type' => 1 ], 'recipient' => [ 'name' => 'Ahmed Hassan', 'phone' => '0910000000', 'to_city' => 'lejRej', 'to_place' => 'mep2bM' ], 'financial_info' => [ 'amount' => 250, 'amount_type' => 1 ] ]
]); $data = json_decode($response->getBody(), true);const fetch = require('node-fetch'); const createOrder = async () => { const response = await fetch('https://your-domain.com/api/v1/public/orders/create', { method: 'POST', headers: { 'ShippingEyes-Api-Key': 'se_live_...', 'ShippingEyes-Api-Secret': 'your_secret...', 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ shipping_service: 'srv8gJ', shipment_info: { pieces: 1, order_type: 1 }, recipient: { name: 'Ahmed Hassan', phone: '0910000000', to_city: 'lejRej' }, financial_info: { amount: 250, amount_type: 1 } }) }); const data = await response.json(); console.log(data);
};{ "status": 200, "message": "success", "meta": { "api_version": "1.0.0", "timestamp": "2026-04-30T10:45:00Z", "request_id": "uuid-string", "rate_limit": { "limit": 30, "unit": "requests per minute", "note": "Standard creation quota." } }, "order_id": "ord4cN", "order_snum": 10042
}📱 Required Headers for All Requests
| Header | Value | Required |
|---|---|---|
| Content-Type | application/json | POST requests |
| Accept | application/json | All requests |
| ShippingEyes-Api-Key | Your API key | ✅ Always |
| ShippingEyes-Api-Secret | Your API secret | ✅ Always |
📌 ID Format Convention
"lejRej", "mep2bM"). Never assume IDs are numeric or sequential. Treat them as opaque strings. 🔒 Security Layers (6-Layer Middleware Stack)
| # | Layer | Purpose |
|---|---|---|
| 1 | HTTPS Enforcement | Blocks all non-HTTPS requests |
| 2 | Rate Limiting | Dynamic quotas (3/5min to 60/min) based on endpoint sensitivity |
| 3 | API Key Auth | Validates key + secret + IP whitelist |
| 4 | Scope Check | Verifies endpoint permission |
| 5 | Response Filter | Strips sensitive internal fields |
| 6 | Audit Log | Logs every request for forensics |
📌 Order Status Reference
Use these status keys to interpret the status_key field in responses and to filter the view-orders-list endpoint.
| Key | Status Name | Description | Workflow Stage |
|---|---|---|---|
| 1 | New | Order created but not yet processed | Merchant Office |
| 2 | Processing | Order confirmed and being prepared | Warehouse |
| 10 | Picked Up | Package collected from merchant | In Transit |
| 20 | At Branch | Package arrived at destination branch | Local Sorting |
| 22 | Out for Delivery | Delegate is delivering to customer | Final Mile |
| 30 | Delivered | Package delivered and paid | Completed |
| 40 | Returned | Order rejected/returned to merchant | Closed |
| 50 | Cancelled | Order cancelled before pickup | Closed |
🔢 Pagination Standard
All list endpoints use a standard metadata object to handle large data sets:
{ "data": [...], "meta": { "api_version": "1.0.0", "timestamp": "2026-04-30T10:45:00Z", "rate_limit": { "limit": 60, "unit": "requests per minute", "note": "Real-time monitoring quota. No-cache enforced." }, "current_page": 1, "last_page": 12, "per_page": 20, "total": 235, "next_page_url": "...?page=2", "prev_page_url": null }
}🔑 Authentication
Unlike the mobile app API that uses Bearer tokens (login + password), the Public API uses pre-generated API credentials:
1. Obtain Your Credentials
Your API Key and Secret are generated by the system administrator. You receive them once:
{ "ShippingEyes-Api-Key": "se_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345678", "ShippingEyes-Api-Secret": "x9K2mP7qR4tL8wN3vB6jH1dF5gY0sA..."
}2. Include in Every Request
ShippingEyes-Api-Key: se_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345678 ShippingEyes-Api-Secret: x9K2mP7qR4tL8wN3vB6jH1dF5gY0sA...
3. Authentication Errors
{ "status": "error", "message": "authentication_required", "meta": { "api_version": "1.0.0", "timestamp": "2026-04-30T10:45:00Z", "request_id": "uuid-string", "rate_limit": { "limit": 60, "unit": "requests per minute", "note": "Standard authentication quota." } }
}{ "status": "error", "message": "invalid_credentials", "meta": { "api_version": "1.0.0", "timestamp": "2026-04-30T10:45:00Z", "request_id": "uuid-string", "rate_limit": { "limit": 60, "unit": "requests per minute", "note": "Standard authentication quota." } }
}4. Implementation Examples
Integrate ShippingEyes into your stack using these boilerplate examples:
Shell / cURL
curl -X GET "https://api.shippingeyes.com/v1/orders" \ -H "ShippingEyes-Api-Key: YOUR_API_KEY" \ -H "ShippingEyes-Api-Secret: YOUR_API_SECRET" \ -H "Accept: application/json"
PHP (cURL)
<?php $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://api.shippingeyes.com/v1/orders"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "ShippingEyes-Api-Key: YOUR_API_KEY", "ShippingEyes-Api-Secret: YOUR_API_SECRET", "Accept: application/json" ]); $response = curl_exec($ch); curl_close($ch); echo $response; ?>
Node.js (axios)
const axios = require('axios'); axios.get('https://api.shippingeyes.com/v1/orders', { headers: { 'ShippingEyes-Api-Key': 'YOUR_API_KEY', 'ShippingEyes-Api-Secret': 'YOUR_API_SECRET', 'Accept': 'application/json' }
})
.then(response => console.log(response.data))
.catch(error => console.error(error));Python (requests)
import requests url = "https://api.shippingeyes.com/v1/orders"
headers = { "ShippingEyes-Api-Key": "YOUR_API_KEY", "ShippingEyes-Api-Secret": "YOUR_API_SECRET", "Accept": "application/json"
} response = requests.get(url, headers=headers)
print(response.json()){ "status": "error", "message": "ip_forbidden", "meta": { "api_version": "1.0.0", "timestamp": "2026-04-30T10:45:00Z", "request_id": "uuid-string", "rate_limit": { "limit": 60, "unit": "requests per minute", "note": "Standard authentication quota." } }
}{ "status": "error", "message": "too_many_requests", "meta": { "api_version": "1.0.0", "timestamp": "2026-04-30T10:45:00Z", "request_id": "uuid-string", "rate_limit": { "limit": 60, "unit": "requests per minute", "note": "Quota exceeded. Check headers for retry time." } }
}- Never expose your API Secret in client-side code, Git repos, or public URLs
- IP Whitelist: If configured, only requests from allowed IPs will be accepted
- Auto-Disable: After multiple consecutive failed secret attempts, the key is automatically disabled
- Rotation: Keys should be rotated periodically as a security best practice
🧪 Test Environment (Test Mode)
We provide a safe sandbox environment to test your integration without affecting live data or real balances.
- Request a Test API Key from the system administrator (keys starting with
se_test_). - Use the exact same endpoints and URLs as production.
- When using a
se_test_key, orders are fully validated and saved, but they are flagged internally as test orders. They will not trigger dispatchers, billing, or real logistics workflows.
⚡ Webhooks & Real-time Notifications
Webhooks allow your application to receive real-time notifications about events in the ShippingEyes ecosystem. Instead of polling the API, we push data to your server as soon as an event occurs.
order.created
Sync inventory the moment an order hits our system.
order.dispatched
Notify your customers immediately when the courier leaves.
order.delivered
Trigger automated reviews or loyalty points instantly.
order.updated
Real-time tracking for every step of the journey.
🚀 Marketing Edge: Why Webhooks?
Webhooks transform your integration from a "pull" system to an Active Intelligence Engine. Instead of wasting server resources asking "Is it delivered yet?", ShippingEyes shouts "It's delivered!" the millisecond it happens. This enables you to provide an Amazon-like experience to your customers with zero latency.
📑 HTTP Headers
Every webhook POST request includes the following headers for identification and security:
| Header | Value Example | Description |
|---|---|---|
| ShippingEyes-Webhook-Signature | sha256=a1b2c3... | HMAC-SHA256 signature for payload verification. |
| ShippingEyes-Webhook-Event | order.status_updated | The type of event being sent. |
| ShippingEyes-Webhook-ID | uuid-string | Unique identifier for this specific delivery attempt. |
| ShippingEyes-Webhook-Timestamp | 1714838400 | Unix timestamp of when the event was generated. |
🛠️ Webhook Delivery Architecture
Our webhook engine is built for reliability and high performance:
- Queue-Based: Events are dispatched to a background queue to ensure instant response times.
- Retry Policy: If your server is down or returns a non-2xx status, we automatically retry the delivery multiple times using an exponential backoff strategy.
- Auto-Disable: To protect our system, webhooks that repeatedly fail are automatically disabled. Ensure your endpoint is stable and returns a 2xx status promptly.
🔐 Verifying the Signature
Every webhook request includes a ShippingEyes-Webhook-Signature header. This is a sha256 hash of the raw JSON body signed with your Webhook Secret.
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_SHIPPINGEYES_WEBHOOK_SIGNATURE'] ?? '';
$secret = 'your_webhook_secret_here'; $expected = 'sha256=' . hash_hmac('sha256', $payload, $secret); if (hash_equals($expected, $signature)) { // ✅ Valid request $data = json_decode($payload, true);
} else { // ❌ Invalid signature http_response_code(401);
}const crypto = require('crypto'); app.post('/webhook', (req, res) => { const signature = req.headers['shippingeyes-webhook-signature']; const secret = 'your_webhook_secret_here'; const payload = JSON.stringify(req.body); const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(payload) .digest('hex'); // Timing-safe comparison const signatureBuffer = Buffer.from(signature || '', 'utf8'); const expectedBuffer = Buffer.from(expected, 'utf8'); if (signatureBuffer.length === expectedBuffer.length && crypto.timingSafeEqual(signatureBuffer, expectedBuffer)) { res.status(200).send('OK'); } else { res.status(401).send('Unauthorized'); }
});📅 Event Catalog
The following events are currently supported by the ShippingEyes platform:
🔔 order.status_updated
Triggered whenever an order's status changes (e.g., from "New" to "Out for Delivery").
{ "event": "order.status_updated", "timestamp": 1714838400, "uuid": "550e8400-e29b-41d4-a716-446655440000", "data": { "order_snum": 10042, "order_status": 22, "status_name": "Out for Delivery", "updated_at": "2026-05-04 15:30:00" }
}💬 order.followup_recorded
Triggered when a new followup or note is added to an order by the delivery agent or customer service.
{ "event": "order.followup_recorded", "timestamp": 1714838400, "uuid": "660f9500-f39c-52d5-b817-557766551111", "data": { "order_snum": 10042, "followup_type": "followup", "notes": "Customer requested delivery after 4 PM", "created_at": "2026-05-04 16:00:00" }
}💰 catalog.shipping_pricing_updated
Triggered when shipping prices in your assigned price package are updated. Useful for syncing prices with your ecommerce store.
{ "event": "catalog.shipping_pricing_updated", "timestamp": 1714838400, "uuid": "770g0600-g40d-63e6-c928-668877662222", "data": { "price_package_id": "pkg9jL", "message": "Shipping prices have been updated", "updated_at": "2026-05-04T16:30:00Z" }
}⚙️ platform.configuration_updated
Triggered when global shipment settings (e.g., allowed order types, closing times) are modified by the system.
{ "event": "platform.configuration_updated", "timestamp": 1714838400, "uuid": "880h1700-h51e-74f7-d039-779988773333", "data": { "message": "Orders creation settings have been updated", "updated_at": "2026-05-04T16:30:00Z" }
}