Integrate your system with Shipper to create and track orders programmatically.
The Shipper Open API is a REST API that allows you to:
https://server.shipper.network/api/v1To get started, generate an API key from your Shipper dashboardunder Integrations > API.
All API requests require a Bearer token in the Authorization header.
Authorization: Bearer YOUR_API_KEY
Accept: application/json
Content-Type: application/jsonRequests are rate-limited per API key. Exceeding the limit returns HTTP 429 (Too Many Requests).
| Endpoint Type | Limit | Endpoints |
|---|---|---|
| Read | 120req/min | GET /products, /orders, /orders/:id, /webhooks |
| Reference | 60req/min | GET /countries, /countries/:code/divisions |
| Write | 30req/min | POST /orders, /webhooks |
| Destructive | 20req/min | POST /orders/:id/cancel, DELETE /webhooks/:id |
Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining) are included in every response.
GET /countriesReturns all supported countries with their geographic structure. Use this to understand which address division levels (governorate, delegation, etc.) are required for each country.
curl https://server.shipper.network/api/v1/countries \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"200
[
{
"code": "TN",
"name": "Tunisia",
"geo_structure": {
"fields": [
{ "id": "division_1_id", "label": "Governorate", "level": 1, "required": true, "depends_on": null },
{ "id": "division_2_id", "label": "Delegation", "level": 2, "required": true, "depends_on": "division_1_id" }
]
}
}
]The geo_structure.fields array tells you how many division levels exist, their labels, and dependencies. Use depends_on to build cascading dropdowns.
GET /countries/{code}/divisionsReturns division names for a country. Call this to get valid values for address.division_1 and address.division_2 when creating orders.
| Parameter | Type | Required | Description |
|---|---|---|---|
level | integer | - | Division level (default: 1 = top level) |
parent_id | integer | - | Filter by parent division ID to get children |
curl https://server.shipper.network/api/v1/countries/TN/divisions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"{
"country": "TN",
"geo_structure": { ... },
"divisions": [
{ "id": 1, "name": "Tunis", "level": 1, "parent_id": null },
{ "id": 2, "name": "Sfax", "level": 1, "parent_id": null },
{ "id": 3, "name": "Sousse", "level": 1, "parent_id": null }
]
}curl "https://server.shipper.network/api/v1/countries/TN/divisions?level=2&parent_id=1" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"{
"country": "TN",
"geo_structure": { ... },
"divisions": [
{ "id": 10, "name": "Bab El Bhar", "level": 2, "parent_id": 1 },
{ "id": 11, "name": "Bab Souika", "level": 2, "parent_id": 1 },
{ "id": 12, "name": "Carthage", "level": 2, "parent_id": 1 }
]
}GET /productsReturns your product catalog with UUIDs. You need product UUIDs to create orders via the API.
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | - | Page number (default: 1) |
per_page | integer | - | Items per page (default: 20, max: 100) |
search | string | - | Search by product name or SKU |
curl "https://server.shipper.network/api/v1/products?per_page=10&search=shirt" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"200
{
"data": [
{
"uuid": "2e97b37a-3118-4905-842c-19706bcf1455",
"name": "Blue T-Shirt",
"sku": "SKU-001",
"price": 190,
"status": "active",
"available_qte": 42,
"variants": [
{
"uuid": "a1b2c3d4-5678-9012-abcd-ef0123456789",
"name": "Size L",
"sku": "SKU-001-L",
"price": 190,
"available_qte": 15
}
]
}
],
"pagination": { "total": 50, "current_page": 1, "per_page": 10, "last_page": 5 }
}GET /products/{uuid}Returns a single product by UUID, including its variants and available stock quantities.
curl https://server.shipper.network/api/v1/products/2e97b37a-3118-4905-842c-19706bcf1455 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"200
{
"uuid": "2e97b37a-3118-4905-842c-19706bcf1455",
"name": "Blue T-Shirt",
"sku": "SKU-001",
"price": 190,
"status": "active",
"available_qte": 42,
"variants": [
{ "uuid": "a1b2c3d4-...", "name": "Size L", "sku": "SKU-001-L", "price": 190, "available_qte": 15 },
{ "uuid": "e5f6a7b8-...", "name": "Size M", "sku": "SKU-001-M", "price": 190, "available_qte": 27 }
]
}GET /ordersReturns a paginated list of your orders. Use filters to narrow results by status, date range, or search terms.
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | - | Page number (default: 1) |
per_page | integer | - | Items per page (default: 20, max: 100) |
status | string | - | Filter by shipment status (e.g. delivered, awaitingpackaging, canceled) |
start_date | string | - | Filter orders created on or after this date (YYYY-MM-DD) |
end_date | string | - | Filter orders created on or before this date (YYYY-MM-DD) |
search | string | - | Search by order ID, customer name, or phone number |
curl "https://server.shipper.network/api/v1/orders?status=delivered&start_date=2025-01-01&per_page=10" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"200
{
"data": [
{
"id": 555,
"status": "Paid",
"store_name": "My Store",
"is_cod": true,
"is_paid": true,
"created_at": "2025-01-10T10:00:00.000000Z",
"address": { "first_name": "Omar", "last_name": "Ben Ali", "phone1": "+21628177188" },
"items_count": 2,
"shipments_count": 1,
"external_order_id": "ORD-123",
"external_order_url": "https://mystore.com/orders/123"
}
],
"pagination": { "total": 150, "current_page": 1, "per_page": 10, "last_page": 15 }
}POST /ordersCreates a new order in Shipper. Returns the order ID which you can use to track the order.
| Parameter | Type | Required | Description |
|---|---|---|---|
address | object | * | Recipient address object (see fields below) |
address.name | string | * | Full name - automatically split into first and last name |
address.phone1 | string | * | Primary phone number |
address.phone2 | string | - | Secondary phone number |
address.address1 | string | * | Street address line 1 |
address.address2 | string | - | Street address line 2 |
address.division_1 | string | - | State / Governorate - use exact name from GET /countries/{code}/divisions |
address.division_2 | string | - | City / Delegation - use exact name from GET /countries/{code}/divisions?level=2&parent_id=... |
address.country | string | * | Two-letter country code (e.g. TN, DZ, MA) |
items | array | * | Array of order line items |
items[].id | string | * | Product or variant UUID from GET /products |
items[].quantity | integer | - | Quantity (default: 1) |
items[].total_price | number | - | Total price for this line. Defaults to product price x quantity |
shipping_total | number | - | Shipping cost charged to the customer (default: 0) |
is_cod | boolean | - | ) |
auto_fulfill | boolean | - | Send directly to fulfillment without creating a draft (default: false) |
with_confirmation | boolean | - | Require a phone confirmation call before shipping (default: false) |
store_name | string | - | Source store name - appears in order tracking for your reference |
external_order_id | string | - | Your system order reference ID - displayed in the Shipper dashboard |
external_order_url | string | - | URL to the order in your system - clickable from the Shipper dashboard |
curl -X POST https://server.shipper.network/api/v1/orders \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"address": {
"name": "Omar Ben Ali",
"division_1": "Tunis",
"division_2": null,
"phone1": "28177188",
"address1": "Rue de la Liberte",
"country": "TN"
},
"items": [
{ "quantity": 1, "id": "YOUR_PRODUCT_UUID", "total_price": 190 }
],
"shipping_total": 9,
"is_cod": true,
"auto_fulfill": true,
"with_confirmation": true,
"store_name": "My Store",
"external_order_id": "ORD-12345",
"external_order_url": "https://mystore.com/orders/12345"
}'201Created
{
"id": 555
}GET /orders/{id}Returns full order details including current status, shipment tracking numbers with delivery logs, and the recipient address.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | * | The order ID returned when the order was created |
curl https://server.shipper.network/api/v1/orders/555 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"200
{
"id": 555,
"status": "Paid",
"contact_task_status": "called",
"shipments": [
{
"uuid": "11111111-2222-3333-4444-555555555555",
"status": "delivered",
"tracking_numbers": [
{
"number": "99999999999",
"logs": [
{ "status": "at_carrier_origin_facility", "created_at": "2025-01-15T02:02:59.000000Z", "external_timestamp": "2025-01-14 22:48:00" },
{ "status": "in_transit", "created_at": "2025-01-15T02:02:59.000000Z", "external_timestamp": "2025-01-14 23:05:00" },
{ "status": "delivered", "created_at": "2025-01-18T02:09:19.000000Z", "external_timestamp": "2025-01-17 14:16:00" }
]
}
]
}
],
"address": {
"first_name": "Omar", "last_name": "Ben Ali",
"phone1": "+21628177188", "phone2": "",
"address1": "Rue de la Liberte", "address2": "",
"division_1": "Tunis", "division_2": null, "country": "TN"
}
}POST /orders/{id}/cancelCancels an order and its associated shipments. This works for orders that are still in the confirmation phase (awaiting phone confirmation) or in the preparation phase (awaiting packaging). Orders that have already been shipped cannot be canceled via the API.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | * | The order ID to cancel |
curl -X POST https://server.shipper.network/api/v1/orders/555/cancel \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"200
{
"success": true,
"canceled_shipments": 1
}422If no shipments can be canceled:
{
"error": "No shipments available to cancel"
}Webhooks let you receive real-time HTTP notifications when order or shipment statuses change, instead of polling the API.
POST /webhooksRegister a URL to receive webhook events. You can register up to 5 webhooks per API key. The URL must use HTTPS.
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | * | HTTPS URL that will receive POST requests with event data |
events | array | * | Events to subscribe to: "order.status_changed" and/or "shipment.status_changed" |
secret | string | - | Secret key (min 16 chars) used to sign payloads with HMAC-SHA256 for verification |
curl -X POST https://server.shipper.network/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"url": "https://mystore.com/webhooks/shipper",
"events": ["order.status_changed", "shipment.status_changed"],
"secret": "my-webhook-secret-key"
}'201
{
"id": 1,
"url": "https://mystore.com/webhooks/shipper",
"events": ["order.status_changed", "shipment.status_changed"],
"status": "active",
"created_at": "2025-03-29T10:00:00.000000Z"
}GET /webhooksReturns all webhooks registered for the current API key, including their status and failure count.
curl https://server.shipper.network/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"200
[
{
"id": 1,
"url": "https://mystore.com/webhooks/shipper",
"events": ["order.status_changed", "shipment.status_changed"],
"status": "active",
"last_triggered_at": "2025-03-29T15:30:00.000000Z",
"failure_count": 0,
"created_at": "2025-03-29T10:00:00.000000Z"
}
]DELETE /webhooks/{id}Removes a webhook registration. The webhook will stop receiving events immediately.
curl -X DELETE https://server.shipper.network/api/v1/webhooks/1 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"200
{ "success": true }When an event occurs, Shipper sends a POST request to your registered URL with a JSON body:
{
"event": "order.status_changed",
"timestamp": "2025-01-15T10:30:00.000000Z",
"data": {
"order_id": 555,
"status": "Paid",
"previous_status": "Pending"
}
}{
"event": "shipment.status_changed",
"timestamp": "2025-01-15T10:30:00.000000Z",
"data": {
"order_id": 555,
"shipment_uuid": "11111111-2222-3333-4444-555555555555",
"status": "delivered",
"previous_status": "onitsway"
}
}If you provided a secret when registering the webhook, each request includes an X-Shipper-Signature header. Verify it by computing the HMAC-SHA256 of the raw request body using your secret:
// Node.js example
const crypto = require('crypto');
const signature = crypto.createHmac('sha256', 'my-webhook-secret-key')
.update(rawBody)
.digest('hex');
const isValid = signature === req.headers['x-shipper-signature'];The API uses standard HTTP status codes. All error responses include a JSON body with details.
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Resource created successfully |
| 401 | Unauthorized - missing or invalid API key |
| 403 | Forbidden - API key is disabled or revoked |
| 404 | Not found - the requested resource does not exist |
| 422 | Validation error - check the errors object in the response body for details |
| 429 | Too Many Requests - rate limit exceeded. Wait and retry |