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.

🛈
Benefits Webhooks eliminate polling overhead, reduce API calls, and ensure your system is updated within seconds of a status change.

Setup

To enable webhooks for your account, contact your SLP-Connect account manager with:

  1. Order Webhook URL - Your HTTPS endpoint for order status notifications
  2. Tracking Webhook URL (optional) - A separate HTTPS endpoint for shipment tracking updates
  3. Webhook Secret(s) - We'll generate secrets for you (starts with whsec_)
🛈
Two Separate URLs Order webhooks and tracking webhooks are delivered to different URLs with separate secrets, allowing you to route them to different services or enable only the ones you need.
HTTPS Required Your webhook endpoint must use HTTPS. HTTP endpoints are not supported for security reasons.

Endpoint Requirements

  • Must accept POST requests
  • Must respond with a 2xx status code within 10 seconds
  • Must accept application/json content 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)
🛈
Separate Webhook URLs Tracking webhooks are delivered to a different URL than order webhooks. Contact your account manager to configure your tracking webhook endpoint.

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)
Timing Attacks Always use constant-time comparison functions when verifying signatures to prevent timing attacks.

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
1Immediate
21 minute
35 minutes
430 minutes
52 hours

After 5 failed attempts, the webhook is marked as exhausted and no further retries are attempted.

🛈
Idempotency Use the X-Webhook-ID header to detect duplicate deliveries. Store processed IDs and skip if you've already handled that webhook.