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 Acceptedimmediately (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:
| Event | Description |
|---|---|
order:created | Triggered when a new order is created |
order:updated | Triggered when an order is updated |
order:status_changed | Triggered when an order's status changes |
order:failed | Triggered when an order operation fails (uses operation-type oriented approach) |
webhook:test | Used 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 (
fromandtostatus) - 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 failuresorder_status_update- Status update failures (e.g., invalid transitions)order_validation- Validation failuresplace_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:
- A publicly accessible HTTPS URL where UOS can send webhook events
- A secret key to verify webhook signatures (optional but recommended)
- 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
| Field | Required | Description |
|---|---|---|
oauth2_enabled | Yes | Enable/disable OAuth 2.0 authentication |
oauth2_client_id | Yes* | OAuth 2.0 client identifier |
oauth2_client_secret | Yes* | OAuth 2.0 client secret |
oauth2_token_url | Yes* | OAuth 2.0 token endpoint URL |
oauth2_scope | No | OAuth 2.0 scope for token requests |
oauth2_additional_params | No | Additional parameters for token requests |
*Required when oauth2_enabled is true
OAuth 2.0 Management Endpoints
| Endpoint | Method | Description |
|---|---|---|
/v1/channels/{channelId}/oauth2 | PUT | Configure OAuth 2.0 settings |
/v1/channels/{channelId}/oauth2 | GET | Get OAuth 2.0 configuration |
/v1/channels/{channelId}/oauth2/test | POST | Test OAuth 2.0 configuration |
/v1/channels/{channelId}/oauth2 | DELETE | Remove 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
- OAuth + HMAC: When both are configured, UOS sends both
Authorizationheader (OAuth) andX-Webhook-Signatureheader (HMAC) - OAuth Fallback: If OAuth token acquisition fails, UOS continues delivery with HMAC-only authentication
- Token Caching: UOS automatically caches and refreshes OAuth tokens to minimize authentication overhead
- 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:
- Get the signature from the
X-Webhook-Signatureheader - Compute the HMAC-SHA256 hash of the request body using your webhook secret
- 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
- Secure Client Secrets: Store OAuth client secrets securely (environment variables, key management systems)
- Scope Limitation: Use the most restrictive OAuth scopes possible
- Token Validation: Validate OAuth tokens on your webhook endpoint
- Regular Rotation: Rotate OAuth client credentials periodically
General Webhook Security
- HTTPS Only: Always use HTTPS for webhook URLs
- Signature Verification: Always verify webhook signatures when provided
- Idempotency: Handle duplicate webhook deliveries gracefully
- Rate Limiting: Implement rate limiting on your webhook endpoints
- Logging: Log webhook receipts for audit purposes
Troubleshooting
Common OAuth Issues
Problem: OAuth test fails with "Invalid client credentials"
- Solution: Verify
oauth2_client_idandoauth2_client_secretare 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_invalues 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:
- Check Logs: Review webhook delivery logs in UOS admin panel
- Test Configuration: Use the webhook and OAuth test endpoints
- Monitor: Set up monitoring for webhook delivery failures
- 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:
- Configure OAuth: Set up OAuth 2.0 configuration alongside existing webhook settings
- Test: Use the OAuth test endpoint to verify configuration
- Update Endpoint: Modify your webhook endpoint to handle OAuth tokens
- Monitor: Watch for successful OAuth-authenticated deliveries
- 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 Requestwhen 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 whitespaceoauth2_client_secret: Must not be empty or contain only whitespaceoauth2_token_url: Must be a valid HTTP/HTTPS URLoauth2_enabled: Must be set totrue
Token Acquisition Failures
Symptoms:
- OAuth2 test shows "Token acquisition failed"
- Webhooks not being delivered
Common Causes:
- Invalid credentials: Double-check your client ID and secret
- Wrong token URL: Ensure the token endpoint URL is correct
- Network issues: Verify UOS can reach your OAuth2 server
- Scope issues: Some providers require specific scopes
Debugging Steps:
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"Check UOS logs for detailed error messages
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:
- Create test channels with OAuth2 configuration
- Set up mock OAuth2 servers for testing
- Trigger webhook events through order status changes
- Verify OAuth2 authentication in delivered webhooks
- 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
- Test Configuration: Always use the OAuth2 test endpoint before going live
- Monitor Logs: Check UOS logs for OAuth2-related errors
- Secure Storage: Ensure client secrets are properly secured
- Token Refresh: UOS automatically handles token refresh, but monitor for failures
- Fallback Handling: Consider how to handle OAuth2 failures gracefully
Next Steps
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:
- Processes the order update normally
- Sends webhooks to all configured endpoints
- Skips the webhook to the originating system (loop prevention!)
- 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: