Subscribe to real-time events and receive instant notifications when important actions occur in your account.
Webhooks allow you to receive real-time HTTP notifications when events occur in your 3PL SHIP account. Instead of polling our API, webhooks push data to your application as events happen.
Instant notifications as they happen
HMAC SHA-256 signature verification
Automatic retry with exponential backoff
https://app.3plship.com/api/v1/webhooksCreate a new webhook subscription to receive events at your endpoint.
{
"url": "https://your-domain.com/webhooks/3plship",
"events": [
"order.created",
"order.fulfilled",
"shipment.tracking_updated",
"inventory.low_stock"
],
"description": "Production webhook endpoint",
"active": true
}{
"success": true,
"data": {
"id": "wh_1234567890",
"url": "https://your-domain.com/webhooks/3plship",
"events": [
"order.created",
"order.fulfilled",
"shipment.tracking_updated",
"inventory.low_stock"
],
"secret": "whsec_K9x7mN2pQ5vR8tY4zB6cD1fH3jL0wE",
"active": true,
"created_at": "2025-01-12T10:30:00.000Z"
},
"meta": {
"timestamp": "2025-01-12T10:30:00.000Z",
"requestId": "req_abc123xyz",
"version": "v1"
}
}The webhook secret is only shown once. Store it securely to verify webhook signatures.
| Event Type | Description |
|---|---|
order.created | New order received |
order.fulfilled | Order has been fulfilled and shipped |
order.shipped | Order shipment created and tracking available |
order.delivered | Order delivered to customer |
order.cancelled | Order has been cancelled |
shipment.created | Shipment label generated |
shipment.tracking_updated | Tracking status changed |
inventory.updated | Inventory quantity changed |
inventory.low_stock | Stock level below threshold |
return.created | Return request initiated |
return.received | Return received at warehouse |
{
"id": "evt_1234567890",
"type": "order.fulfilled",
"created_at": "2025-01-12T15:30:00.000Z",
"data": {
"object": "order",
"id": "ord_abc123",
"order_number": "ORD-2025-0001",
"status": "fulfilled",
"customer": {
"email": "customer@example.com",
"name": "John Doe"
},
"shipment": {
"tracking_number": "1Z999AA10123456784",
"carrier": "UPS",
"service": "Ground"
},
"fulfilled_at": "2025-01-12T15:30:00.000Z"
}
}All webhook requests include an X-Webhook-Signature header containing an HMAC SHA-256 signature. Verify this signature to ensure the request came from 3PL SHIP.
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}
// In your webhook handler
app.post('/webhooks/3plship', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process webhook event
const event = req.body;
console.log('Received event:', event.type);
res.status(200).send('OK');
});import hmac
import hashlib
def verify_webhook_signature(payload, signature, secret):
"""Verify webhook signature using HMAC SHA-256"""
computed_signature = hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, computed_signature)
# In your Flask webhook handler
@app.route('/webhooks/3plship', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Webhook-Signature')
payload = request.get_data(as_text=True)
if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
return jsonify({'error': 'Invalid signature'}), 401
event = request.json
print(f"Received event: {event['type']}")
return jsonify({'status': 'ok'}), 200curl -X POST https://app.3plship.com/api/v1/webhooks/wh_1234567890/test \
-H "Authorization: Bearer zl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"event_type": "order.created"
}'If your endpoint doesn't respond with a 2xx status code, we'll retry the webhook delivery using exponential backoff:
After 5 failed attempts, the webhook will be marked as failed and you'll receive an email notification.