Shipper Open API

Integrate your system with Shipper to create and track orders programmatically.

v1https://server.shipper.network/api/v1PostmanDownload Postman Collection

Getting Started

The Shipper Open API is a REST API that allows you to:

  • Create orders automatically from your system
  • Retrieve order details and shipment tracking
  • List and search your products and orders
  • Cancel orders programmatically
  • Receive real-time status updates via webhooks

To get started, generate an API key from your Shipper dashboardunder Integrations > API.


Authentication

All API requests require a Bearer token in the Authorization header.

Authorization: Bearer YOUR_API_KEY
Accept: application/json
Content-Type: application/json
Rate Limits

Requests are rate-limited per API key. Exceeding the limit returns HTTP 429 (Too Many Requests).

Endpoint TypeLimitEndpoints
Read120req/minGET /products, /orders, /orders/:id, /webhooks
Reference60req/minGET /countries, /countries/:code/divisions
Write30req/minPOST /orders, /webhooks
Destructive20req/minPOST /orders/:id/cancel, DELETE /webhooks/:id

Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining) are included in every response.


Geography

GETGET /countries

Returns all supported countries with their geographic structure. Use this to understand which address division levels (governorate, delegation, etc.) are required for each country.

Example Request
curl https://server.shipper.network/api/v1/countries \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
Response

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.

GETGET /countries/{code}/divisions

Returns division names for a country. Call this to get valid values for address.division_1 and address.division_2 when creating orders.

Query Parameters
ParameterTypeRequiredDescription
levelinteger-Division level (default: 1 = top level)
parent_idinteger-Filter by parent division ID to get children
Example: Top-level divisions
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 }
  ]
}
Example: Sub-divisions under a parent
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 }
  ]
}

Products

GETGET /products

Returns your product catalog with UUIDs. You need product UUIDs to create orders via the API.

Query Parameters
ParameterTypeRequiredDescription
pageinteger-Page number (default: 1)
per_pageinteger-Items per page (default: 20, max: 100)
searchstring-Search by product name or SKU
Example Request
curl "https://server.shipper.network/api/v1/products?per_page=10&search=shirt" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
Response

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 }
}

GETGET /products/{uuid}

Returns a single product by UUID, including its variants and available stock quantities.

Example Request
curl https://server.shipper.network/api/v1/products/2e97b37a-3118-4905-842c-19706bcf1455 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
Response

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 }
  ]
}

Orders

GETGET /orders

Returns a paginated list of your orders. Use filters to narrow results by status, date range, or search terms.

Query Parameters
ParameterTypeRequiredDescription
pageinteger-Page number (default: 1)
per_pageinteger-Items per page (default: 20, max: 100)
statusstring-Filter by shipment status (e.g. delivered, awaitingpackaging, canceled)
start_datestring-Filter orders created on or after this date (YYYY-MM-DD)
end_datestring-Filter orders created on or before this date (YYYY-MM-DD)
searchstring-Search by order ID, customer name, or phone number
Example Request
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"
Response

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 }
}

POSTPOST /orders

Creates a new order in Shipper. Returns the order ID which you can use to track the order.

Request Body
ParameterTypeRequiredDescription
addressobject*Recipient address object (see fields below)
address.namestring*Full name - automatically split into first and last name
address.phone1string*Primary phone number
address.phone2string-Secondary phone number
address.address1string*Street address line 1
address.address2string-Street address line 2
address.division_1string-State / Governorate - use exact name from GET /countries/{code}/divisions
address.division_2string-City / Delegation - use exact name from GET /countries/{code}/divisions?level=2&parent_id=...
address.countrystring*Two-letter country code (e.g. TN, DZ, MA)
itemsarray*Array of order line items
items[].idstring*Product or variant UUID from GET /products
items[].quantityinteger-Quantity (default: 1)
items[].total_pricenumber-Total price for this line. Defaults to product price x quantity
shipping_totalnumber-Shipping cost charged to the customer (default: 0)
is_codboolean-)
auto_fulfillboolean-Send directly to fulfillment without creating a draft (default: false)
with_confirmationboolean-Require a phone confirmation call before shipping (default: false)
store_namestring-Source store name - appears in order tracking for your reference
external_order_idstring-Your system order reference ID - displayed in the Shipper dashboard
external_order_urlstring-URL to the order in your system - clickable from the Shipper dashboard
Example Request
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"
  }'
Response

201Created

{
  "id": 555
}

GETGET /orders/{id}

Returns full order details including current status, shipment tracking numbers with delivery logs, and the recipient address.

Path Parameters
ParameterTypeRequiredDescription
idinteger*The order ID returned when the order was created
Example Request
curl https://server.shipper.network/api/v1/orders/555 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
Response

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"
  }
}

POSTPOST /orders/{id}/cancel

Cancels 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.

Path Parameters
ParameterTypeRequiredDescription
idinteger*The order ID to cancel
Example Request
curl -X POST https://server.shipper.network/api/v1/orders/555/cancel \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
Response

200

{
  "success": true,
  "canceled_shipments": 1
}
Error Response

422If no shipments can be canceled:

{
  "error": "No shipments available to cancel"
}

Webhooks

Webhooks let you receive real-time HTTP notifications when order or shipment statuses change, instead of polling the API.

POSTPOST /webhooks

Register a URL to receive webhook events. You can register up to 5 webhooks per API key. The URL must use HTTPS.

Request Body
ParameterTypeRequiredDescription
urlstring*HTTPS URL that will receive POST requests with event data
eventsarray*Events to subscribe to: "order.status_changed" and/or "shipment.status_changed"
secretstring-Secret key (min 16 chars) used to sign payloads with HMAC-SHA256 for verification
Example Request
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"
  }'
Response

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"
}

GETGET /webhooks

Returns all webhooks registered for the current API key, including their status and failure count.

Example Request
curl https://server.shipper.network/api/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
Response

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"
  }
]

DELETEDELETE /webhooks/{id}

Removes a webhook registration. The webhook will stop receiving events immediately.

Example Request
curl -X DELETE https://server.shipper.network/api/v1/webhooks/1 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
Response

200

{ "success": true }

Webhook Payload Format

When an event occurs, Shipper sends a POST request to your registered URL with a JSON body:

order.status_changed
{
  "event": "order.status_changed",
  "timestamp": "2025-01-15T10:30:00.000000Z",
  "data": {
    "order_id": 555,
    "status": "Paid",
    "previous_status": "Pending"
  }
}
shipment.status_changed
{
  "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"
  }
}
Signature Verification

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'];

Error Codes

The API uses standard HTTP status codes. All error responses include a JSON body with details.

CodeMeaning
200Success
201Resource created successfully
401Unauthorized - missing or invalid API key
403Forbidden - API key is disabled or revoked
404Not found - the requested resource does not exist
422Validation error - check the errors object in the response body for details
429Too Many Requests - rate limit exceeded. Wait and retry