Flexport Webhooks & Events
Overview
Flexport sends webhook notifications for shipment milestones, booking confirmations, PO updates, invoice events, and document availability. Webhooks are configured in Portal > Settings with a secret token for HMAC-SHA256 signature verification via the X-Hub-Signature header.
Webhook Event Types
| Category |
Events |
Use Case |
| Milestones |
cargo_ready, departed, arrived, customs_cleared, delivered |
Shipment tracking |
| Transit |
estimated_departure, estimated_arrival, actual_departure |
ETA updates |
| Bookings |
booking_confirmed, booking_amended |
Booking lifecycle |
| Purchase Orders |
po_created, po_updated, po_archived |
PO management |
| Invoices |
invoice_created, freight_invoice_ready |
Billing |
| Documents |
document_uploaded, bill_of_lading_ready |
Document management |
| Container |
container_loaded, container_unloaded |
Container tracking |
Instructions
Step 1: Create Webhook Endpoint in Flexport
Navigate to Portal > Settings > Webhooks > Add Endpoint:
- URL:
https://your-app.com/webhooks/flexport
- Secret: Generate a strong random string
- Events: Select event types to subscribe to
Step 2: Implement Webhook Handler
import crypto from 'crypto';
import express from 'express';
const app = express();
// IMPORTANT: Use raw body for signature verification
app.post('/webhooks/flexport', express.raw({ type: '*/*' }), async (req, res) => {
// Step 1: Verify signature
const signature = req.headers['x-hub-signature'] as string;
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.FLEXPORT_WEBHOOK_SECRET!)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
console.error('Invalid webhook signature');
return res.status(401).send('Invalid signature');
}
// Step 2: Parse and route event
const event = JSON.parse(req.body.toString());
console.log(`Webhook: ${event.type} | ID: ${event.data?.id}`);
try {
await routeEvent(event);
res.status(200).send('OK');
} catch (err) {
console.error('Webhook processing failed:', err);
res.status(500).send('Processing error');
// Dead letter: store for retry
}
});
// Step 3: Route events to handlers
async function routeEvent(event: { type: string; data: any }) {
switch (event.type) {
case 'shipment.milestone':
await handleMilestone(event.data);
break;
case 'shipment.eta_updated':
await handleETAUpdate(event.data);
break;
case 'booking.confirmed':
await handleBookingConfirmed(event.data);
break;
case 'invoice.created':
await handleInvoice(event.data);
break;
case 'document.uploaded':
await handleDocument(event.data);
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
}
Step 3: Handle Shipment Milestones
async function handleMilestone(data: {
shipment_id: string;
milestone: string;
occurred_at: string;
location?: { name: string; country: string };
}) {
console.log(`Milestone: ${data.milestone} for ${data.shipment_id}`);
console.log(` At: ${data.occurred_at} | Location: ${data.location?.name}`);
// Update your database
await db.shipments.update({
where: { flexportId: data.shipment_id },
data: {
status: data.milestone,
lastMilestoneAt: new Date(data.occurred_at),
currentLocation: data.location?.name,
},
});
// Notify stakeholders for key milestones
if (['departed', 'arrived', 'delivered'].includes(data.milestone)) {
await notifyStakeholders(data.shipment_id, data.milestone);
}
}
Step 4: Idempotent Processing
// Flexport may retry webhooks — ensure idempotent handling
async function processWebhookIdempotently(event: any) {
const eventId = event.id || crypto.createHash('sha256')
.update(JSON.stringify(event)).digest('hex');
// Check if already processed
const exists = await db.webhookLog.findUnique({ where: { eventId } });
if (exists) {
console.log(`Duplicate webhook ${eventId}, skipping`);
return;
}
await db.$transaction([
db.webhookLog.create({ data: { eventId, type: event.type, processedAt: new Date() } }),
routeEvent(event),
]);
}
Error Handling
| Issue |
Cause |
Solution |
| 401 signature mismatch |
Wrong secret or body parsing |
Use express.raw(), verify secret matches Portal |
| Duplicate events |
Flexport retries on timeout |
Implement idempotency with event ID dedup |
| Missing events |
Endpoint unreachable |
Monitor uptime, use dead letter queue |
| Slow processing |
Complex handler logic |
Acknowledge fast (200), process async |
Resources
Next Steps
For performance optimization, see flexport-performance-tuning.
Source: jeremylongshore/claude-code-plugins-plus-skills → plugins/saas-packs/flexport-pack/skills/flexport-webhooks-events/SKILL.md
1---2name: flexport-webhooks-events3description: "'Implement Flexport webhook event handling for shipment milestones, booking updates, purchase order events, and invoice notifications. Trigger: \"flexport webhooks\", \"flexport events\", \"flexport milestones\", \"flexport shipment tracking webhook\". '"4allowed-tools: "Read, Write, Edit, Bash(npm:*), Bash(curl:*)"5category: backend-and-data6source_repo: jeremylongshore/claude-code-plugins-plus-skills7source_path: "plugins/saas-packs/flexport-pack/skills/flexport-webhooks-events/SKILL.md"8source_url: https://github.com/jeremylongshore/claude-code-plugins-plus-skills/blob/HEAD/plugins/saas-packs/flexport-pack/skills/flexport-webhooks-events/SKILL.md9---1011# Flexport Webhooks & Events1213## Overview1415Flexport sends webhook notifications for shipment milestones, booking confirmations, PO updates, invoice events, and document availability. Webhooks are configured in Portal > Settings with a secret token for HMAC-SHA256 signature verification via the `X-Hub-Signature` header.1617## Webhook Event Types1819| Category | Events | Use Case |20|----------|--------|----------|21| Milestones | `cargo_ready`, `departed`, `arrived`, `customs_cleared`, `delivered` | Shipment tracking |22| Transit | `estimated_departure`, `estimated_arrival`, `actual_departure` | ETA updates |23| Bookings | `booking_confirmed`, `booking_amended` | Booking lifecycle |24| Purchase Orders | `po_created`, `po_updated`, `po_archived` | PO management |25| Invoices | `invoice_created`, `freight_invoice_ready` | Billing |26| Documents | `document_uploaded`, `bill_of_lading_ready` | Document management |27| Container | `container_loaded`, `container_unloaded` | Container tracking |2829## Instructions3031### Step 1: Create Webhook Endpoint in Flexport3233Navigate to Portal > Settings > Webhooks > Add Endpoint:3435- URL: `https://your-app.com/webhooks/flexport`36- Secret: Generate a strong random string37- Events: Select event types to subscribe to3839### Step 2: Implement Webhook Handler4041```typescript42import crypto from 'crypto';43import express from 'express';4445const app = express();4647// IMPORTANT: Use raw body for signature verification48app.post('/webhooks/flexport', express.raw({ type: '*/*' }), async (req, res) => {49 // Step 1: Verify signature50 const signature = req.headers['x-hub-signature'] as string;51 const expected = 'sha256=' + crypto52 .createHmac('sha256', process.env.FLEXPORT_WEBHOOK_SECRET!)53 .update(req.body)54 .digest('hex');5556 if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {57 console.error('Invalid webhook signature');58 return res.status(401).send('Invalid signature');59 }6061 // Step 2: Parse and route event62 const event = JSON.parse(req.body.toString());63 console.log(`Webhook: ${event.type} | ID: ${event.data?.id}`);6465 try {66 await routeEvent(event);67 res.status(200).send('OK');68 } catch (err) {69 console.error('Webhook processing failed:', err);70 res.status(500).send('Processing error');71 // Dead letter: store for retry72 }73});7475// Step 3: Route events to handlers76async function routeEvent(event: { type: string; data: any }) {77 switch (event.type) {78 case 'shipment.milestone':79 await handleMilestone(event.data);80 break;81 case 'shipment.eta_updated':82 await handleETAUpdate(event.data);83 break;84 case 'booking.confirmed':85 await handleBookingConfirmed(event.data);86 break;87 case 'invoice.created':88 await handleInvoice(event.data);89 break;90 case 'document.uploaded':91 await handleDocument(event.data);92 break;93 default:94 console.log(`Unhandled event type: ${event.type}`);95 }96}97```9899### Step 3: Handle Shipment Milestones100101```typescript102async function handleMilestone(data: {103 shipment_id: string;104 milestone: string;105 occurred_at: string;106 location?: { name: string; country: string };107}) {108 console.log(`Milestone: ${data.milestone} for ${data.shipment_id}`);109 console.log(` At: ${data.occurred_at} | Location: ${data.location?.name}`);110111 // Update your database112 await db.shipments.update({113 where: { flexportId: data.shipment_id },114 data: {115 status: data.milestone,116 lastMilestoneAt: new Date(data.occurred_at),117 currentLocation: data.location?.name,118 },119 });120121 // Notify stakeholders for key milestones122 if (['departed', 'arrived', 'delivered'].includes(data.milestone)) {123 await notifyStakeholders(data.shipment_id, data.milestone);124 }125}126```127128### Step 4: Idempotent Processing129130```typescript131// Flexport may retry webhooks — ensure idempotent handling132async function processWebhookIdempotently(event: any) {133 const eventId = event.id || crypto.createHash('sha256')134 .update(JSON.stringify(event)).digest('hex');135136 // Check if already processed137 const exists = await db.webhookLog.findUnique({ where: { eventId } });138 if (exists) {139 console.log(`Duplicate webhook ${eventId}, skipping`);140 return;141 }142143 await db.$transaction([144 db.webhookLog.create({ data: { eventId, type: event.type, processedAt: new Date() } }),145 routeEvent(event),146 ]);147}148```149150## Error Handling151152| Issue | Cause | Solution |153|-------|-------|----------|154| 401 signature mismatch | Wrong secret or body parsing | Use `express.raw()`, verify secret matches Portal |155| Duplicate events | Flexport retries on timeout | Implement idempotency with event ID dedup |156| Missing events | Endpoint unreachable | Monitor uptime, use dead letter queue |157| Slow processing | Complex handler logic | Acknowledge fast (200), process async |158159## Resources160161- [Flexport Webhook API](https://apidocs.flexport.com/v2/tag/Webhook-Endpoints/)162- [Flexport Developer Portal](https://developers.flexport.com/)163164## Next Steps165166For performance optimization, see `flexport-performance-tuning`.167168---169170**Source:** [`jeremylongshore/claude-code-plugins-plus-skills`](https://github.com/jeremylongshore/claude-code-plugins-plus-skills) → `plugins/saas-packs/flexport-pack/skills/flexport-webhooks-events/SKILL.md`