Set up Webhooks
Unleashed Webhooks API Documentataion
Table of Contents
- Introduction
- Getting Started
- Webhook Concepts
- Available Events
- Setting Up Webhooks
- Webhook Event Structure
- Verifying Webhook Signatures
- Managing Subscriptions
- Monitoring Deliveries
- Best Practices
- Error Handling
- Troubleshooting
- Complete Example
- Send us your feedback
1. Introduction
Subscribe to Webhooks to receive real-time notifications when specific events occur in your Unleashed account, such as new product creation or customer updates. This provides a more efficient way to keep your integrated applications synchronized with Unleashed data compared to continuously polling the API.2. Getting Started
What you will need
- An active Unleashed account (trial accounts can also access the Webhooks API)
- API ID and Key from your Unleashed account
- A publicly accessible HTTPS endpoint with a valid SSL certificate
- Maximum of 5 webhook subscriptions per account
Authentication
Webhook API requests use the same authentication as the standard Unleashed API. Each request must include these HTTP headers:Content-Type: application/json
Accept: application/json
api-auth-id: YOUR_API_ID
api-auth-signature: CALCULATED_SIGNATURE
client-type: your_company/your_app
Important: The
Accept: application/json
header is required to receive API responses. Missing this header is a common cause of request failures.
3. Webhook Concepts
- Webhook Subscription: Configuration that defines which events to monitor and where to send notifications
- Webhook Event: The data payload sent to your endpoint when a subscribed event occurs
- Event Delivery: A record of each delivery attempt, including success/failure status
- Signature Verification: Security mechanism to verify that webhook events originated from Unleashed
4. Available Events
Object | Event Types | Description |
---|---|---|
Customer | created, updated, deleted | Customer record changes |
Product | created, updated, deleted | Product record changes |
Purchase Order | created, updated, deleted | Purchase Order changes |
Sales Order | created, updated, deleted, completed | Sales Order changes |
Sales Shipment | created, deleted | Shipment record changes |
Credit Note | created, deleted | Credit Note changes |
Supplier Return | created, updated, deleted | Supplier Return changes |
Note: Not all events are available for every object. Check the specific object documentation for available events.
5. Setting Up Webhooks
Step 1: Create a Webhook Subscription
POST /webhooks/subscriptions
Request Example:
{
"description": "Monitor customer and product changes",
"notificationUrl": "https://your-domain.com/webhook",
"eventTypes": [ "customer.created", "customer.updated", "product.created" ]
}
{
"subscriptionId": "123e4567-e89b-12d3-a456-426614174000",
"description": "Monitor customer and product changes",
"notificationUrl": "https://your-domain.com/webhook",
"active": true,
"eventTypes": [ "customer.created", "customer.updated", "product.created" ],
"createdOn": "2025-06-23T10:30:00Z",
"signatureKey": "base64_encoded_key_save_this_value"
}
⚠️ Important: Save the signatureKey from the response - this is required for verifying webhook events and is only shown once.
Step 2: Implement Your Webhook Endpoint
Your endpoint must:- Accept POST requests
- Respond with a 2XX status code quickly
- Verify the webhook signature for security
app.post('/webhook', (req, res) => {
const signature = req.headers['x-unleashed-signature'];
const timestamp = req.headers['x-unleashed-timestamp'];
// Verify signature (see verification section below)
if (verifySignature(signature, timestamp, req.body)) {
// Respond quickly
res.status(200).send('OK');
// Process webhook asynchronously
processWebhookEvent(req.body);
} else {
res.status(401).send('Unauthorized');
}
});
6. Webhook Event Structure
When an event occurs, Unleashed sends a POST request to your endpoint:{
"subscriptionId": "123e4567-e89b-12d3-a456-426614174000",
"eventNotificationId": "987fcdeb-51a2-43d1-b789-123456789000",
"eventType": "customer.created",
"createdOn": "2025-06-23T10:30:00Z",
"data": "{\"customerGuid\":\"456e7890-e12b-34c5-d678-901234567890\"}"
}
Event Data Objects
Customer Events:
{"customerGuid": "456e7890-e12b-34c5-d678-901234567890"}
Product Events:
{"productGuid": "789e0123-e45f-67g8-h901-234567890123"}
Sales Order Events:
{"salesOrderGuid": "012e3456-e78f-90g1-h234-567890123456"}
7. Verifying Webhook Signatures
Always verify webhook signatures to ensure events originated from Unleashed:function verifySignature(signature, timestamp, body, signatureKey) {
const crypto = require('crypto');
// Check timestamp (reject if older than 5 minutes)
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime - parseInt(timestamp) > 300) {
return false;
}
// Create signed payload: timestamp + body
const signedPayload = `${timestamp}.${JSON.stringify(body)}`;
// Calculate expected signature
const expectedSignature = crypto
.createHmac('sha256', signatureKey)
.update(signedPayload)
.digest('base64');
// Secure comparison
return crypto.timingSafeEqual( Buffer.from(signature, 'base64'), Buffer.from(expectedSignature, 'base64') );
}
8. Managing Subscriptions
List Subscriptions
GET /webhooks/subscriptions
Update Subscription
PUT /webhooks/subscriptions/{subscriptionId}
{
"description": "Updated description",
"active": true,
"eventTypes": ["customer.created", "customer.updated"]
}
Delete Subscription
DELETE /webhooks/subscriptions/{subscriptionId}
9. Monitoring Deliveries
List Delivery Attempts
GET /webhooks/subscriptions/{subscriptionId}/deliveries
Query Parameters:
pageSize
: Items per page (default: 200, max: 1000)successful
: Filter by success status (true/false)
Get Delivery Details
GET /webhooks/deliveries/{deliveryId}
10. Best Practices
Handle Duplicate Events
Webhooks may be delivered multiple times. Use theeventNotificationId
to detect duplicates:
const processedEvents = new Set();
function handleWebhook(event) {
if (processedEvents.has(event.eventNotificationId)) {
return; // Already processed
}
processedEvents.add(event.eventNotificationId);
processEvent(event);
}
Handle Failed Deliveries
- Unleashed retries failed deliveries for 72 hours with exponential backoff
- Webhooks are disabled after repeated failures
- Use the update subscription API to reactivate disabled webhooks
- Monitor delivery status regularly
Respond Quickly
- Return a 2XX status code as soon as possible
- Process webhook data asynchronously to avoid timeouts
- Webhook requests have a short timeout period
IP Whitelisting
For additional security, whitelist these Unleashed IP addresses:- 13.236.30.20
- 3.105.186.32
- 3.104.150.228
11. Error Handling
Common HTTP status codes returned by the Webhooks API:Status Code | Description |
---|---|
200 OK | Request successful |
400 Bad Request | Invalid request format or parameters |
401 Unauthorized | Invalid API credentials or signature |
403 Forbidden | Access denied |
404 Not Found | Subscription not found |
500 Internal Server Error | Server error |
12. Troubleshooting
Webhook Not Triggered
- Verify your endpoint is publicly accessible via HTTPS
- Check SSL certificate validity
- Confirm subscription is active and includes the correct event types
Signature Verification Fails
- Ensure you saved the signature key from subscription creation
- Verify timestamp parsing (Unix timestamp in seconds)
- Check payload format:
{timestamp}.{body}
API Requests Failing
- Include the
Accept: application/json
header in all requests - Verify API credentials and signature generation
- Check that all required headers are
13. Complete Example: Webhook Handler
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const SIGNATURE_KEY = 'your_signature_key_from_subscription';
const processedEvents = new Set();
app.post('/webhook', (req, res) => {
try {
const signature = req.headers['x-unleashed-signature'];
const timestamp = req.headers['x-unleashed-timestamp'];
// Verify signature
if (!verifySignature(signature, timestamp, req.body, SIGNATURE_KEY)) {
return res.status(401).send('Unauthorized');
}
// Respond immediately
res.status(200).send('OK');
// Check for duplicates
if (processedEvents.has(req.body.eventNotificationId)) {
return;
}
processedEvents.add(req.body.eventNotificationId);
// Process event asynchronously
processEvent(req.body);
} catch (error) {
console.error('Webhook error:', error);
res.status(500).send('Internal Server Error');
}
});
async function processEvent(event) {
console.log(`Processing ${event.eventType} event`);
const data = JSON.parse(event.data);
switch (event.eventType) {
case 'customer.created':
await handleCustomerCreated(data.customerGuid);
break;
case 'product.updated':
await handleProductUpdated(data.productGuid);
break;
// Handle other event types...
}
}
function verifySignature(signature, timestamp, body, key) {
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime - parseInt(timestamp) > 300)
return false;
const payload = `${timestamp}.${JSON.stringify(body)}`;
const expected = crypto.createHmac('sha256', key)
.update(payload)
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(signature, 'base64'),
Buffer.from(expected, 'base64')
);
}
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
});
14. Send us your feedback
If you have an idea for a new webhook, please click on the link below and send us your feedback.Product Feedback