Home → Blog

Logistics teams don’t lose sleep over “happy path” shipments. They lose it when storms materialize over a port of discharge, when a carrier’s safety rating changes mid-quarter, or when diesel prices spike the week you bid a strategic lane. Those are exception moments—the ones that break SLAs and erode margins. The fastest way to resolve exceptions is to surface them quickly and route them to the right humans or services with full context. This post shows how to do that by pairing event-driven design with FreightPulse’s consolidated logistics intelligence, so your operations can act in minutes, not days.

The old pattern—checking half a dozen portals, exporting CSVs, paying for yet another AIS feed just to monitor congestion, and stitching together data in a spreadsheet—can’t keep up with live freight. Developers need reliable signals they can automate against: whether a cyclone is approaching an inbound vessel’s port, whether FTL rates have shifted enough to re-route, whether a carrier is actually active and safe, whether fuel updates justify a mid-cycle surcharge tweak. FreightPulse provides those signals through one REST API; in this article, I’ll show how to convert them into webhook-driven exception workflows that push the right alert to the right channel, complete with remediation links and the next best action.

We’ll cover a practical, production-minded approach: which FreightPulse endpoints to poll on schedules appropriate to their data cadences, how to normalize and enrich events into a domain model that your operations team understands (“PortCongestionAlert”, “CarrierRiskChange”, “FuelSurchargeBreach”, “SevereWeatherProximity”), and how to fan out notifications via your webhook dispatcher to incident bots, TMS webhooks, or a custom Slack/Teams integration. We’ll include cURL requests, complete JSON examples, and code patterns you can adapt for your stack.

Why FreightPulse

Event-driven exception handling is only as good as the signals you ingest. FreightPulse consolidates critical transportation signals—live US FTL quotes, national and PADD-level fuel prices, port activity from IMF PortWatch, FMCSA/FMC carrier registries, and natural-disruption alerts—behind one consistent response shape. That means one schema to validate, one error model to catch, and one approach to retries and observability. Concretely, you benefit in four ways:

Quick Start

The FreightPulse REST API base URL is:

https://freightpulsehq.com/api/v1

Each response uses a consistent shape:

{
"success": true,
"data": { ... }
// or, if success=false:
// "error": "Human-readable message."
}

Here’s a first request to retrieve national and PADD-level fuel prices. You can run this from your CI agent, a serverless function, or a scheduled job that feeds a webhook dispatcher.

curl -H "X-API-Key: YOUR_API_KEY" \
"https://freightpulsehq.com/api/v1/fuel-prices"

Example response, which we’ll later translate into webhook-ready events when thresholds are crossed:

{
"success": true,
"data": {
"timestamp": "2026-07-30T12:00:00Z",
"source": "EIA (U.S. Energy Information Administration)",
"currency": "USD",
"unit": "gallon",
"data": {
"diesel": {
"national_average": 3.847,
"change_week": -0.023,
"change_percent": -0.6,
"regions": {
"east_coast": 3.912,
"midwest": 3.756,
"gulf_coast": 3.623,
"rocky_mountain": 3.891,
"west_coast": 4.456,
"california": 4.892
},
"historical": {
"30d_avg": 3.82,
"90d_avg": 3.75,
"yoy_change_percent": -8.2
}
},
"gasoline": {
"national_average": 3.156,
"historical": {
"30d_avg": 3.1,
"90d_avg": 3.05,
"yoy_change_percent": 1.1
}
}
}
}
}

Key fields you’ll typically use for exception workflows:

This single response can power alerts like “Fuel surcharge needs update” or “Rocky Mountain PADD diverges from national by > 10%.”

Core Endpoints

For event-driven exception workflows, you typically poll several endpoints on tailored cadences, translate deltas into domain events, and push to downstream systems via webhooks. Below are the core endpoints that generate actionable logistics signals. We’ll show cURL requests, complete JSON responses, and how to interpret key fields.

1) Fuel Prices — GET /fuel-prices

Purpose: Fetch national and PADD-region diesel/gasoline prices with weekly change and short- and mid-term historical averages. This supports surcharge recalculations, cost-to-serve monitoring, and budget variance alerts.

Key params: none.

curl -H "X-API-Key: YOUR_API_KEY" \
"https://freightpulsehq.com/api/v1/fuel-prices"

Response (complete example shown earlier) has:

Event design tip: Persist the last “effective” price set your TMS used and compare each poll. If the absolute delta or percent delta exceeds your configured threshold, emit FuelSurchargeBreach with a payload containing national and relevant PADD values plus the calculated suggested surcharge.

2) Freight Rates — GET /freight-rates

Purpose: Obtain live quotes for US FTL trucking (via Warp) and limited ocean/air into US ZIP codes from Asian origins. Use this for spot rate validation, exception routing when contract carriers reject tenders, and re-quote workflows when disruptions or fuel shifts impact transit costs.

Key params (trucking): mode=trucking, origin_zip, destination_zip. Optional pickup_date, qty, total_weight_lb.

curl -H "X-API-Key: YOUR_API_KEY" \
"https://freightpulsehq.com/api/v1/freight-rates?mode=trucking&origin_zip=10001&destination_zip=90210"

Example response:

{
"success": true,
"data": {
"mode": "trucking",
"service": "FTL (53' dry van)",
"source": "Warp",
"price_usd": 7207.92,
"transit_days": 4,
"pickup_date": "2026-08-24",
"delivery_date": "2026-08-28"
}
}

Interpretation:

Event design tip: For shipments that fall outside contract windows or face capacity constraints, poll /freight-rates for the affected lanes and emit a SpotRateDeviation event when the quote is X% over contract. Include your shipment ID, affected SKUs, and an action URL that deep-links to a one-click re-tender flow.

3) Port Congestion — GET /port-congestion

Purpose: Approximate congestion conditions at major global ports using IMF PortWatch (AIS-derived) portcall averages. This informs ETA buffers, re-routing decisions, and customer communications. Important: this signal is lagged by ~6–7 days and represents a 7-day average vs a 90-day baseline. It is not a live AIS wait-time tracker.

Key params: at least one of port (name or LOCODE like USLAX), country, or region. Optional congestion=low|moderate|high filter.

curl -H "X-API-Key: YOUR_API_KEY" \
"https://freightpulsehq.com/api/v1/port-congestion?port=USLAX"

Example response:

{
"success": true,
"data": {
"results": [
{
"port": "Los Angeles-Long Beach",
"locode": "US LAX",
"country": "United States",
"congestion": "moderate",
"as_of": "2026-08-14",
"metrics": {
"recent_avg_portcalls_per_day": 8.7,
"baseline_avg_portcalls_per_day": 10.2,
"ratio_vs_baseline": 0.85
}
}
]
}
}

Interpretation:

Event design tip: Build a PortCongestionTrend event weekly. If ratio_vs_baseline drops below a threshold or flips category (e.g., from low to moderate), emit an alert with recommended buffer days for inbound shipments targeting that port.

4) Carriers — GET /carriers

Purpose: Verify US trucking carriers (FMCSA SAFER) or ocean NVOCC/OTI entities (FMC directory). Use for onboarding, re-audits, exception handling after incidents, or compliance-driven workflows.

Key params: q (required), type=trucking|ocean|air (air returns available:false).

curl -H "X-API-Key: YOUR_API_KEY" \
"https://freightpulsehq.com/api/v1/carriers?q=54283"

Example response:

{
"success": true,
"data": {
"type": "trucking",
"query": "54283",
"results": [
{
"dot_number": 54283,
"legal_name": "SWIFT TRANSPORTATION COMPANY OF ARIZONA LLC",
"status": "active",
"safety_rating": "S",
"drivers": 12883,
"address": {
"city": "Phoenix",
"state": "AZ",
"country": "US"
}
}
]
}
}

Interpretation:

Event design tip: Run nightly re-verification for key carriers. If status != active or the safety rating changes from your stored baseline, emit a CarrierRiskChange webhook to procurement and ops channels, including the carrier’s DOT number and a proposed mitigation step (e.g., secondary tender route).

5) Disruptions — GET /disruptions

Purpose: Detect active natural-hazard disruptions (GDACS, USGS, NOAA/NWS). These are precisely the externalities that derail schedules—cyclones, earthquakes, floods, volcanoes, drought, wildfire, and severe weather. Filter by country or by proximity to a target port.

Key params: type (cyclone|earthquake|flood|volcano|drought|wildfire|severe_weather), severity (low|moderate|high), country, port (with a proximity filter: 300km; 100km for US weather). Combine as needed.

curl -H "X-API-Key: YOUR_API_KEY" \
"https://freightpulsehq.com/api/v1/disruptions?port=USLAX&severity=high"

Example response:

{
"success": true,
"data": {
"results": [
{
"id": "gdacs:TC:1001305",
"type": "cyclone",
"severity": "moderate",
"title": "Tropical Cyclone SAUDEL-26",
"country": "Japan",
"starts_at": "2026-08-18T12:00:00+00:00",
"source": "GDACS"
}
]
}
}

Interpretation:

Event design tip: A scheduled task runs every 30 minutes to scan disruptions near your key ports. When severity >= configured threshold, emit DisruptionProximityAlert with shipment IDs impacted (join by planned port) and next steps.

6) Historical Series — GET /historical

Purpose: Pull weekly national fuel-price series or daily port-congestion series for curated major ports. Use this to power thresholds based on moving averages, visualize trends inside your incident console, or compute seasonality-adjusted anomaly alerts that are less noisy than single-point thresholds.

Key params: metric (fuel_price_diesel_national, fuel_price_gasoline_national, or port_congestion:<portid>), period (30d|90d|1y).

curl -H "X-API-Key: YOUR_API_KEY" \
"https://freightpulsehq.com/api/v1/historical?metric=fuel_price_diesel_national&period=30d"

Example response:

{
"success": true,
"data": {
"metric": "fuel_price_diesel_national",
"period": "30d",
"series": [
{ "date": "2026-07-27", "value": 5.257 },
{ "date": "2026-08-03", "value": 5.343 }
]
}
}

Interpretation:

Event design tip: Compute a 4-week rolling average vs latest. If deviation exceeds Z-score thresholds, emit FuelTrendAnomaly with context (Z-score, latest value, rolling mean).

Designing Event-Driven Exception Workflows with a Webhook Dispatcher

FreightPulse provides the signals. Your job is to convert them into domain events that operations trust, with minimal noise and maximum context. A pragmatic way to do this is to run scheduled fetchers per endpoint, compute deltas vs your last-known baselines, and post webhook payloads to downstream systems (your TMS, internal incident bot, or BI pipeline). Below is a reference architecture and implementation patterns you can adapt.

Here is a minimal Node.js/Express webhook receiver you might run in your downstream systems for local testing:

const express = require('express');
const app = express();
app.use(express.json());

app.post('/webhooks/logistics', (req, res) => {
const event = req.body;
console.log('Received event:', event.type, 'id:', event.id);
// TODO: route to incident bot, TMS update, or analytics
res.status(200).send({ received: true });
});

app.listen(3000, () => console.log('Webhook listener on :3000'));

And a simple dispatcher example in JavaScript that would run inside your fetcher after detecting a meaningful delta:

async function postWebhook(eventPayload) {
const resp = await fetch('https://example.com/webhooks/logistics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(eventPayload),
});
if (!resp.ok) {
// Implement retries/backoff and push to DLQ if needed
throw new Error(`Webhook delivery failed: ${resp.status}`);
}
}

Best practices:

End-to-End Examples With Complete JSON Payloads

A) FuelSurchargeBreach event example

Polling /fuel-prices yields a week-over-week change beyond a 2% threshold. You compute a recommended surcharge and push a webhook event for immediate review.

{
"id": "evt_fuel_2026-07-30",
"type": "FuelSurchargeBreach",
"occurred_at": "2026-07-30T12:00:00Z",
"source": "freightpulse",
"payload": {
"diesel": {
"national_average": 3.847,
"change_week": -0.023,
"change_percent": -0.6,
"regions": {
"east_coast": 3.912,
"midwest": 3.756,
"gulf_coast": 3.623,
"rocky_mountain": 3.891,
"west_coast": 4.456,
"california": 4.892
},
"historical": {
"30d_avg": 3.82,
"90d_avg": 3.75,
"yoy_change_percent": -8.2
}
},
"surcharge_policy": {
"baseline_index": "EIA_diesel_national",
"threshold_percent": 2.0,
"previous_applied": 3.80,
"recommended_new": 3.85,
"justification": "Delta vs previous_applied > threshold"
},
"links": {
"review_url": "https://ops.example.com/surcharge/review?date=2026-07-30"
}
}
}

Downstream systems can auto-open a ticket with the recommended value and historical justification, speeding approvals.

B) PortCongestionTrend event example

The Los Angeles–Long Beach port ratio_vs_baseline dips below 0.85 compared to last week. You emit a trend event with remediation guidance.

{
"id": "evt_congestion_USLAX_2026-08-14",
"type": "PortCongestionTrend",
"occurred_at": "2026-08-14T00:00:00Z",
"source": "freightpulse",
"payload": {
"port": "Los Angeles-Long Beach",
"locode": "US LAX",
"country": "United States",
"as_of": "2026-08-14",
"congestion": "moderate",
"metrics": {
"recent_avg_portcalls_per_day": 8.7,
"baseline_avg_portcalls_per_day": 10.2,
"ratio_vs_baseline": 0.85
},
"policy": {
"threshold_ratio": 0.9,
"recommended_buffer_days": 2
},
"impacted_shipments": [
{ "id": "SH12345", "eta_port": "2026-08-21" },
{ "id": "SH19876", "eta_port": "2026-08-22" }
],
"links": {
"dashboard": "https://ops.example.com/ports/USLAX?as_of=2026-08-14"
}
}
}

Because port-congestion is a lagged approximation, always present “as_of” clearly and pair it with buffer guidance, not hard ETAs.

C) DisruptionProximityAlert event example

A high-severity disruption appears within range of a target port. You correlate shipments and push a consolidated alert:

{
"id": "evt_disruption_USLAX_gdacs:TC:1001305",
"type": "DisruptionProximityAlert",
"occurred_at": "2026-08-18T12:00:00Z",
"source": "freightpulse",
"payload": {
"disruption": {
"id": "gdacs:TC:1001305",
"type": "cyclone",
"severity": "moderate",
"title": "Tropical Cyclone SAUDEL-26",
"country": "Japan",
"starts_at": "2026-08-18T12:00:00+00:00"
},
"near_port": "USLAX",
"proximity_km": 300,
"impacted_shipments": [
{ "id": "SH77654", "milestone": "approaching_port" },
{ "id": "SH88901", "milestone": "loaded_on_vessel" }
],
"next_best_actions": [
"Evaluate alternate discharge port for SH77654",
"Alert consignee about potential delay (template D-3)"
],
"links": {
"incident_playbook": "https://ops.example.com/playbooks/cyclone-US-ports"
}
}
}

Note how the downstream sees not just the hazard but the operational impact and a ready-made playbook link.

Building the Fetchers: cURL, JavaScript, and Python Patterns

While cURL is perfect for exploration, production fetchers need robust HTTP clients, retries, and structured logging. Below are two minimal patterns you can adapt and extend.

JavaScript (Node.js) polling pattern for disruptions

import fetch from 'node-fetch';

async function fetchDisruptionsNear(port) {
const url = `https://freightpulsehq.com/api/v1/disruptions?port=${encodeURIComponent(port)}&severity=high`;
const resp = await fetch(url, {
headers: {
'X-API-Key': process.env.FP_API_KEY
}
});
const json = await resp.json();
if (!json.success) {
throw new Error(`Disruptions fetch failed: ${json.error}`);
}
return json.data.results || [];
}

async function run() {
try {
const results = await fetchDisruptionsNear('USLAX');
for (const item of results) {
// Deduplicate by item.id
// Match to open shipments by port
// Compute next best actions, then dispatch webhook
console.log('High-severity disruption candidate:', item.id, item.title);
}
} catch (err) {
console.error('Worker error:', err);
}
}

run();

Python polling pattern for port congestion

import os
import requests

def get_port_congestion(port_code: str):
url = f"https://freightpulsehq.com/api/v1/port-congestion?port={port_code}"
headers = { "X-API-Key": os.getenv("FP_API_KEY") }
r = requests.get(url, headers=headers, timeout=20)
r.raise_for_status()
data = r.json()
if not data.get("success", False):
raise RuntimeError(f"API error: {data.get('error')}")
return data["data"]["results"]

def evaluate_and_alert(results):
for r in results:
ratio = r["metrics"]["ratio_vs_baseline"]
if ratio < 0.9:
event = {
"id": f"evt_congestion_{r['locode'].replace(' ', '')}_{r['as_of']}",
"type": "PortCongestionTrend",
"occurred_at": r["as_of"] + "T00:00:00Z",
"source": "freightpulse",
"payload": {
"port": r["port"],
"locode": r["locode"],
"as_of": r["as_of"],
"congestion": r["congestion"],
"metrics": r["metrics"],
"policy": {
"threshold_ratio": 0.9,
"recommended_buffer_days": 2
}
}
}
# POST event to your webhook bus here
print("Dispatching webhook:", event["id"])

if __name__ == "__main__":
res = get_port_congestion("USLAX")
evaluate_and_alert(res)

Production notes: wrap I/O in retries with jitter, add a small circuit breaker to pause calls if repeated upstream errors occur, and log the upstream “as_of” or “timestamp” with each event so you can align alerts to source freshness.

Error Handling, Observability, and Governance

FreightPulse uses a uniform error envelope with success=false and a human-readable error message. Your workers should branch on “success” before reading data. Additionally, HTTP status codes communicate authentication or parameter issues. Here’s how to structure resilient handling and governance controls:

Below is a synthetic example of an upstream error you should expect and handle:

{
"success": false,
"error": "Missing required parameter: q"
}

Your worker should convert this into an internal HealthCheckFailed event for visibility in your on-call dashboards, not an operator-facing alert. Keep operator noise exceedingly low; exception workflows earn trust when they are precise and rare.

Real-World Use Cases

Here are concrete projects teams ship with FreightPulse data powering event-driven exception handling:

Complete Endpoint Coverage and Practical Notes

For reference, here’s a concise mapping from FreightPulse endpoints to exception-driven value:

Tips for each:

Putting It Together: A Minimal Exception Engine

Below is a minimal pseudo-implementation that ties together polling, decisioning, and webhook dispatch. Adapt to your stack and productionize by adding metrics, queues, and robust retries.

// Pseudo-code sketch
schedule('0 */1 * * *', scanDisruptionsNearPorts); // hourly
schedule('0 6 * * 1', updateFuelAndSurcharge); // weekly, Mon 06:00
schedule('0 7 * * 1', refreshPortCongestionTrends); // weekly, Mon 07:00
schedule('0 2 * * *', reverifyCoreCarriers); // nightly 02:00

async function scanDisruptionsNearPorts() {
for (const port of WATCHED_PORTS) {
const resp = await fp('/disruptions', { port, severity: 'high' });
for (const d of resp.results) {
if (!seen(d.id)) {
const impacted = correlateShipments(port);
const event = buildDisruptionEvent(d, port, impacted);
await dispatch(event);
remember(d.id);
}
}
}
}

async function updateFuelAndSurcharge() {
const fpResp = await fp('/fuel-prices');
const current = fpResp.data.data.diesel.national_average;
const previous = await getLastApplied();
if (breach(previous, current, 0.02)) {
const recommended = computeSurcharge(previous, current);
await dispatch(buildFuelBreachEvent(fpResp.data, recommended));
await setLastApplied(current);
}
}

async function refreshPortCongestionTrends() {
for (const port of KEY_PORTS) {
const res = await fp('/port-congestion', { port });
for (const r of res.results) {
if (!processed(r.locode, r.as_of) && r.metrics.ratio_vs_baseline < 0.9) {
const shipments = correlateInbound(r.locode);
await dispatch(buildPortTrendEvent(r, shipments));
markProcessed(r.locode, r.as_of);
}
}
}
}

async function reverifyCoreCarriers() {
for (const dot of CORE_CARRIERS) {
const res = await fp('/carriers', { q: dot });
const carrier = res.results[0];
const prev = await getCarrierBaseline(dot);
if (prev.safety_rating !== carrier.safety_rating || prev.status !== carrier.status) {
await dispatch(buildCarrierRiskEvent(carrier, prev));
await setCarrierBaseline(dot, carrier);
}
}
}

This pattern scales because every event derives from deterministic inputs: upstream timestamps/IDs, your stored baselines, and threshold policies. With thorough logging, you can always explain why an alert fired and replay it if needed.

FAQ

How often do FreightPulse fuel prices update?

Fuel data in FreightPulse reflects EIA’s cadence. You can safely schedule a weekly pull and rely on change_week and change_percent to drive surcharge or budget variance workflows. For nuanced analysis, pair with /historical to compute rolling averages that reduce noise.

Can I look up ocean carriers as well as trucking carriers?

Yes. The /carriers endpoint supports trucking (FMCSA SAFER) and ocean NVOCC/OTI lookups (FMC directory) via the type parameter. Air currently returns available:false, which you can treat as a signal to skip or downgrade your checks for air carriers.

Does the port congestion data reflect real-time vessel wait times?

No. FreightPulse’s port congestion metric is a lagged approximation based on a 7-day average of portcalls versus a 90-day baseline, with a typical 6–7 day data lag. It’s excellent for trend alerts and buffer policies, but not for minute-by-minute decisions.

How should I deduplicate disruption alerts?

Use the disruption id as a stable key and maintain a small cache of processed IDs. When you emit webhooks, include a deterministic event.id so downstream systems can ignore duplicates and ensure idempotent processing.

What’s the best way to correlate disruptions or congestion to shipments?

Join on planned port (by LOCODE) and milestone (e.g., “approaching_port” within N days), or use the discharge/loading port on the shipment’s leg. For trucking, map disruption country/region or weather advisories to last-mile ZIP clusters and compute proximity drives.

Conclusion + CTA

Exception moments define your logistics operation’s reputation. The difference between a frustrated consignee and a delighted one often comes down to minutes—the time it takes to detect a risk and route it to the right owner with a concrete next step. By consolidating fuel, rates, port activity, carrier verification, and natural-disruption alerts into one API, FreightPulse makes it straightforward to build a webhook-driven exception engine that’s fast, explainable, and trusted by operators.

Whether you are a freight forwarder building a congestion-aware ETA dashboard, a 3PL automating carrier vetting and re-quote flows, or a supply chain SaaS team delivering proactive incident bots, the patterns in this post will get you into production quickly. Start by wiring the core endpoints, add a small decision layer for thresholds and playbooks, and let your webhook dispatcher push clean, contextual alerts across your stack.

Explore the API and see how quickly you can turn real logistics signals into action. Visit FreightPulse to learn more, and Try FreightPulse for free to start building your event-driven exception workflows today.