Courier Express API Documentation
Create pickup & delivery orders and track them from your own system.
Get your API keys
You need three values before calling the API.
Get an access token
POSTSend your Client ID and Secret. Use the returned token on every other request.
{
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET"
}{
"code": 0,
"message": "",
"data": {
"customerId": "YOUR_CUSTOMER_ID",
"accessToken": "ACCESS_TOKEN",
"expiresIn": 3600
},
"metadata": {}
}Authorization: Bearer ACCESS_TOKEN. It expires after expiresIn seconds.Create an order
POSTCreate a pickup & delivery order, with optional cash on delivery.
{
"customerId": "YOUR_CUSTOMER_ID",
"pickup": {
"address": "Valletta, Malta",
"addressDetail": "",
"completeAfter": 0,
"completeBefore": 0,
"coordinates": [14.5146, 35.8992],
"fullName": "Sender Name",
"phone": "+35620000000",
"email": "sender@example.com",
"placeId": ""
},
"delivery": {
"address": "Sliema, Malta",
"addressDetail": "Apartment 2",
"completeAfter": 0,
"completeBefore": 0,
"coordinates": [14.5010, 35.9122],
"fullName": "Recipient Name",
"phone": "+35620000001",
"email": "recipient@example.com",
"placeId": ""
},
"service": {
"id": "YOUR_SERVICE_ID",
"options": []
},
"paymentMethod": "Cash",
"paymentSide": "Sender",
"draft": false,
"codAmount": 25,
"uid": "UNIQUE_ORDER_001",
"note": "Handle with care",
"referenceId": "ORDER-001"
}Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
Accept-Language: en{
"code": 0,
"data": {
"orderId": "CREATED_ORDER_ID"
},
"metadata": {}
}shipmentLabelUrl from step 4.0 for no schedule.| Field | Description |
|---|---|
| customerId | From the access-token response |
| service.id | Your Service ID from Courier Express |
| service.options | Optional; use [] if none |
| pickup / delivery | Contact, address, coordinates and time window |
| paymentMethod | Cash or Wallet |
| paymentSide | Sender or Receiver |
| draft | false = confirmed order, true = draft |
| codAmount | Cash to collect on delivery; 0 for none |
| uid | Optional unique ID to prevent duplicates |
| referenceId | Optional reference from your system |
| Option | paymentMethod | paymentSide | Fee paid by |
|---|---|---|---|
| Wallet | Wallet | Sender | Your account wallet |
| Cash · receiver | Cash | Receiver | Recipient, at delivery |
| Cash · sender | Cash | Sender | Sender, at pickup |
Available options depend on your service. codAmount is separate from the delivery fee.
Check an order
GETGet the latest details and status using the orderId from step 3.
{
"code": 0,
"message": "",
"data": {
"id": "YOUR_ORDER_ID",
"status": "Confirmed",
"type": "PickupDelivery",
"code": "4737926",
"price": 10,
"paymentMethod": "Wallet",
"paymentSide": "Sender",
"codAmount": 0,
"trackOrder": "https://example.com/track/YOUR_ORDER",
"shipmentLabelUrl": "https://example.com/shipment-label/YOUR_ORDER",
"referenceId": "ORDER-001"
}
}Authorization: Bearer ACCESS_TOKEN
Accept-Language: enshipmentLabelUrl, print it and stick it on the package before pickup.The full response has more order, pickup, delivery and tracking fields.
Receive webhooks
IncomingGet status updates pushed to your server instead of polling.
- Create an HTTPS endpoint on your serverYou
- Send the endpoint URL to our support teamYou
- Support adds it to your accountCourier Express
- Support shares your webhook secretCourier Express
- Verify each request's signature, then process itYou
{
"timestamp": 1758028850388,
"data": {
"trigger": "Updated",
"id": "YOUR_ORDER_ID",
"status": "NotDelivered",
"type": "PickupDelivery",
"customerId": "YOUR_CUSTOMER_ID",
"code": "5184194",
"paymentMethod": "Cash",
"paymentSide": "Receiver",
"codAmount": 32500,
"shipmentLabelUrl": "https://example.com/shipment-label/12345",
"referenceId": "REF_0002/12345"
}
}The real payload also includes pickup, delivery, driver, service and proof details.
const crypto = require('crypto');
// Use the exact payload serialization expected by the sender.
const body = JSON.stringify(req.body);
const expected = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(body)
.digest('hex');
const received = req.get('x-webhook-signature') || '';
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(received, 'hex');
if (a.length !== b.length ||
!crypto.timingSafeEqual(a, b)) {
return res.sendStatus(401);
}
// Signature is valid — handle the update.WEBHOOK_SECRET on your server. Reject requests whose signature doesn't match.Print shipment labels
POSTEvery package must carry its label. There are two ways to get it.
{
"customerId": "YOUR_CUSTOMER_ID",
"ids": [
"ORDER_ID_1",
"ORDER_ID_2"
]
}Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
Accept-Language: en| Field | Description |
|---|---|
| customerId | From the access-token response |
| ids | List of orderId values to print |
shipmentLabelUrl is also included in every webhook payload.
Cancel an order
POSTCancel an order before it is picked up. Optionally give a reason.
{
"code": 0,
"message": "",
"data": [
{
"id": "REASON_ID",
"text": "I want to change my order details",
"type": "Cancellation"
}
],
"metadata": {}
}{
"orderId": "YOUR_ORDER_ID",
"customerId": "YOUR_CUSTOMER_ID",
"failureReasonId": "REASON_ID",
"failureReasonText": "I want to change my order details"
}Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
Accept-Language: en{
"code": 0,
"message": "",
"data": {
"id": "YOUR_ORDER_ID",
"status": "CustomerCanceled",
"type": "PickupDelivery",
"statusMessage": "Order canceled"
}
}| Field | Required | Description |
|---|---|---|
| orderId | Yes | The order to cancel |
| customerId | Yes | From the access-token response |
| failureReasonId | No | An id from the reasons list |
| failureReasonText | No | The matching reason text |
CustomerCanceled.