Developer Docs

← Back to Console

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

Future pollJobStatus(
  String businessId,
  String jobId,
  String token, {
  Duration timeout = const Duration(seconds: 60),
}) async {
  final url = 'https://api.equos.one/e9v3/jobs/$jobId';
  final headers = {'Authorization': 'Bearer $token'};

  final startTime = DateTime.now();
  while (DateTime.now().difference(startTime) < timeout) {
    try {
      final response = await http.get(
        Uri.parse(url),
        headers: headers,
      );

      if (response.statusCode != 200) {
        print('Error fetching job: ${response.statusCode}');
        return null;
      }

      final data = jsonDecode(response.body);
      final status = data['status'];

      if (status == 'completed') {
        final orderId = data['result']?['orderId'];
        print('✓ Order created: $orderId');
        return {'status': 'completed', 'orderId': orderId};
      } else if (status == 'failed') {
        print('✗ Job failed: ${data['errorMessage']}');
        return {'status': 'failed'};
      }

      print('Status: $status... waiting');
      await Future.delayed(Duration(seconds: 2));
    } catch (e) {
      print('Error fetching job: $e');
      return null;
    }
  }

  print('Timeout waiting for job completion');
  return null;
}
import 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 None
const 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
done

Handle 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}, 200
const 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