Overview
SLP-Connect webhooks provide real-time notifications when order statuses change.
Instead of polling our API, your system receives an HTTP POST request whenever
an order transitions to processing or shipped status.
Setup
To enable webhooks for your account, contact your SLP-Connect account manager with:
- Webhook URL - Your HTTPS endpoint that will receive POST requests
- Webhook Secret - We'll generate a secret for you (starts with
whsec_)
Endpoint Requirements
- Must accept
POSTrequests - Must respond with a
2xxstatus code within 10 seconds - Must accept
application/jsoncontent type
Security
Every webhook request includes a cryptographic signature so you can verify it originated from SLP-Connect and wasn't tampered with.
Request Headers
| Header | Description |
|---|---|
X-Webhook-Signature |
HMAC-SHA256 signature (e.g., sha256=abc123...) |
X-Webhook-Timestamp |
Unix timestamp when the request was sent |
X-Webhook-Event |
Event type (e.g., order.status_changed) |
X-Webhook-ID |
Unique delivery ID for idempotency |
Content-Type |
application/json |
User-Agent |
SLP-Connect-Webhooks/1.0 |
Event Types
Currently, webhooks are sent for the following status transitions:
| Event | Trigger | Description |
|---|---|---|
order.status_changed |
Order → processing |
Order has been received and is being prepared |
order.status_changed |
Order → shipped |
Order has shipped with tracking information |
Payload Structure
All webhook payloads follow this structure:
{
"event": "order.status_changed",
"timestamp": "2026-01-21T02:30:00.000Z",
"data": {
"client_order_number": "YOUR-ORDER-123",
"order_number": "ORD-2026-000069",
"new_status": "shipped",
"previous_status": "processing",
...
}
}
Root Fields
| Field | Type | Description |
|---|---|---|
event |
string | Event type (e.g., order.status_changed) |
timestamp |
string (ISO 8601) | When the webhook was generated |
data |
object | Event-specific payload data |
Schema Reference
Processing Status Payload
Sent when an order transitions to processing:
| Field | Type | Description |
|---|---|---|
data.client_order_number |
string | Your reference number for the order |
data.order_number |
string | SLP-Connect order number |
data.new_status |
string | processing |
data.previous_status |
string | Previous status (typically created) |
Shipped Status Payload
Sent when an order transitions to shipped. Includes tracking and item details:
| Field | Type | Description |
|---|---|---|
data.client_order_number |
string | Your reference number for the order |
data.order_number |
string | SLP-Connect order number |
data.new_status |
string | shipped |
data.previous_status |
string | Previous status (typically processing) |
data.outbound_tracking_number |
string | Carrier tracking number for outbound shipment |
data.outbound_tracking_url |
string | Direct link to carrier tracking page |
data.carrier |
string | Carrier display name (e.g., "United Parcel Service") |
data.carrier_code |
string | Carrier code matching the API (e.g., "UPS") |
data.service |
string | Service display name (e.g., "UPS Ground") |
data.service_code |
string | Service code matching the API (e.g., "ups_ground") |
data.items |
array | Array of shipped items (see below) |
Items Array
Each item in the data.items array represents a single kit:
| Field | Type | Description |
|---|---|---|
sku |
string | Product SKU |
kit_id |
string | Unique kit identifier |
sample_id |
string | null | Sample ID (only present if applicable) |
lot |
string | null | Lot number of shipped product |
expiration |
string | null | Expiration date (YYYY-MM-DD format) |
return_tracking_number |
string | null | Return label tracking number (if applicable) |
Examples
Processing Webhook
{
"event": "order.status_changed",
"timestamp": "2026-01-21T02:15:00.000Z",
"data": {
"client_order_number": "ACME-ORD-12345",
"order_number": "ORD-2026-000069",
"new_status": "processing",
"previous_status": "created"
}
}
Shipped Webhook (Single Kit)
{
"event": "order.status_changed",
"timestamp": "2026-01-21T02:30:00.000Z",
"data": {
"client_order_number": "ACME-ORD-12345",
"order_number": "ORD-2026-000069",
"new_status": "shipped",
"previous_status": "processing",
"outbound_tracking_number": "1ZR0829H0327081021",
"outbound_tracking_url": "https://www.ups.com/track?tracknum=1ZR0829H0327081021",
"carrier": "United Parcel Service",
"carrier_code": "UPS",
"service": "UPS Ground",
"service_code": "ups_ground",
"items": [
{
"sku": "SLP001",
"kit_id": "2K34-1N2J1",
"lot": "010926-01",
"expiration": "2029-01-19",
"return_tracking_number": "1ZR0829H0338909438"
}
]
}
}
Shipped Webhook (Multiple Kits)
{
"event": "order.status_changed",
"timestamp": "2026-01-21T02:31:00.776Z",
"data": {
"client_order_number": "ACME-MULTI-001",
"order_number": "ORD-2026-000070",
"new_status": "shipped",
"previous_status": "processing",
"outbound_tracking_number": "1ZR0829H0335025602",
"outbound_tracking_url": "https://www.ups.com/track?tracknum=1ZR0829H0335025602",
"carrier": "United Parcel Service",
"carrier_code": "UPS",
"service": "UPS Ground",
"service_code": "ups_ground",
"items": [
{
"sku": "SLP001",
"kit_id": "2K34-1N2J1",
"lot": "010926-01",
"expiration": "2029-01-19",
"return_tracking_number": "1ZR0829H0338909438"
},
{
"sku": "SLP001",
"kit_id": "7H92-4PK8R",
"lot": "010926-01",
"expiration": "2029-01-19",
"return_tracking_number": "1ZR0829H0333943641"
}
]
}
}
Shipped Webhook (With Sample ID)
{
"event": "order.status_changed",
"timestamp": "2026-01-21T03:00:00.000Z",
"data": {
"client_order_number": "ECOM-12345",
"order_number": "ORD-2026-000075",
"new_status": "shipped",
"previous_status": "processing",
"outbound_tracking_number": "1Z999AA10123456784",
"outbound_tracking_url": "https://www.ups.com/track?tracknum=1Z999AA10123456784",
"carrier": "United Parcel Service",
"carrier_code": "UPS",
"service": "UPS Ground",
"service_code": "ups_ground",
"items": [
{
"sku": "SLP001",
"kit_id": "2K34-1N2J1",
"sample_id": "SLP-J213H4",
"lot": "012026-02",
"expiration": "2029-06-15",
"return_tracking_number": "1Z999AA10123456785"
}
]
}
}
Signature Verification
Verify webhook authenticity by computing an HMAC-SHA256 signature and comparing
it to the X-Webhook-Signature header.
Signature Format
The signature is computed over: {timestamp}.{raw_body}
Node.js Example
const crypto = require('crypto');
function verifyWebhook(req, secret) {
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const body = req.rawBody; // Raw request body as string
// Compute expected signature
const payload = `${timestamp}.${body}`;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
// Constant-time comparison
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Python Example
import hmac
import hashlib
def verify_webhook(headers, body, secret):
signature = headers.get('X-Webhook-Signature')
timestamp = headers.get('X-Webhook-Timestamp')
# Compute expected signature
payload = f"{timestamp}.{body}"
expected = 'sha256=' + hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
# Constant-time comparison
return hmac.compare_digest(signature, expected)
Retry Policy
If your endpoint doesn't respond with a 2xx status code within 10 seconds,
we'll retry the delivery with exponential backoff.
| Attempt | Delay After Failure |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failed attempts, the webhook is marked as exhausted and no further retries are attempted.
X-Webhook-ID header to detect duplicate deliveries.
Store processed IDs and skip if you've already handled that webhook.