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.

🛈
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. Webhook URL - Your HTTPS endpoint that will receive POST requests
  2. Webhook Secret - We'll generate a secret for you (starts with whsec_)
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

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)
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.