Unified Order Service
Home
Getting Started
  • Core API
  • Drone API
Resources
Home
Getting Started
  • Core API
  • Drone API
Resources
  • Introduction

    • Getting Started
    • System Overview
    • Authentication
  • Core Concepts

    • Working with Orders
    • Working with Channels
    • Places
    • Storage Locations
    • Places Management Dashboard
  • Integration

    • Webhooks
    • Order Status Workflows
    • FCM Push Notifications
  • Advanced Topics

    • API Keys
    • Order Versioning
    • Picking App Integration Guide
    • Subscriptions
    • Cancellation Reasons

Webhooks

The Unified Order Service (UOS) provides a webhook system that allows you to receive real-time notifications about order events. This guide explains how to configure and use webhooks, including OAuth 2.0 authentication for enhanced security.

WEBHOOKS ARE REQUIRED FOR ASYNC OPERATIONS

The UOS platform uses a Web-Queue-Worker architecture for asynchronous processing. Webhooks are NOT optional — they are the primary mechanism for receiving operation results from the platform. See the Asynchronous Platform Architecture section in the System Overview for a complete explanation.

Overview

Webhooks are HTTP callbacks that deliver data to your systems when events occur in the UOS system. Instead of continuously polling the API for updates, you can set up webhooks to receive push notifications when orders are created, updated, or their status changes.

Why Webhooks are Essential:

  • UOS API operations return 202 Accepted immediately (order queued)
  • Actual processing happens asynchronously in background workers
  • Webhooks deliver the final result (success or failure) to your system
  • Without webhooks, you won't know if operations succeeded or failed

UOS webhooks support multiple authentication methods:

  • HMAC Signature Verification: Ensures webhook authenticity using shared secrets
  • OAuth 2.0 Client Credentials Flow: Industry-standard authentication for secure API access

Webhook Events

UOS supports the following webhook event types:

EventDescription
order:createdTriggered when a new order is created
order:updatedTriggered when an order is updated
order:status_changedTriggered when an order's status changes
order:failedTriggered when an order operation fails (uses operation-type oriented approach)
webhook:testUsed to test your webhook configuration

Order Status Changed Events

The order:status_changed event supports various status transitions including fulfillment workflow events:

Fulfillment Workflow Events

  • Order Picking Started (processing → picking): When an order enters the picking phase
  • Order Picked (picking → picked): When an order is picked, including detailed item-level picking information with substitutions and short picks
  • Order Collected (retrieving → collected): When a Click & Collect order is collected by the customer, including any item rejections

Event Data Structure

All status change events include:

  • Order identification (order_id, order_number, channel_id)
  • Status transition (from and to status)
  • Metadata with context about who made the change and when
  • Event-specific data (e.g., item picking details, collection information)

Enhanced order:failed Webhook

The order:failed webhook has been updated to use an operation-type oriented approach that provides better clarity on what specific operation failed. This approach gives you more precise information about what went wrong during order processing.

Operation Types:

  • order_create - Order creation failures
  • order_status_update - Status update failures (e.g., invalid transitions)
  • order_validation - Validation failures
  • place_resolution - Place ID resolution failures

Each operation type includes specific metadata to help diagnose and handle the failure appropriately.

Configuring Webhooks

Webhooks are configured per channel. To set up a webhook for a channel, you need:

  1. A publicly accessible HTTPS URL where UOS can send webhook events
  2. A secret key to verify webhook signatures (optional but recommended)
  3. Optional OAuth 2.0 configuration for enhanced authentication

Setting Up a Basic Webhook

To configure a webhook for a channel, send a PUT request to the /v1/channels/{channelId}/webhook endpoint:

curl -X PUT "https://api.uos.example.com/v1/channels/channel-123/webhook" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-api.com/webhooks/orders",
    "webhook_secret": "your-webhook-secret",
    "webhook_enabled": true,
    "webhook_events": ["order:created", "order:updated", "order:status_changed"]
  }'

OAuth 2.0 Authentication

UOS supports OAuth 2.0 client credentials flow for webhook authentication, providing enterprise-grade security for webhook deliveries.

Why Use OAuth 2.0?

  • Enhanced Security: Industry-standard authentication protocol
  • Token-based Authentication: No long-lived credentials in webhook URLs
  • Automatic Token Management: UOS handles token acquisition and refresh
  • Audit Trail: Better tracking of authenticated requests
  • Enterprise Compliance: Meets security requirements for many organizations

OAuth 2.0 Configuration

OAuth 2.0 is configured separately from basic webhook settings and is completely optional.

Basic OAuth Configuration

curl -X PUT "https://api.uos.example.com/v1/channels/channel-123/oauth2" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "oauth2_enabled": true,
    "oauth2_client_id": "your-oauth-client-id",
    "oauth2_client_secret": "your-oauth-client-secret",
    "oauth2_token_url": "https://auth.provider.com/oauth/token",
    "oauth2_scope": "webhook:write"
  }'

Advanced OAuth Configuration

For providers requiring additional parameters:

curl -X PUT "https://api.uos.example.com/v1/channels/channel-123/oauth2" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "oauth2_enabled": true,
    "oauth2_client_id": "your-oauth-client-id",
    "oauth2_client_secret": "your-oauth-client-secret",
    "oauth2_token_url": "https://auth.provider.com/oauth/token",
    "oauth2_scope": "webhook:write orders:read",
    "oauth2_additional_params": {
      "audience": "https://api.example.com",
      "resource": "https://api.example.com"
    }
  }'

OAuth Configuration Fields

FieldRequiredDescription
oauth2_enabledYesEnable/disable OAuth 2.0 authentication
oauth2_client_idYes*OAuth 2.0 client identifier
oauth2_client_secretYes*OAuth 2.0 client secret
oauth2_token_urlYes*OAuth 2.0 token endpoint URL
oauth2_scopeNoOAuth 2.0 scope for token requests
oauth2_additional_paramsNoAdditional parameters for token requests

*Required when oauth2_enabled is true

OAuth 2.0 Management Endpoints

EndpointMethodDescription
/v1/channels/{channelId}/oauth2PUTConfigure OAuth 2.0 settings
/v1/channels/{channelId}/oauth2GETGet OAuth 2.0 configuration
/v1/channels/{channelId}/oauth2/testPOSTTest OAuth 2.0 configuration
/v1/channels/{channelId}/oauth2DELETERemove OAuth 2.0 configuration

Testing OAuth Configuration

Before enabling OAuth in production, test your configuration:

curl -X POST "https://api.uos.example.com/v1/channels/channel-123/oauth2/test" \
  -H "Authorization: Bearer YOUR_API_KEY"

Success Response:

{
  "success": true,
  "message": "OAuth 2.0 configuration is valid and working",
  "token_acquired": true,
  "oauth2_enabled": true,
  "channel_id": "channel-123",
  "tested_at": "2025-01-16T15:30:00.000Z"
}

Failure Response:

{
  "success": false,
  "message": "OAuth 2.0 configuration test failed: Invalid client credentials",
  "token_acquired": false,
  "oauth2_enabled": true,
  "channel_id": "channel-123",
  "error": "Invalid client credentials",
  "tested_at": "2025-01-16T15:30:00.000Z"
}

Webhook Delivery with OAuth

When OAuth 2.0 is enabled, UOS automatically handles token acquisition and includes the access token in webhook requests.

Webhook Headers with OAuth

POST /webhooks/orders HTTP/1.1
Host: your-api.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
X-Webhook-Signature: sha256=abc123...

{
  "event_type": "order:created",
  "event_id": "evt-123",
  "timestamp": "2025-01-16T15:30:00.000Z",
  "data": {
    "order_number": "ORD-789",
    "channel_id": "channel-123",
    ...
  }
}

Authentication Behavior

  1. OAuth + HMAC: When both are configured, UOS sends both Authorization header (OAuth) and X-Webhook-Signature header (HMAC)
  2. OAuth Fallback: If OAuth token acquisition fails, UOS continues delivery with HMAC-only authentication
  3. Token Caching: UOS automatically caches and refreshes OAuth tokens to minimize authentication overhead
  4. Error Handling: OAuth failures are logged but don't prevent webhook delivery

Webhook Signatures

To verify that webhook events are sent by UOS and not a third party, you can enable webhook signatures. When enabled, UOS will include a signature in the X-Webhook-Signature header of webhook requests.

The signature is created by computing an HMAC-SHA256 hash of the request body using your webhook secret as the key.

Verifying Webhook Signatures

To verify a webhook signature in your webhook handler:

  1. Get the signature from the X-Webhook-Signature header
  2. Compute the HMAC-SHA256 hash of the request body using your webhook secret
  3. Compare the computed hash with the signature from the header

Example in Node.js

const crypto = require('crypto');
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhooks/orders', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const authHeader = req.headers['authorization'];
  const body = JSON.stringify(req.body);
  const secret = 'your-webhook-secret';
  
  // Verify OAuth token (if your system supports it)
  if (authHeader && authHeader.startsWith('Bearer ')) {
    const token = authHeader.substring(7);
    if (!verifyOAuthToken(token)) {
      console.error('Invalid OAuth token');
      return res.status(401).send('Unauthorized');
    }
  }
  
  // Verify HMAC signature
  if (signature) {
    const hmac = crypto.createHmac('sha256', secret);
    const computedSignature = hmac.update(body).digest('hex');
    
    if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(computedSignature))) {
      console.error('Invalid webhook signature');
      return res.status(403).send('Invalid signature');
    }
  }
  
  // Process the webhook
  console.log('Webhook event:', req.body.event_type);
  
  switch (req.body.event_type) {
    case 'order:created':
      handleOrderCreated(req.body.data);
      break;
    case 'order:updated':
      handleOrderUpdated(req.body.data);
      break;
    case 'order:status_changed':
      handleOrderStatusChanged(req.body.data);
      break;
    case 'order:failed':
      handleOrderFailed(req.body.data);
      break;
    default:
      console.log('Unknown event type:', req.body.event_type);
  }
  
  res.status(200).send('Webhook received');
});

function verifyOAuthToken(token) {
  // Implement OAuth token verification according to your OAuth provider
  // This might involve JWT verification or token introspection
  return true; // Simplified for example
}

Security Best Practices

OAuth 2.0 Security

  1. Secure Client Secrets: Store OAuth client secrets securely (environment variables, key management systems)
  2. Scope Limitation: Use the most restrictive OAuth scopes possible
  3. Token Validation: Validate OAuth tokens on your webhook endpoint
  4. Regular Rotation: Rotate OAuth client credentials periodically

General Webhook Security

  1. HTTPS Only: Always use HTTPS for webhook URLs
  2. Signature Verification: Always verify webhook signatures when provided
  3. Idempotency: Handle duplicate webhook deliveries gracefully
  4. Rate Limiting: Implement rate limiting on your webhook endpoints
  5. Logging: Log webhook receipts for audit purposes

Troubleshooting

Common OAuth Issues

Problem: OAuth test fails with "Invalid client credentials"

  • Solution: Verify oauth2_client_id and oauth2_client_secret are correct
  • Check: Ensure credentials are valid for the specified oauth2_token_url

Problem: Webhooks receive without OAuth headers despite configuration

  • Solution: Check OAuth configuration with GET /v1/channels/{channelId}/oauth2
  • Check: Review UOS logs for OAuth token acquisition failures

Problem: OAuth tokens expire quickly

  • Solution: UOS automatically handles token refresh; check if your OAuth provider supports longer-lived tokens
  • Check: Verify expires_in values in OAuth provider responses

General Troubleshooting

Problem: Webhooks not being delivered

  • Check: Webhook URL is accessible from UOS servers
  • Check: Webhook events are enabled for the channel
  • Check: Channel webhook configuration is enabled

Problem: Signature verification fails

  • Check: Webhook secret matches between UOS and your system
  • Check: Request body is used exactly as received (no modifications)
  • Check: HMAC computation uses the same algorithm (SHA256)

Getting Help

For webhook-related issues:

  1. Check Logs: Review webhook delivery logs in UOS admin panel
  2. Test Configuration: Use the webhook and OAuth test endpoints
  3. Monitor: Set up monitoring for webhook delivery failures
  4. Support: Contact UOS support with specific error messages and timestamps

Migration from Legacy Authentication

If you're currently using webhook signatures only and want to add OAuth 2.0:

  1. Configure OAuth: Set up OAuth 2.0 configuration alongside existing webhook settings
  2. Test: Use the OAuth test endpoint to verify configuration
  3. Update Endpoint: Modify your webhook endpoint to handle OAuth tokens
  4. Monitor: Watch for successful OAuth-authenticated deliveries
  5. Optional: Keep HMAC signature verification as a secondary authentication method

OAuth 2.0 is additive - existing webhook functionality continues to work unchanged.

Complete Configuration Example

Here's a complete example of setting up a channel with both webhook and OAuth 2.0 configuration:

# 1. Configure basic webhook
curl -X PUT "https://api.uos.example.com/v1/channels/channel-123/webhook" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-api.com/webhooks/orders",
    "webhook_secret": "your-webhook-secret",
    "webhook_enabled": true,
    "webhook_events": ["order:created", "order:updated", "order:status_changed", "order:failed"]
  }'

# 2. Configure OAuth 2.0
curl -X PUT "https://api.uos.example.com/v1/channels/channel-123/oauth2" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "oauth2_enabled": true,
    "oauth2_client_id": "your-oauth-client-id",
    "oauth2_client_secret": "your-oauth-client-secret",
    "oauth2_token_url": "https://auth.provider.com/oauth/token",
    "oauth2_scope": "webhook:write",
    "oauth2_additional_params": {
      "audience": "https://your-api.com"
    }
  }'

# 3. Test OAuth configuration
curl -X POST "https://api.uos.example.com/v1/channels/channel-123/oauth2/test" \
  -H "Authorization: Bearer YOUR_API_KEY"

# 4. Test webhook delivery
curl -X POST "https://api.uos.example.com/v1/channels/channel-123/webhook/test" \
  -H "Authorization: Bearer YOUR_API_KEY"

Your webhook endpoint will now receive requests with both OAuth 2.0 authentication and HMAC signature verification, providing multiple layers of security for your webhook integrations.

Troubleshooting OAuth 2.0 Webhooks

Common Issues and Solutions

400 Bad Request During OAuth2 Configuration

Symptoms:

  • Receiving 400 Bad Request when configuring OAuth2 for webhooks
  • Error message: "Your request has bad syntax or is inherently impossible to satisfy"

Root Cause: This issue typically occurs when the receiving OAuth2 endpoint doesn't properly parse application/x-www-form-urlencoded requests. UOS sends OAuth2 token requests using the standard form-encoded format as required by the OAuth2 specification.

Solution: Ensure your OAuth2 token endpoint properly handles form-encoded requests. If you're implementing a custom OAuth2 server, make sure you include form parsing middleware:

// Express.js example
app.use(express.json());
app.use(express.urlencoded({ extended: true })); // Required for OAuth2

Verification: Use the OAuth2 test endpoint to verify your configuration:

curl -X POST "https://api.uos.example.com/v1/channels/{channelId}/oauth2/test" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json"

Missing or Invalid Client Credentials

Symptoms:

  • OAuth2 test fails with "invalid_client" error
  • Token acquisition fails during webhook delivery

Solution: Verify all required OAuth2 fields are correctly configured:

  • oauth2_client_id: Must not be empty or contain only whitespace
  • oauth2_client_secret: Must not be empty or contain only whitespace
  • oauth2_token_url: Must be a valid HTTP/HTTPS URL
  • oauth2_enabled: Must be set to true

Token Acquisition Failures

Symptoms:

  • OAuth2 test shows "Token acquisition failed"
  • Webhooks not being delivered

Common Causes:

  1. Invalid credentials: Double-check your client ID and secret
  2. Wrong token URL: Ensure the token endpoint URL is correct
  3. Network issues: Verify UOS can reach your OAuth2 server
  4. Scope issues: Some providers require specific scopes

Debugging Steps:

  1. Test your OAuth2 credentials independently using curl:

    curl -X POST "https://your-oauth-server.com/oauth/token" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=client_credentials&client_id=YOUR_ID&client_secret=YOUR_SECRET"
    
  2. Check UOS logs for detailed error messages

  3. Use the OAuth2 test endpoint for validation

Testing and Validation

UOS provides comprehensive testing tools for OAuth2 webhook functionality:

OAuth2 Configuration Test

# Test OAuth2 configuration
POST /v1/channels/{channelId}/oauth2/test

# Example response
{
  "success": true,
  "oauth2_enabled": true,
  "token_acquired": true,
  "tested_at": "2024-01-15T10:30:00Z",
  "channel_id": "channel-123"
}

End-to-End Webhook Testing

For comprehensive testing of the entire OAuth2 webhook flow, UOS includes automated test harnesses that:

  1. Create test channels with OAuth2 configuration
  2. Set up mock OAuth2 servers for testing
  3. Trigger webhook events through order status changes
  4. Verify OAuth2 authentication in delivered webhooks
  5. Validate complete request flow from configuration to delivery

These test harnesses are available in the UOS codebase under /scripts/ for development and integration testing.

OAuth2 Request Format

UOS sends OAuth2 token requests using the standard format specified in RFC 6749:

Content-Type: application/x-www-form-urlencoded

Request Body:

grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=YOUR_SCOPE

Expected Response:

{
  "access_token": "token_value",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "requested_scope"
}

Best Practices

  1. Test Configuration: Always use the OAuth2 test endpoint before going live
  2. Monitor Logs: Check UOS logs for OAuth2-related errors
  3. Secure Storage: Ensure client secrets are properly secured
  4. Token Refresh: UOS automatically handles token refresh, but monitor for failures
  5. Fallback Handling: Consider how to handle OAuth2 failures gracefully

Next Steps

  • API Reference
  • Order Status Workflows
  • FCM Notifications

Loop Prevention (Advanced)

UOS provides an advanced webhook loop prevention system to prevent infinite webhook loops when systems update orders and receive their own notifications back.

Problem: Webhook Loops

graph LR
    A[CC Adapter] -->|Updates Order| B[UOS]
    B -->|Sends Webhook| A
    A -->|Updates Order Again| B
    B -->|Sends Webhook| A
    style A fill:#ffcccc
    style B fill:#ccccff

Solution: Origin Headers

Systems can include optional headers to enable loop prevention:

# CC Adapter request with origin tracking
PATCH /api/v1/orders/ORD-123
X-Command-Origin: cc-adapter
X-Correlation-Id: corr-456
Content-Type: application/json

{"status": "completed"}

How It Works

When UOS receives a request with origin headers:

  1. Processes the order update normally
  2. Sends webhooks to all configured endpoints
  3. Skips the webhook to the originating system (loop prevention!)
  4. Includes context in all webhook payloads

Enhanced Webhook Payloads

All webhooks now include optional loop prevention context:

{
  "event": "order:status_changed",
  "caused_by": "cc-adapter",        // NEW: Origin system (if provided)
  "correlation_id": "corr-456",     // NEW: Request correlation ID
  "data": {
    "id": "order-uuid",
    "order_number": "ORD-123",
    "old_status": "preparing", 
    "new_status": "completed"
  }
}

Enhanced Webhook Headers

All outbound webhooks include context headers (when available):

X-Event-Caused-By: cc-adapter
X-Correlation-Id: corr-456
X-UOS-Event: order:status_changed
X-UOS-Source: UOS

Backward Compatibility

Zero Breaking Changes

The loop prevention system is completely optional and maintains full backward compatibility:

  • Existing code works unchanged
  • Requests without headers work normally
  • Legacy integrations continue functioning
  • Manual testing unaffected

Usage

For systems that need loop prevention (like CC Adapter):

// Include origin headers in requests
const headers = {
  'X-Command-Origin': 'your-system-name',
  'X-Correlation-Id': `corr-${Date.now()}`
};

await fetch(`${UOS_URL}/api/v1/orders/${orderId}`, {
  method: 'PATCH',
  headers: {
    ...headers,
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${apiKey}`
  },
  body: JSON.stringify({status: 'completed'})
});

For comprehensive documentation on loop prevention, see:

  • Webhook Loop Prevention Guide
  • System Notifications CC Integration
Last Updated: 12/1/25, 11:31 AM
Next
Order Status Workflows