Build a Flight Delay Notification System (Email, SMS, Webhook)
A step-by-step playbook for a delay-alert system your users actually trust: point a webhook at your app, create a monitor per flight, then fan out to email, SMS and Slack — with a debounce so you never spam anyone.
"Your flight is delayed" is one of the most valuable messages a product can send — if it arrives early, once, and only when it's true. Build it badly and you either miss the delay or bury the user in noise every time an estimate wobbles by a minute. This playbook walks through a delay-notification system built entirely on the consumer side of FlightNerve: you create monitors, receive webhooks, and fan out to email, SMS and Slack. No polling loop, and no change-detection code to maintain.
The shape of the system
Four moving parts, and FlightNerve is the one you don't have to build:
- A webhook endpoint on your side that receives alerts.
- A monitor per flight your users care about.
- A dispatch layer that turns an alert into email / SMS / Slack.
- A mapping from each alert back to the user or booking it belongs to.
Step 1 — Point a webhook at your app
Set one endpoint URL for your account. FlightNerve POSTs a JSON alert to it whenever a monitored flight changes; reply with any 2xx to acknowledge.
curl -X POST "https://api.flightnerve.com/webhook/YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://your-app.example/hooks/flightnerve"}'
You can fire a test alert at the same URL before a single flight is monitored, so you confirm your receiver works up front:
curl -X POST "https://api.flightnerve.com/webhook/YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"test": true, "url": "https://your-app.example/hooks/flightnerve"}'
→ { "success": true, "delivered": true, "code": 200 }
Step 2 — Create a monitor per flight
When a user adds a flight to track, create a monitor for it. Store the monitoring_id you get back against that user or booking — it is how you will route the alert later.
curl -X POST "https://api.flightnerve.com/monitor/YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "flight": "EK72", "date": "20260802", "monitor": { "sensitivity_min": 15 } }'
→ { "success": true, "monitoring_id": "mon_fbba35cd573ca081" }
Step 3 — Receive the alert and route it
A delay alert arrives as a compact delta — what changed, by how much, and the new local time — with the monitoring_id so you know whose flight it is:
{
"event": "flightnerve.alert",
"monitoring_id": "mon_fbba35cd573ca081",
"flight": "EK72",
"route": "DXB-MUC",
"changes": [
{ "field": "arrival_estimate", "type": "delayed",
"from": "2026-08-02T14:00:00Z", "to": "2026-08-02T14:25:00Z",
"delta_min": 25, "to_local": "16:25" }
]
}
Your handler looks up the user by monitoring_id and fans out. The alert is already diffed, so there is nothing to compute:
# pseudo-code for your webhook handler
def on_flightnerve_alert(payload):
user = lookup_user(payload["monitoring_id"])
for c in payload["changes"]:
if c["field"] == "arrival_estimate" and c["type"] == "delayed":
msg = f'{payload["flight"]} now arriving {c["to_local"]} (+{c["delta_min"]} min)'
send_email(user.email, msg)
send_sms(user.phone, msg)
post_slack(user.slack, msg)
Step 4 — Keep it from becoming noise
The fastest way to get your alerts muted is to send too many. Two habits prevent it:
- Use the sensitivity threshold.
sensitivity_min(default 15) means a time alert only re-fires when the estimate moves that many minutes past the value you were last told — so a two-minute wobble never reaches the user. Set it higher for casual travellers, lower for time-critical operations. - Pick the events that matter. Mute the ones your users don't care about (an early departure, a gate change) and keep the ones they do (arrival delay, cancellation, landing), so every message you send is worth reading.
Step 5 — Match the channel to the moment
- Email for the full picture — new time, gate, what to do next.
- SMS for the one line that changes a plan: "EK72 now 16:25, +25 min."
- Slack / push for an ops channel that needs the whole team to see it at once.
Because every alert carries the monitoring_id, the same pipeline serves a consumer app (one traveller) and an operations desk (hundreds of flights) without changing the routing logic.
Frequently asked questions
Do I need a polling job anywhere in this?
No. You create monitors and receive webhooks. There is no timer and no status-diffing code in the system.
How do I stop alerting users over tiny estimate changes?
Set sensitivity_min when you create the monitor. Time alerts only re-fire once the estimate moves past that threshold from the last value you were told.
How do I know which user an alert belongs to?
Store the monitoring_id returned at creation against the user or booking; every alert for that flight includes it, so routing is a lookup.
What if my endpoint is down when an alert fires?
Alerts are stored with their delivery result, so you can reconcile misses, and you can re-test your endpoint at any time to confirm it is receiving.
Can the same system handle cancellations and gate changes?
Yes. A monitor covers the whole journey — delays, gate and terminal changes, diversions, cancellation and landing — so you choose which of them become notifications.
A trustworthy delay alert is a small system: one webhook, one monitor per flight, a lookup, and a fan-out — with a sensitivity threshold keeping it quiet until it matters. FlightNerve handles the watching and the diffing; you handle the message. Point a webhook at your app and send the alert your users actually want.
