This guide provides an implementation recipe for Sage 50cloud Pastel, Sage Business Cloud Accounting, or Sage 300 (Pastel Evolution). It requires a Sage SDK script, scheduled task, or integration middleware; SalesPro Hub does not claim a one-click native Sage/Pastel connector. The same REST/JSON building blocks can be mapped to other ERP/accounting systems.
Overview
The integration lets you:
- Pull orders from SalesPro Hub into Sage — Sales reps capture orders on the road, your office staff processes them in Sage.
- Push stock levels from Sage to SalesPro Hub — Keep your sales team's stock counts accurate.
- Receive real-time order notifications — Get webhooks when orders are placed so Sage can process them immediately.
Architecture Options
Option A: Polling (Recommended for Most Users)
Your Sage system runs a scheduled task that periodically checks SalesPro Hub for new orders and syncs stock levels.
Every 5-15 minutes:
1. GET /api/v1/orders?status=pending&acknowledged=false
2. For each new order → create Sales Order in Sage
3. POST /api/v1/orders/{orderNumber}/acknowledge (with Sage reference)
4. PUT /api/v1/orders/{orderNumber}/status → "confirmed"
Daily (or as needed):
5. Export stock levels from Sage
6. POST /api/v1/inventory/bulk (update SalesPro Hub stock)Pros: Simple, reliable, works behind firewalls. Cons: 5-15 minute delay on order processing.
Option B: Webhook-Driven (Real-Time)
SalesPro Hub pushes events to your system as they happen. Best if you have a publicly accessible endpoint.
When order.submitted fires:
1. Receive webhook POST with order details
2. Create Sales Order in Sage
3. POST /api/v1/orders/{orderNumber}/acknowledge
4. PUT /api/v1/orders/{orderNumber}/status → "confirmed"Pros: Near-instant order processing. Cons: Requires a publicly accessible HTTPS endpoint.
Option C: Hybrid
Use webhooks for orders (time-sensitive) and polling for stock sync (daily).
Step-by-Step Setup
Step 1: Create an API Key
- Log into SalesPro Hub as an admin.
- Go to Settings > API Keys.
- Click Create API Key.
- Name it "Sage Integration".
- Select the permissions required by your chosen data flows:
orders:readorders:writeinventory:readinventory:writecustomers:readcustomers:write
- Click Create and copy the key.
Store the key securely (e.g., in Sage's integration settings or your server's environment variables).
Step 2: Match Your Products
Ensure product SKUs in SalesPro Hub match the item codes in Sage. The API uses SKUs to identify products.
To check: paginate through GET /api/v1/inventory?per_page=100 to retrieve
products with their SKUs. Compare these against your Sage item codes.
Step 3: Set Up Order Polling
Create a scheduled script that runs every 5-15 minutes.
PowerShell Example (Windows / Sage 50cloud)
$apiKey = "sk_your_api_key_here"
$baseUrl = "https://portal.salesrepsoftware.co.za/api/v1"
$headers = @{
"X-API-Key" = $apiKey
"Accept" = "application/json"
"Content-Type" = "application/json"
}
# 1. Get unacknowledged pending orders
$response = Invoke-RestMethod -Uri "$baseUrl/orders?status=pending&acknowledged=false" `
-Headers $headers -Method Get
foreach ($order in $response.data) {
Write-Host "Processing order: $($order.order_number)"
# 2. Create the order in Sage (your Sage SDK logic here)
# $sageRef = Create-SageOrder -Order $order
$sageRef = "SAGE-" + (Get-Date -Format "yyyyMMdd") + "-001" # placeholder
# 3. Acknowledge the order in SalesPro Hub
$ackBody = @{ external_reference = $sageRef } | ConvertTo-Json
Invoke-RestMethod -Uri "$baseUrl/orders/$($order.order_number)/acknowledge" `
-Headers $headers -Method Post -Body $ackBody
# 4. Mark as confirmed
$statusBody = @{ status = "confirmed"; external_reference = $sageRef } | ConvertTo-Json
Invoke-RestMethod -Uri "$baseUrl/orders/$($order.order_number)/status" `
-Headers $headers -Method Put -Body $statusBody
Write-Host "Order $($order.order_number) processed as $sageRef"
}Save as SalesRepSync.ps1 and schedule with Windows Task Scheduler to run every 10 minutes.
PHP Example
<?php
$apiKey = 'sk_your_api_key_here';
$baseUrl = 'https://portal.salesrepsoftware.co.za/api/v1';
function apiRequest(string $method, string $url, ?array $data = null): array
{
global $apiKey;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"X-API-Key: {$apiKey}",
'Accept: application/json',
'Content-Type: application/json',
]);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data ?? []));
} elseif ($method === 'PUT') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data ?? []));
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// 1. Get unacknowledged pending orders
$orders = apiRequest('GET', "{$baseUrl}/orders?status=pending&acknowledged=false");
foreach ($orders['data'] as $order) {
echo "Processing: {$order['order_number']}\n";
// 2. Create order in your accounting system
// $sageRef = createSageOrder($order);
$sageRef = 'SAGE-' . date('Ymd') . '-001'; // placeholder
// 3. Acknowledge in SalesPro Hub
apiRequest('POST', "{$baseUrl}/orders/{$order['order_number']}/acknowledge", [
'external_reference' => $sageRef,
]);
// 4. Mark as confirmed
apiRequest('PUT', "{$baseUrl}/orders/{$order['order_number']}/status", [
'status' => 'confirmed',
'external_reference' => $sageRef,
]);
echo "Done: {$order['order_number']} → {$sageRef}\n";
}Run with cron: */10 * * * * php /path/to/salesrep-sync.php
Step 4: Set Up Stock Sync
Run daily to push stock levels from Sage to SalesPro Hub.
cURL Example
curl -X POST "https://portal.salesrepsoftware.co.za/api/v1/inventory/bulk" \
-H "X-API-Key: sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"products": [
{"sku": "WDG-001", "stock_qty": 150},
{"sku": "WDG-002", "stock_qty": 80},
{"sku": "WDG-003", "stock_qty": 0}
],
"reference": "SAGE-SYNC-2026-02-09"
}'Sage Pastel SDK (VB.NET / C#)
If you're using the Sage Pastel SDK, export stock levels and call the SalesPro Hub API:
using System.Net.Http;
using System.Text.Json;
public async Task SyncStockToSalesRep()
{
var apiKey = "sk_your_api_key_here";
var baseUrl = "https://portal.salesrepsoftware.co.za/api/v1";
// Get stock from Sage (your SDK logic)
var sageProducts = GetSageStockLevels();
var products = sageProducts.Select(p => new {
sku = p.ItemCode,
stock_qty = p.QtyOnHand
}).ToList();
var payload = new {
products = products,
reference = $"SAGE-SYNC-{DateTime.Now:yyyy-MM-dd}"
};
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", apiKey);
client.DefaultRequestHeaders.Add("Accept", "application/json");
var content = new StringContent(
JsonSerializer.Serialize(payload),
System.Text.Encoding.UTF8,
"application/json"
);
var response = await client.PostAsync($"{baseUrl}/inventory/bulk", content);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Stock sync result: {result}");
}Step 5: Set Up Webhooks (Optional)
If you want real-time order notifications instead of polling:
- Go to Settings > Webhooks.
- Click Add Endpoint.
- Enter your endpoint URL (must be publicly accessible HTTPS).
- Select
order.submitted(and any other events you need). - Copy the signing secret.
Your endpoint receives a POST with the full order details the moment a sales rep submits it.
See the Webhooks guide for payload formats and signature verification.
Mapping SalesPro Hub Data to Sage
Customer Mapping
The order payload includes customer details:
"customer": {
"name": "ABC Trading (Pty) Ltd",
"contact_person": "John Smith",
"email": "john@abctrading.co.za",
"phone": "0821234567"
}Map customer.name to your Sage customer account. If the customer doesn't exist in Sage, you may want to create them automatically or flag for manual review.
Item Mapping
Order items include SKUs:
"items": [
{
"sku": "WDG-001",
"name": "Widget A",
"quantity": 50,
"unit_price": "200.00",
"discount_percent": "0.00",
"line_total": "10000.00"
}
]Map sku to your Sage item code. Ensure SKUs match between systems.
VAT
SalesPro Hub calculates VAT at the South African rate (15%):
"subtotal": "10000.00",
"vat_rate": "15.00",
"vat_amount": "1500.00",
"total": "11500.00"Map these to the appropriate tax fields in Sage.
Amounts
All monetary values are strings formatted to 2 decimal places in South African Rand (ZAR). Convert to your accounting system's numeric format as needed.
Order Lifecycle
Here's how an order flows through both systems:
SalesPro Hub Sage
───────── ────
1. Sales rep creates order
(status: draft)
2. Sales rep submits order
(status: pending)
─── API poll or webhook ──→
3. Create Sales Order
4. POST /acknowledge
←── acknowledged ─────────
5. PUT /status → confirmed
←── status: confirmed ────
6. Process & dispatch
7. PUT /status → delivered
←── status: delivered ────
8. Commission calculated
Stock deductedTroubleshooting
Orders not appearing in API
- Check that orders have been submitted (status
pending). Draft orders are not visible via the API. - Verify your API key has
orders:readpermission. - Check your filter parameters —
?acknowledged=truewould hide unacknowledged orders.
Stock sync not reflecting in SalesPro Hub
- Verify your API key has
inventory:writepermission. - Check the API response for errors — invalid SKUs are listed in the
errorsarray. - Ensure SKUs match exactly (case-sensitive).
Webhook not receiving events
- Verify the endpoint URL is publicly accessible.
- Check the delivery history in Settings > Webhooks > View Deliveries.
- If the endpoint was auto-disabled (10 failures), re-enable it.
- Test with the "Send Test" button.
Rate limiting
Professional plan allows 5,000 API calls per day. A typical integration uses:
- 96 order polls/day (every 15 min) = ~100 calls
- 1 daily stock sync = 1 call
- Order acknowledges = depends on volume
You're unlikely to hit the limit unless doing very frequent polling or large bulk operations.