Overview
SLP-Connect webhooks provide real-time notifications for order and shipment status changes.
Instead of polling our API, your system receives HTTP POST requests whenever
an order transitions to processing or shipped status,
or when shipment tracking updates to in_transit, delivered, or exception.
Setup
To enable webhooks for your account, contact your SLP-Connect account manager with:
- Order Webhook URL - Your HTTPS endpoint for order status notifications
- Tracking Webhook URL (optional) - A separate HTTPS endpoint for shipment tracking updates
- Webhook Secret(s) - We'll generate secrets 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
SLP-Connect supports two categories of webhooks, each delivered to a separate URL:
Order Status Events
Sent to your Order Webhook URL when order status changes:
| 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 |
Tracking Event Types
Sent to your Tracking Webhook URL (separate from order webhooks) when shipment tracking status changes:
| Event | Trigger | Description |
|---|---|---|
shipment.created |
Shipment created or label generated | A shipping label has been created for this shipment |
shipment.in_transit |
Package picked up by carrier | Package is in transit to the destination |
shipment.delivered |
Package delivered | Package has been delivered to the destination |
shipment.exception |
Delivery exception | An issue occurred during delivery (e.g., failed attempt, address issue) |
Tracking Payload Structure
All tracking webhook payloads follow this structure:
{
"event": "shipment.in_transit",
"timestamp": "2026-03-19T14:00:00.000Z",
"data": {
"shipment_id": "f4eeeec0-1431-40fc-a5da-22a13f1c6d45",
"order_number": "ORD-2026-000259",
"client_order_number": "CLIENT-12345",
"direction": "outbound",
"tracking_number": "1ZR0829H0446443585",
"tracking_url": "https://www.ups.com/track?tracknum=1ZR0829H0446443585",
...
}
}
Tracking Data Fields
| Field | Type | Description |
|---|---|---|
data.shipment_id |
string | Unique shipment identifier |
data.order_number |
string | SLP-Connect order number |
data.client_order_number |
string | null | Your reference number for the order |
data.direction |
string | outbound (to customer) or return (to lab) |
data.tracking_number |
string | null | Carrier tracking number |
data.tracking_url |
string | null | Direct link to carrier tracking page |
data.carrier |
string | null | Carrier display name (e.g., "United Parcel Service") |
data.carrier_code |
string | null | Carrier code (e.g., "UPS") |
data.service |
string | null | Service display name (e.g., "UPS Ground") |
data.service_code |
string | null | Service code (e.g., "ups_ground") |
data.status |
string | Current shipment status |
data.previous_status |
string | null | Previous shipment status (null for new shipments) |
data.estimated_delivery_date |
string | null | Estimated delivery date (YYYY-MM-DD) |
data.delivered_at |
string | null | Actual delivery timestamp (only for delivered status) |
data.kit_ids |
string[] | null | Kit IDs associated with this shipment |
data.sample_ids |
string[] | null | Sample IDs associated with this shipment (if applicable) |
Tracking Webhook Examples
Shipment Created
{
"event": "shipment.created",
"timestamp": "2026-03-19T10:00:00.000Z",
"data": {
"shipment_id": "f4eeeec0-1431-40fc-a5da-22a13f1c6d45",
"order_number": "ORD-2026-000259",
"client_order_number": "CLIENT-12345",
"direction": "outbound",
"tracking_number": "1ZR0829H0446443585",
"tracking_url": "https://www.ups.com/track?tracknum=1ZR0829H0446443585",
"carrier": "United Parcel Service",
"carrier_code": "UPS",
"service": "UPS Ground",
"service_code": "ups_ground",
"status": "created",
"previous_status": null,
"estimated_delivery_date": null,
"delivered_at": null,
"kit_ids": ["2K34-1N2J1"],
"sample_ids": ["SLP-J213H4"]
}
}
Shipment In Transit
{
"event": "shipment.in_transit",
"timestamp": "2026-03-20T08:30:00.000Z",
"data": {
"shipment_id": "f4eeeec0-1431-40fc-a5da-22a13f1c6d45",
"order_number": "ORD-2026-000259",
"client_order_number": "CLIENT-12345",
"direction": "outbound",
"tracking_number": "1ZR0829H0446443585",
"tracking_url": "https://www.ups.com/track?tracknum=1ZR0829H0446443585",
"carrier": "United Parcel Service",
"carrier_code": "UPS",
"service": "UPS Ground",
"service_code": "ups_ground",
"status": "in_transit",
"previous_status": "created",
"estimated_delivery_date": "2026-03-22",
"delivered_at": null,
"kit_ids": ["2K34-1N2J1"],
"sample_ids": ["SLP-J213H4"]
}
}
Shipment Delivered
{
"event": "shipment.delivered",
"timestamp": "2026-03-22T14:15:00.000Z",
"data": {
"shipment_id": "f4eeeec0-1431-40fc-a5da-22a13f1c6d45",
"order_number": "ORD-2026-000259",
"client_order_number": "CLIENT-12345",
"direction": "outbound",
"tracking_number": "1ZR0829H0446443585",
"tracking_url": "https://www.ups.com/track?tracknum=1ZR0829H0446443585",
"carrier": "United Parcel Service",
"carrier_code": "UPS",
"service": "UPS Ground",
"service_code": "ups_ground",
"status": "delivered",
"previous_status": "in_transit",
"estimated_delivery_date": "2026-03-22",
"delivered_at": "2026-03-22T14:12:00.000Z",
"kit_ids": ["2K34-1N2J1"],
"sample_ids": ["SLP-J213H4"]
}
}
Return Shipment In Transit
{
"event": "shipment.in_transit",
"timestamp": "2026-03-25T09:00:00.000Z",
"data": {
"shipment_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"order_number": "ORD-2026-000259",
"client_order_number": "CLIENT-12345",
"direction": "return",
"tracking_number": "1ZR0829H0338909438",
"tracking_url": "https://www.ups.com/track?tracknum=1ZR0829H0338909438",
"carrier": "United Parcel Service",
"carrier_code": "UPS",
"service": "UPS Ground",
"service_code": "ups_ground",
"status": "in_transit",
"previous_status": "created",
"estimated_delivery_date": "2026-03-28",
"delivered_at": null,
"kit_ids": ["2K34-1N2J1"]
}
}
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.