3PL Webhook Integration Guide
How to capture and verify 3PL fulfillment webhooks from Shippo, EasyPost, and ShipBob. Handle delivery guarantees and idempotency.
Quick Answer
3PL webhooks notify your backend when shipments are created, picked, packed, shipped, or delivered. Shippo and EasyPost sign webhooks with HMAC-SHA256; ShipBob includes a signature header. Test locally with Requex.me (no tunneling needed). Key challenges: schema mapping (EDI X12), 24-48h delivery guarantees, idempotency (webhook_id persists 3 days), and exponential backoff retries.
Why 3PL Webhooks Matter for Ecommerce
Third-party logistics providers (Shippo, EasyPost, ShipBob) manage fulfillment after you hand off an order. Webhooks keep your system in sync: when the 3PL picks and ships, you need to know immediately to update order status, notify customers, and trigger downstream workflows (email campaigns, inventory adjustments, return flows).
Without webhooks, you'd poll the 3PL API constantly. With webhooks, events arrive in real-time (or with a 24-48h SLA for some providers). This enables:
- Real-time tracking number sync to order confirmation emails
- Automated return label generation when a shipment is returned
- Inventory deallocation once the 3PL confirms pick
- Customer notifications ("Your order shipped!" → "Out for delivery" → "Delivered")
- Chargeback prevention by confirming delivery timestamps
Key 3PL Webhook Events
| Provider | Events | Use Case |
|---|---|---|
| Shippo | shipment.created, label.created, track_updated | Label generation, pickup scheduling, tracking updates |
| EasyPost | shipment.updated, tracker.updated, batch.created | Bulk shipping, carrier selection, delivery confirmation |
| ShipBob | fulfillment.created, fulfillment.shipped, fulfillment.delivered | Pick/pack/ship workflow, delivery proof, return processing |
Testing 3PL Webhooks with Requex.me
Before deploying, test webhook payloads locally with a free endpoint:
- Visit Requex.me and copy your unique webhook URL.
- In your 3PL dashboard, register the webhook endpoint. Example:
https://your-id.requex.me - In your 3PL admin, create a test shipment or label.
- Watch the webhook payload appear instantly in your Requex dashboard.
- Inspect the payload, signature header, and event type. Verify your webhook handler can parse it.
Requex captures and displays the raw HTTP request including headers and body—useful for debugging signature verification before deploying to production.
Four Common 3PL Webhook Challenges
1. Schema Mapping (EDI X12/EDIFACT)
3PLs often use EDI (Electronic Data Interchange) formats like X12 or EDIFACT for integration. These are complex, hierarchical formats with fixed-width fields. Shippo and EasyPost expose webhooks in JSON, but the underlying data model differs: Shippo uses address_from/address_to; EasyPost uses from_address/to_address.
Fix: Map webhook fields to your internal schema before saving. Use a library like joi or zod to validate and transform.
2. Delivery Guarantees & Sync Delays
3PLs typically guarantee delivery within 24-48 hours. If your server is down, the 3PL may retry with exponential backoff (1 min, 5 min, 30 min, 2h, etc.). Shippo retries 10 times over 3 days. During this window, webhook_id remains the same—critical for idempotency.
Fix: Store webhook_id in your database. When a webhook arrives, check if you've already processed it. If yes, return 200 without re-processing. This prevents duplicate order status updates.
3. Idempotency & Webhook ID Expiry
The webhook_id (or event_id) in the payload is your idempotency key. However, 3PLs may only guarantee this ID for 3 days. After 3 days, if the same webhook is retried, it may come with a new webhook_id. This creates a blind spot: you can't reliably deduplicate after 3 days.
Fix: Use both webhook_id AND the business key (shipment_id + event_type) for idempotency. Query: "Do we have a record of this shipment_id being shipped?" instead of just "Is webhook_id X in our log?"
4. Signature Verification & Secret Management
Shippo and EasyPost both use HMAC-SHA256 to sign webhooks. ShipBob includes a signature header. The challenge: where do you store the webhook signing secret? Typically, the 3PL dashboard shows it once during setup. If you lose it, you can't verify new webhooks until you rotate.
Fix: Store the secret in your backend environment as SHIPPO_WEBHOOK_SECRET, never in code. Use a secrets manager (AWS Secrets Manager, Vault, etc.). Before rotating, add the new secret to your verification logic temporarily.
3PL Webhook Signature Verification
All major 3PLs sign their webhooks using HMAC-SHA256. Verify the signature before processing:
// Node.js / Express middleware for 3PL signature verification
const crypto = require('crypto');
function verify3PLSignature(req, signatureHeader, secret, algorithm = 'sha256') {
const hash = crypto
.createHmac(algorithm, secret)
.update(req.rawBody) // Raw body BEFORE JSON parsing
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(hash)
);
}
// Shippo example
app.post('/webhooks/shippo', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-shippo-signature'];
if (!verify3PLSignature(req, signature, process.env.SHIPPO_WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const payload = JSON.parse(req.body);
const webhookId = payload.id;
// Check idempotency: have we processed this webhook_id?
const existing = await Webhook.findOne({ webhookId });
if (existing) return res.status(200).json({ received: true });
// Save webhook for idempotency
await Webhook.create({ webhookId, event: payload.event, payload });
// Process shipment update
if (payload.event === 'track_updated') {
const { tracking_status } = payload.data;
await Order.update({ orderId: payload.data.order_id }, { trackingStatus: tracking_status });
}
res.status(200).json({ received: true });
});Data Flow: Order → 3PL → Webhook → Your Fulfillment Status
Here's how a typical order flows through a 3PL integration:
- Order placed: Customer buys on your storefront. Order created with items and shipping address.
- Send to 3PL: Your backend calls
POST /shipmentsto Shippo/EasyPost with order details. 3PL responds with shipment_id. - 3PL picks & packs: Warehouse receives the order, picks items, packs box. Status progresses: pending → label_generated → picked → packed.
- Label created webhook: 3PL sends webhook
label.createdwith tracking number. - Your webhook handler: Receives webhook, verifies signature, stores tracking number, updates order status to "shipped".
- Customer notified: Your email system triggers "order shipped" email with tracking link.
- Tracking updates: As package moves, carrier sends events to 3PL. 3PL forwards
track_updatedwebhooks. - Final delivery:
track_updatedevent with status "delivered" triggers final customer notification and closes fulfillment loop.
See Also
For broader integration patterns, see Shopify API integrations and Shopify alternatives for developers.
Start Testing Webhooks Now
Generate your unique URL and test webhooks instantly. Free, no signup.
Open Webhook Tester →