Code Examples
Ready-to-use integration code for the Equos Orders API
Create a Send Order
Basic example to create a send order (shipping to a customer).
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
// Get access token
tokenData := url.Values{
"client_id": {"your_client_id"},
"client_secret": {"secret_..."},
"grant_type": {"client_credentials"},
"resource": {"https://api.equos.one"},
"scope": {"orders:write"},
}
tokenResp, _ := http.PostForm("https://id.equos.one/oidc/token", tokenData)
defer tokenResp.Body.Close()
var tokenResult map[string]interface{}
json.NewDecoder(tokenResp.Body).Decode(&tokenResult)
token := tokenResult["access_token"].(string)
// Create send order
orderData := map[string]interface{}{
"customerId": "cust-123",
"reference1": "PO-2024-001",
"items": []map[string]interface{}{
{
"sku": "WIDGET-A",
"productName": "Blue Widget",
"quantity": 10,
},
},
}
orderJSON, _ := json.Marshal(orderData)
req, _ := http.NewRequest("POST", "https://api.equos.one/e9v3/orders/send",
bytes.NewBuffer(orderJSON))
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
fmt.Printf("Order created: %v\n", result["jobId"])
}import 'package:http/http.dart' as http;
import 'dart:convert';
Future createOrder() async {
// Get access token
final tokenResponse = await http.post(
Uri.parse('https://id.equos.one/oidc/token'),
body: {
'client_id': 'your_client_id',
'client_secret': 'secret_...',
'grant_type': 'client_credentials',
'resource': 'https://api.equos.one',
'scope': 'orders:write',
},
);
final tokenData = jsonDecode(tokenResponse.body);
final token = tokenData['access_token'];
// Create send order
final orderData = {
'customerId': 'cust-123',
'reference1': 'PO-2024-001',
'items': [
{
'sku': 'WIDGET-A',
'productName': 'Blue Widget',
'quantity': 10,
},
],
};
final orderResponse = await http.post(
Uri.parse('https://api.equos.one/e9v3/orders/send'),
headers: {'Authorization': 'Bearer $token'},
body: jsonEncode(orderData),
);
final result = jsonDecode(orderResponse.body);
print('Order created: ${result['jobId']}');
} import requests
# Get token from EQUOS ID
token_response = requests.post(
"https://id.equos.one/oidc/token",
data={
"client_id": "your_client_id",
"client_secret": "secret_...",
"grant_type": "client_credentials",
"resource": "https://api.equos.one",
"scope": "orders:write"
}
)
token = token_response.json()["access_token"]
# Create send order
order_data = {
"customerId": "cust-123",
"reference1": "PO-2024-001",
"items": [
{
"sku": "WIDGET-A",
"productName": "Blue Widget",
"quantity": 10
}
]
}
response = requests.post(
"https://api.equos.one/e9v3/orders/send",
headers={"Authorization": f"Bearer {token}"},
json=order_data
)
if response.status_code == 200:
print(f"Order created: {response.json()['jobId']}")
else:
print(f"Error: {response.status_code}")
print(response.json())const axios = require('axios');
async function createOrder() {
// Get access token
const tokenResponse = await axios.post(
'https://id.equos.one/oidc/token',
new URLSearchParams({
client_id: 'your_client_id',
client_secret: 'secret_...',
grant_type: 'client_credentials',
resource: 'https://api.equos.one',
scope: 'orders:write'
})
);
const token = tokenResponse.data.access_token;
// Create send order
const orderData = {
customerId: 'cust-123',
reference1: 'PO-2024-001',
items: [
{
sku: 'WIDGET-A',
productName: 'Blue Widget',
quantity: 10
}
]
};
const orderResponse = await axios.post(
'https://api.equos.one/e9v3/orders/send',
orderData,
{ headers: { Authorization: `Bearer ${token}` } }
);
console.log(`Order created: ${orderResponse.data.jobId}`);
}
createOrder().catch(console.error);# Get token
TOKEN=$(curl -s https://id.equos.one/oidc/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=your_client_id" \
-d "client_secret=secret_..." \
-d "grant_type=client_credentials" \
-d "resource=https://api.equos.one" \
-d "scope=orders:write" | jq -r '.access_token')
# Create order
curl -X POST https://api.equos.one/e9v3/orders/send \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"customerId": "cust-123",
"reference1": "PO-2024-001",
"items": [
{
"sku": "WIDGET-A",
"productName": "Blue Widget",
"quantity": 10
}
]
}'Create a Receive Order
Create a receive order with supplier match-or-create. If the supplier email doesn't exist, it will be created.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
// Get access token
tokenData := url.Values{
"client_id": {"your_client_id"},
"client_secret": {"secret_..."},
"grant_type": {"client_credentials"},
"resource": {"https://api.equos.one"},
"scope": {"orders:write"},
}
tokenResp, _ := http.PostForm("https://id.equos.one/oidc/token", tokenData)
defer tokenResp.Body.Close()
var tokenResult map[string]interface{}
json.NewDecoder(tokenResp.Body).Decode(&tokenResult)
token := tokenResult["access_token"].(string)
// Create receive order with supplier match-or-create
orderData := map[string]interface{}{
"supplierEmail": "supplier@example.com",
"supplierName": "Acme Corp",
"reference1": "PO-SUP-2024-001",
"items": []map[string]interface{}{
{
"sku": "COMP-B",
"productName": "Component B",
"quantity": 100,
},
{
"sku": "COMP-C",
"productName": "Component C",
"quantity": 50,
},
},
}
orderJSON, _ := json.Marshal(orderData)
req, _ := http.NewRequest("POST", "https://api.equos.one/e9v3/orders/receive",
bytes.NewBuffer(orderJSON))
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
fmt.Printf("Receive order created: %v\n", result["jobId"])
}import 'package:http/http.dart' as http;
import 'dart:convert';
Future createReceiveOrder() async {
// Get access token
final tokenResponse = await http.post(
Uri.parse('https://id.equos.one/oidc/token'),
body: {
'client_id': 'your_client_id',
'client_secret': 'secret_...',
'grant_type': 'client_credentials',
'resource': 'https://api.equos.one',
'scope': 'orders:write',
},
);
final tokenData = jsonDecode(tokenResponse.body);
final token = tokenData['access_token'];
// Create receive order with supplier match-or-create
final orderData = {
'supplierEmail': 'supplier@example.com',
'supplierName': 'Acme Corp',
'reference1': 'PO-SUP-2024-001',
'items': [
{
'sku': 'COMP-B',
'productName': 'Component B',
'quantity': 100,
},
{
'sku': 'COMP-C',
'productName': 'Component C',
'quantity': 50,
},
],
};
final orderResponse = await http.post(
Uri.parse('https://api.equos.one/e9v3/orders/receive'),
headers: {'Authorization': 'Bearer $token'},
body: jsonEncode(orderData),
);
final result = jsonDecode(orderResponse.body);
print('Receive order created: ${result['jobId']}');
} import requests
token_response = requests.post(
"https://id.equos.one/oidc/token",
data={
"client_id": "your_client_id",
"client_secret": "secret_...",
"grant_type": "client_credentials",
"resource": "https://api.equos.one",
"scope": "orders:write"
}
)
token = token_response.json()["access_token"]
# Create receive order with supplier match-or-create
order_data = {
"supplierEmail": "supplier@example.com",
"supplierName": "Acme Corp",
"reference1": "PO-SUP-2024-001",
"items": [
{
"sku": "COMP-B",
"productName": "Component B",
"quantity": 100
},
{
"sku": "COMP-C",
"productName": "Component C",
"quantity": 50
}
]
}
response = requests.post(
"https://api.equos.one/e9v3/orders/receive",
headers={"Authorization": f"Bearer {token}"},
json=order_data
)
print(f"Receive order created: {response.json()['jobId']}")# Get token
TOKEN=$(curl -s https://id.equos.one/oidc/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=your_client_id" \
-d "client_secret=secret_..." \
-d "grant_type=client_credentials" \
-d "resource=https://api.equos.one" \
-d "scope=orders:write" | jq -r '.access_token')
# Create receive order
curl -X POST https://api.equos.one/e9v3/orders/receive \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"supplierEmail": "supplier@example.com",
"supplierName": "Acme Corp",
"reference1": "PO-SUP-2024-001",
"items": [
{
"sku": "COMP-B",
"productName": "Component B",
"quantity": 100
},
{
"sku": "COMP-C",
"productName": "Component C",
"quantity": 50
}
]
}'Create a Dynamic Order (Sync Endpoint)
Use the /sync endpoint when the order type is determined dynamically at runtime.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
// Get token
tokenData := url.Values{
"client_id": {"your_client_id"},
"client_secret": {"secret_..."},
"grant_type": {"client_credentials"},
"resource": {"https://api.equos.one"},
"scope": {"orders:write"},
}
tokenResp, _ := http.PostForm("https://id.equos.one/oidc/token", tokenData)
defer tokenResp.Body.Close()
var tokenResult map[string]interface{}
json.NewDecoder(tokenResp.Body).Decode(&tokenResult)
token := tokenResult["access_token"].(string)
// Create order dynamically
orderData := map[string]interface{}{
"orderType": "transfer",
"locationId": "loc-456",
"reference1": "XFER-2024-001",
"items": []map[string]interface{}{
{
"sku": "ITEM-X",
"productName": "Item X",
"quantity": 25,
},
},
}
orderJSON, _ := json.Marshal(orderData)
req, _ := http.NewRequest("POST", "https://api.equos.one/e9v3/orders/sync",
bytes.NewBuffer(orderJSON))
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
fmt.Printf("Order created: %v\n", result["jobId"])
}import 'package:http/http.dart' as http;
import 'dart:convert';
Future createSyncOrder() async {
// Get token
final tokenResponse = await http.post(
Uri.parse('https://id.equos.one/oidc/token'),
body: {
'client_id': 'your_client_id',
'client_secret': 'secret_...',
'grant_type': 'client_credentials',
'resource': 'https://api.equos.one',
'scope': 'orders:write',
},
);
final tokenData = jsonDecode(tokenResponse.body);
final token = tokenData['access_token'];
// Create order dynamically
final orderData = {
'orderType': 'transfer',
'locationId': 'loc-456',
'reference1': 'XFER-2024-001',
'items': [
{
'sku': 'ITEM-X',
'productName': 'Item X',
'quantity': 25,
},
],
};
final orderResponse = await http.post(
Uri.parse('https://api.equos.one/e9v3/orders/sync'),
headers: {'Authorization': 'Bearer $token'},
body: jsonEncode(orderData),
);
final result = jsonDecode(orderResponse.body);
print('Order created: ${result['jobId']}');
} import requests
# Get token from EQUOS ID
token_response = requests.post(
"https://id.equos.one/oidc/token",
data={
"client_id": "your_client_id",
"client_secret": "secret_...",
"grant_type": "client_credentials",
"resource": "https://api.equos.one",
"scope": "orders:write"
}
)
token = token_response.json()["access_token"]
# Create order dynamically
order_data = {
"orderType": "transfer", # Can be dynamic
"locationId": "loc-456",
"reference1": "XFER-2024-001",
"items": [
{
"sku": "ITEM-X",
"productName": "Item X",
"quantity": 25
}
]
}
response = requests.post(
"https://api.equos.one/e9v3/orders/sync",
headers={"Authorization": f"Bearer {token}"},
json=order_data
)
print(f"Order created: {response.json()['jobId']}")const axios = require('axios');
async function createSyncOrder() {
// Get token
const tokenResponse = await axios.post(
'https://id.equos.one/oidc/token',
new URLSearchParams({
client_id: 'your_client_id',
client_secret: 'secret_...',
grant_type: 'client_credentials',
resource: 'https://api.equos.one',
scope: 'orders:write'
})
);
const token = tokenResponse.data.access_token;
// Create order dynamically
const orderData = {
orderType: 'transfer', // Can be dynamic
locationId: 'loc-456',
reference1: 'XFER-2024-001',
items: [
{
sku: 'ITEM-X',
productName: 'Item X',
quantity: 25
}
]
};
const response = await axios.post(
'https://api.equos.one/e9v3/orders/sync',
orderData,
{ headers: { Authorization: `Bearer ${token}` } }
);
console.log(`Order created: ${response.data.jobId}`);
}
createSyncOrder().catch(console.error);# Get token
TOKEN=$(curl -s https://id.equos.one/oidc/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=your_client_id" \
-d "client_secret=secret_..." \
-d "grant_type=client_credentials" \
-d "resource=https://api.equos.one" \
-d "scope=orders:write" | jq -r '.access_token')
# Create order dynamically
curl -X POST https://api.equos.one/e9v3/orders/sync \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"orderType": "transfer",
"locationId": "loc-456",
"reference1": "XFER-2024-001",
"items": [
{
"sku": "ITEM-X",
"productName": "Item X",
"quantity": 25
}
]
}'Handle Validation Errors
Validation errors return HTTP 400 with a details array listing all validation issues.
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
// Invalid request (missing quantity)
orderData := map[string]interface{}{
"customerId": "cust-123",
"items": []map[string]interface{}{
{
"sku": "WIDGET-A",
"productName": "Blue Widget",
// Missing quantity!
},
},
}
orderJSON, _ := json.Marshal(orderData)
req, _ := http.NewRequest("POST", "https://api.equos.one/e9v3/orders/send",
bytes.NewBuffer(orderJSON))
req.Header.Set("Authorization", "Bearer token_here")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
if resp.StatusCode == 400 {
var errorResult map[string]interface{}
json.NewDecoder(resp.Body).Decode(&errorResult)
fmt.Printf("Validation error: %v\n", errorResult["error"])
if details, ok := errorResult["details"].([]interface{}); ok {
for _, d := range details {
detail := d.(map[string]interface{})
fmt.Printf(" - %v: %v\n", detail["field"], detail["message"])
}
}
}
}import 'package:http/http.dart' as http;
import 'dart:convert';
Future handleValidationError() async {
// Invalid request (missing quantity)
final orderData = {
'customerId': 'cust-123',
'items': [
{
'sku': 'WIDGET-A',
'productName': 'Blue Widget',
// Missing quantity!
},
],
};
final response = await http.post(
Uri.parse('https://api.equos.one/e9v3/orders/send'),
headers: {'Authorization': 'Bearer token_here'},
body: jsonEncode(orderData),
);
if (response.statusCode == 400) {
final error = jsonDecode(response.body);
print('Validation error: ${error['error']}');
if (error['details'] is List) {
for (var detail in error['details']) {
print(' - ${detail['field']}: ${detail['message']}');
}
}
}
} import requests
token_response = requests.post(...)
token = token_response.json()["access_token"]
# Invalid request (missing quantity)
order_data = {
"customerId": "cust-123",
"items": [
{
"sku": "WIDGET-A",
"productName": "Blue Widget"
# Missing quantity!
}
]
}
response = requests.post(
"https://api.equos.one/e9v3/orders/send",
headers={"Authorization": f"Bearer {token}"},
json=order_data
)
if response.status_code == 400:
error = response.json()
print(f"Validation error: {error['error']}")
for detail in error.get('details', []):
print(f" - {detail['field']}: {detail['message']}")# Get token
TOKEN=$(curl -s https://id.equos.one/oidc/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=your_client_id" \
-d "client_secret=secret_..." \
-d "grant_type=client_credentials" \
-d "resource=https://api.equos.one" \
-d "scope=orders:write" | jq -r '.access_token')
# Invalid request (missing quantity)
curl -X POST https://api.equos.one/e9v3/orders/send \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"customerId": "cust-123",
"items": [
{
"sku": "WIDGET-A",
"productName": "Blue Widget"
}
]
}' | jq '.error, .details'Track Job Status
Orders are processed asynchronously. Poll the job tracking API using the jobId to track completion and retrieve the created order ID.
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
func pollJobStatus(businessID, jobID, token string, timeout time.Duration) map[string]interface{} {
url := fmt.Sprintf("https://api.equos.one/e9v3/jobs/%s", jobID)
headers := map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)}
startTime := time.Now()
for time.Since(startTime) < timeout {
req, _ := http.NewRequest("GET", url, nil)
for k, v := range headers {
req.Header.Set(k, v)
}
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
if resp.StatusCode != 200 {
fmt.Printf("Error fetching job: %d\n", resp.StatusCode)
return nil
}
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
status := result["status"]
if status == "completed" {
orderID := ""
if r, ok := result["result"].(map[string]interface{}); ok {
orderID, _ = r["orderId"].(string)
}
fmt.Printf("✓ Order created: %s\n", orderID)
return map[string]interface{}{"status": "completed", "orderId": orderID}
} else if status == "failed" {
fmt.Printf("✗ Job failed: %v\n", result["errorMessage"])
return map[string]interface{}{"status": "failed"}
}
time.Sleep(2 * time.Second)
}
fmt.Println("Timeout waiting for job completion")
return nil
}import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
Futureimport requests
import time
# Poll job status until completion
def poll_job_status(business_id, jobId, token, timeout=60):
url = f"https://api.equos.one/e9v3/jobs/{jobId}"
headers = {"Authorization": f"Bearer {token}"}
start_time = time.time()
while time.time() - start_time < timeout:
response = requests.get(url, headers=headers)
if response.status_code != 200:
print(f"Error fetching job: {response.status_code}")
return None
data = response.json()
status = data.get('status')
if status == 'completed':
order_id = data.get('result', {}).get('orderId')
print(f"✓ Order created: {order_id}")
return {'status': 'completed', 'orderId': order_id}
elif status == 'failed':
error = data.get('errorMessage')
print(f"✗ Job failed: {error}")
return {'status': 'failed', 'error': error}
print(f"Status: {status}... waiting")
time.sleep(2)
print("Timeout waiting for job completion")
return Noneconst axios = require('axios');
// Poll job status until completion
async function pollJobStatus(businessId, jobId, token, timeout = 60000) {
const url = 'https://api.equos.one/e9v3/jobs/' + jobId;
const headers = { Authorization: 'Bearer ' + token };
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
const response = await axios.get(url, { headers });
const data = response.data;
const status = data.status;
if (status === 'completed') {
const orderId = data.result?.orderId;
console.log('✓ Order created: ' + orderId);
return { status: 'completed', orderId };
} else if (status === 'failed') {
const error = data.errorMessage;
console.log('✗ Job failed: ' + error);
return { status: 'failed', error };
}
console.log('Status: ' + status + '... waiting');
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
console.error('Error fetching job:', error.response?.status);
return null;
}
}
console.log('Timeout waiting for job completion');
return null;
}# Poll job status with exponential backoff
BUSINESS_ID="bus-123"
JOB_ID="job-abc456"
TOKEN="token_here"
for i in {1..30}; do
RESPONSE=$(curl -s https://api.equos.one/e9v3/jobs/$JOB_ID \
-H "Authorization: Bearer $TOKEN")
STATUS=$(echo $RESPONSE | jq -r '.status')
if [ "$STATUS" = "completed" ]; then
echo "✓ Order created"
echo $RESPONSE | jq '.result'
break
elif [ "$STATUS" = "failed" ]; then
echo "✗ Job failed"
echo $RESPONSE | jq '.error'
break
fi
echo "Status: $STATUS... waiting"
sleep 2
doneHandle Webhooks
Webhooks deliver real-time events to your application. Always verify the signature before processing to ensure the webhook came from Equos.
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
const WEBHOOK_SECRET = "whsec_your_secret_here"
func verifySignature(body []byte, signatureHeader string) bool {
expected := hmac.New(sha256.New, []byte(WEBHOOK_SECRET))
expected.Write(body)
expectedSig := hex.EncodeToString(expected.Sum(nil))
providedSig := strings.TrimPrefix(signatureHeader, "sha256=")
return hmac.Equal([]byte(expectedSig), []byte(providedSig))
}
func handleWebhook(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
// Verify signature
signature := r.Header.Get("X-EQUOS-Signature")
if !verifySignature(body, signature) {
w.WriteHeader(401)
json.NewEncoder(w).Encode(map[string]string{"error": "Invalid signature"})
return
}
// Parse event
eventType := r.Header.Get("X-EQUOS-Event")
deliveryID := r.Header.Get("X-EQUOS-Delivery")
var payload map[string]interface{}
json.Unmarshal(body, &payload)
fmt.Printf("Received %s (delivery: %s)\n", eventType, deliveryID)
// Process by event type
if eventType == "order.status_changed" {
data := payload["data"].(map[string]interface{})
orderID := data["orderId"]
status := data["status"]
fmt.Printf("Order %v is now %v\n", orderID, status)
} else if eventType == "inventory.adjusted" {
data := payload["data"].(map[string]interface{})
locationID := data["locationId"]
fmt.Printf("Inventory adjusted at %v\n", locationID)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
}
func main() {
http.HandleFunc("/webhook", handleWebhook)
http.ListenAndServe(":3000", nil)
}import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'dart:convert';
import 'package:crypto/crypto.dart';
const String WEBHOOK_SECRET = 'whsec_your_secret_here';
bool verifySignature(String body, String signatureHeader) {
final expected = Hmac(sha256, utf8.encode(WEBHOOK_SECRET))
.convert(utf8.encode(body))
.toString();
final provided = signatureHeader.replaceFirst('sha256=', '');
return expected == provided;
}
Future handleWebhook(Request request) async {
final body = await request.readAsString();
// Verify signature
final signature = request.headers['x-equos-signature'] ?? '';
if (!verifySignature(body, signature)) {
return Response(401, body: jsonEncode({'error': 'Invalid signature'}));
}
// Parse event
final eventType = request.headers['x-equos-event'];
final deliveryId = request.headers['x-equos-delivery'];
final payload = jsonDecode(body);
print('Received $eventType (delivery: $deliveryId)');
// Process by event type
if (eventType == 'order.status_changed') {
final data = payload['data'];
final orderId = data['orderId'];
final status = data['status'];
print('Order $orderId is now $status');
} else if (eventType == 'inventory.adjusted') {
final data = payload['data'];
final locationId = data['locationId'];
print('Inventory adjusted at $locationId');
}
return Response.ok(jsonEncode({'ok': true}));
}
void main() async {
final handler = handleWebhook;
await shelf_io.serve(handler, 'localhost', 3000);
print('Webhook server on port 3000');
} import hmac
import hashlib
import json
from flask import Flask, request
app = Flask(__name__)
WEBHOOK_SECRET = 'whsec_your_secret_here'
def verify_signature(body, signature_header):
"""Verify webhook signature using HMAC-SHA256"""
expected_sig = hmac.new(
WEBHOOK_SECRET.encode(),
body,
hashlib.sha256
).hexdigest()
provided_sig = signature_header.replace('sha256=', '')
return hmac.compare_digest(expected_sig, provided_sig)
@app.route('/webhook', methods=['POST'])
def handle_webhook():
# Verify signature
signature = request.headers.get('X-EQUOS-Signature', '')
if not verify_signature(request.get_data(), signature):
return {'error': 'Invalid signature'}, 401
# Parse event
event_type = request.headers.get('X-EQUOS-Event')
delivery_id = request.headers.get('X-EQUOS-Delivery')
payload = request.json
print(f'Received {event_type} (delivery: {delivery_id})')
# Process by event type
if event_type == 'order.status_changed':
order_id = payload['data']['orderId']
status = payload['data']['status']
print(f'Order {order_id} is now {status}')
elif event_type == 'inventory.adjusted':
location_id = payload['data']['locationId']
products = payload['data']['products']
print(f'Inventory adjusted at {location_id}: {len(products)} products')
# Acknowledge receipt
return {'ok': True}, 200const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser');
const app = express();
const WEBHOOK_SECRET = 'whsec_your_secret_here';
// Store raw body for signature verification
app.use(bodyParser.json({ verify: (req, res, buf) => {
req.rawBody = buf;
}}));
function verifySignature(body, signatureHeader) {
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(body)
.digest('hex');
const provided = signatureHeader.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(provided)
);
}
app.post('/webhook', (req, res) => {
// Verify signature
const signature = req.headers['x-equos-signature'] || '';
if (!verifySignature(req.rawBody, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Parse event
const eventType = req.headers['x-equos-event'];
const deliveryId = req.headers['x-equos-delivery'];
const payload = req.body;
console.log('Received', eventType, '(delivery:', deliveryId + ')');
// Process by event type
if (eventType === 'order.status_changed') {
const orderId = payload.data.orderId;
const status = payload.data.status;
console.log('Order', orderId, 'is now', status);
} else if (eventType === 'inventory.adjusted') {
const locationId = payload.data.locationId;
const products = payload.data.products;
console.log('Inventory adjusted at', locationId, products.length, 'products');
}
// Acknowledge receipt
res.json({ ok: true });
});
app.listen(3000, () => console.log('Webhook server on port 3000'));#!/bin/bash
# Simulate webhook delivery from Equos
WEBHOOK_SECRET="whsec_your_secret_here"
PAYLOAD='{
"data": {
"orderId": "order-123",
"status": "completed"
}
}'
# Calculate signature
SIGNATURE="sha256=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | cut -d' ' -f2)"
# Send webhook
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-H "X-EQUOS-Event: order.status_changed" \
-H "X-EQUOS-Delivery: deli-$(date +%s)" \
-H "X-EQUOS-Signature: $SIGNATURE" \
-d "$PAYLOAD"Best Practices
Caching Tokens
Tokens expire after 1 hour. Cache the token and its expiration time to avoid requesting a new one for every API call:
import time
class TokenManager:
def __init__(self, client_id, client_secret):
self.client_id = client_id
self.client_secret = client_secret
self.token = None
self.expires_at = 0
def get_token(self):
if time.time() >= self.expires_at - 60: # Refresh 60s before expiry
response = requests.post(...)
self.token = response.json()['access_token']
self.expires_at = time.time() + response.json()['expires_in']
return self.token
Handling Rate Limits
When you receive a 429 (rate limit) response, wait the amount specified in the Retry-After header before retrying:
import time
response = requests.post(...)
if response.status_code == 429:
retry_after = float(response.headers.get('Retry-After', 1))
print(f"Rate limited. Waiting {retry_after} seconds...")
time.sleep(retry_after)
# Retry the request
Handling Server Errors (5xx)
Use exponential backoff for 5xx errors:
import time
def create_order_with_retry(order_data, max_retries=3):
for attempt in range(max_retries):
response = requests.post(
"https://api.equos.one/e9v3/orders/send",
headers=...,
json=order_data
)
if response.status_code < 500:
return response
# Exponential backoff: 2s, 4s, 8s
wait_time = 2 ** (attempt + 1)
print(f"Server error. Retrying in {wait_time}s...")
time.sleep(wait_time)
raise Exception("Max retries exceeded")
Next Steps
- Read the Endpoints documentation for full API reference
- Review Rate Limiting for handling 429 responses
- Check Error Handling for all error codes
- See Authentication for managing API keys