The Order API lets external systems retrieve orders placed through SalesPro Hub and push status updates back. This is the primary integration point for ERP systems like Sage and Pastel.
Typical Integration Flow
1. External system polls for new orders GET /api/v1/orders?status=pending&acknowledged=false
2. External system creates internal order (your ERP logic)
3. External system acknowledges receipt POST /api/v1/orders/{orderNumber}/acknowledge
4. External system updates status PUT /api/v1/orders/{orderNumber}/statusEndpoints
List Orders
GET /api/v1/ordersPermission required: orders:read
Retrieve a paginated list of orders with optional filters. This is your primary polling endpoint.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: pending, confirmed, processing, delivered, cancelled |
type | string | Filter by type: order, quote |
acknowledged | string | true or false — filter by acknowledgement status |
updated_since | ISO 8601 | Only return orders updated after this timestamp |
updated_since_id | integer | Tie-breaker returned in meta.next_cursor; only send with updated_since |
from_date | date | Orders with order_date on or after this date |
to_date | date | Orders with order_date on or before this date |
per_page | integer | Results per page (1-100, default 50) |
Drafts are never returned by the external Order API. Incremental requests are
sorted by updated_at, then internal ID, so callers can safely resume when
multiple orders share the same timestamp.
Example: Poll for New Unprocessed Orders
curl -X GET "https://portal.salesrepsoftware.co.za/api/v1/orders?status=pending&acknowledged=false" \
-H "X-API-Key: sk_your_key" \
-H "Accept: application/json"Example: Incremental Sync
Get only orders that changed since your last sync:
curl -X GET "https://portal.salesrepsoftware.co.za/api/v1/orders?updated_since=2026-02-09T10:00:00Z" \
-H "X-API-Key: sk_your_key" \
-H "Accept: application/json"Response
{
"success": true,
"data": [
{
"order_number": "ORD-20260209-0001",
"status": "pending",
"type": "order",
"order_date": "2026-02-09",
"delivery_date": "2026-02-16",
"customer": {
"name": "ABC Trading (Pty) Ltd",
"contact_person": "John Smith",
"email": "john@abctrading.co.za",
"phone": "0821234567"
},
"sales_rep": {
"name": "Jane Doe"
},
"subtotal": "10000.00",
"vat_rate": "15.00",
"vat_amount": "1500.00",
"discount_amount": "0.00",
"total": "11500.00",
"delivery_address": "123 Main Road, Sandton, 2196",
"notes": "Deliver before 10am",
"payment_status": "unpaid",
"items": [
{
"sku": "WDG-001",
"name": "Widget A",
"quantity": 50,
"unit_price": "200.00",
"discount_percent": "0.00",
"line_total": "10000.00"
}
],
"acknowledged_at": null,
"external_reference": null,
"created_at": "2026-02-09T08:30:00+02:00",
"updated_at": "2026-02-09T08:30:00+02:00"
}
],
"meta": {
"total": 42,
"page": 1,
"per_page": 50,
"last_page": 1,
"next_cursor": {
"updated_since": "2026-02-09T08:30:00.000000Z",
"updated_since_id": 412
}
}
}Get Single Order
GET /api/v1/orders/{orderNumber}Permission required: orders:read
Retrieve a single order by its order number.
Example
curl -X GET "https://portal.salesrepsoftware.co.za/api/v1/orders/ORD-20260209-0001" \
-H "X-API-Key: sk_your_key" \
-H "Accept: application/json"Response
{
"success": true,
"data": {
"order_number": "ORD-20260209-0001",
"status": "pending",
"type": "order",
"order_date": "2026-02-09",
"delivery_date": "2026-02-16",
"customer": {
"name": "ABC Trading (Pty) Ltd",
"contact_person": "John Smith",
"email": "john@abctrading.co.za",
"phone": "0821234567"
},
"sales_rep": {
"name": "Jane Doe"
},
"subtotal": "10000.00",
"vat_rate": "15.00",
"vat_amount": "1500.00",
"discount_amount": "0.00",
"total": "11500.00",
"delivery_address": "123 Main Road, Sandton, 2196",
"notes": "Deliver before 10am",
"payment_status": "unpaid",
"items": [
{
"sku": "WDG-001",
"name": "Widget A",
"quantity": 50,
"unit_price": "200.00",
"discount_percent": "0.00",
"line_total": "10000.00"
}
],
"acknowledged_at": null,
"external_reference": null,
"created_at": "2026-02-09T08:30:00+02:00",
"updated_at": "2026-02-09T08:30:00+02:00"
}
}Error: Order Not Found
{
"success": false,
"error": "Order not found"
}Acknowledge Order
POST /api/v1/orders/{orderNumber}/acknowledgePermission required: orders:write
Mark an order as received by your external system. This sets the acknowledged_at timestamp and optionally stores your system's reference number.
Use acknowledgement to track which orders you've already processed. Then poll with ?acknowledged=false to only get new, unprocessed orders.
Acknowledgement is idempotent: replaying the same request preserves the first
timestamp. Reusing the order with a different non-empty external reference
returns HTTP 409 instead of silently relinking it.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
external_reference | string | No | Your system's reference number (e.g., Sage invoice number). Max 255 characters. |
Example
curl -X POST "https://portal.salesrepsoftware.co.za/api/v1/orders/ORD-20260209-0001/acknowledge" \
-H "X-API-Key: sk_your_key" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"external_reference": "SAGE-INV-00451"}'Response
{
"success": true,
"data": {
"order_number": "ORD-20260209-0001",
"acknowledged_at": "2026-02-09T10:15:00+02:00",
"external_reference": "SAGE-INV-00451"
},
"message": "Order acknowledged successfully"
}Update Order Status
PUT /api/v1/orders/{orderNumber}/statusPermission required: orders:write
Update an order's status from your external system. For example, mark an order as confirmed after processing in your ERP, or mark as delivered after dispatch.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | New status: confirmed, processing, delivered, or cancelled |
external_reference | string | No | Your system's reference number. Max 255 characters. |
notes | string | No | Additional notes (used as cancellation reason if cancelling). Max 1000 characters. |
Valid Status Transitions
| From | Allowed Transitions |
|---|---|
pending | confirmed, cancelled |
confirmed | processing, delivered, cancelled |
processing | delivered, cancelled |
delivered and cancelled are terminal — they cannot be changed.
Replaying the current status is safe and does not emit another status event or
deduct/release inventory twice. When the tenant uses reserve_and_deduct,
moving an order to delivered deducts its reservation and moving it to
cancelled releases the reservation. Tenants whose ERP is the inventory source
of truth should select the inventory policy that avoids deducting the same
fulfilment in both systems.
Example: Confirm an Order
curl -X PUT "https://portal.salesrepsoftware.co.za/api/v1/orders/ORD-20260209-0001/status" \
-H "X-API-Key: sk_your_key" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"status": "confirmed", "external_reference": "SAGE-INV-00451"}'Response
{
"success": true,
"data": {
"order_number": "ORD-20260209-0001",
"previous_status": "pending",
"new_status": "confirmed",
"external_reference": "SAGE-INV-00451"
},
"message": "Order status updated to 'confirmed'"
}Error: Invalid Transition
{
"success": false,
"error": "Cannot transition from 'delivered' to 'confirmed'"
}Order Statuses
| Status | Description |
|---|---|
draft | Being composed by sales rep (not visible via API) |
pending | Submitted and awaiting approval |
confirmed | Approved and ready for fulfilment |
processing | Being prepared/dispatched |
delivered | Successfully delivered (terminal) |
cancelled | Cancelled (terminal) |
Order Types
| Type | Description |
|---|---|
order | A confirmed purchase order |
quote | A price quotation (can be converted to an order) |
Payment Statuses
| Status | Description |
|---|---|
unpaid | No payment received |
partial | Partial payment received |
paid | Fully paid |
Recommended Polling Strategy
For most integrations, poll every 5-15 minutes:
GET /api/v1/orders?status=pending&acknowledged=false&per_page=100For each order returned:
- Create the order in your external system.
- Call
POST /acknowledgewith your external reference number. - The order won't appear in subsequent polls (filtered by
acknowledged=false).
For incremental sync (catching status changes and updates):
GET /api/v1/orders?updated_since=2026-02-09T10:00:00ZStore and send the complete meta.next_cursor object:
{
"updated_since": "2026-02-09T10:00:00.000000Z",
"updated_since_id": 412
}Using only updated_at can skip records when several orders have the same
timestamp. Begin each cursor request at page 1; the cursor itself advances the
result set.