Push the change, not the schedule.
A travel app already knows the user’s flights, usually because somebody forwarded a booking email. What it has to earn is the moment the gate moves: a notification that arrives before the user walks to the wrong pier, and does not arrive for every two-minute drift in the estimate.
This page is both halves: turning what the email said into a flight you can trust, then one subscription per distinct flight and a few lines of routing to every user on it.
The two notifications that lose users
The one that arrives late
The gate changed at 13:02. The poller runs every ten minutes. The push lands at 13:11, after the user read it off the board and after the app lost its reason to exist.
The one that never stops
The estimate moved from 14:05 to 14:07 to 14:04. Three pushes in eleven minutes. The user turns notifications off, and now the cancellation at 14:30 is silent too.
The first is a polling problem; the second is a filtering problem. A subscription with typed events and a delay threshold solves both at once, and the polling schedule tightens to three minutes at the gate on its own.
From a forwarded booking to a watched flight
An itinerary starts as an email. Four fields from it are the identity of the flight; everything else is a display value that FlightByte will replace.
| Step | What you do | What comes back |
|---|---|---|
| Parse | Keep carrier, flight number, local departure date and origin. Keep the destination too. | The identity of the flight. Times and terminals from the email are display values only. |
| Confirm | Look the flight up with the origin as the leg selector, if it departs within the live window. | Terminals, gate when assigned, aircraft and tail, operating airline, every codeshare number, local times. Further out: 404, keep the email's values. |
| Merge | Check the number against the aliases of flights you already watch, then store this leg's codeshares as aliases. | Two users on one aircraft under two numbers become one watched flight. |
| Watch | Seven days before departure, subscribe once per distinct flight with your own id in metadata. | active, or pending until the airline publishes; subscription.activated then carries the full snapshot. |
The date needs no conversion: FlightByte addresses a flight by the local departure date at the origin, which is the date printed on the confirmation. The origin is your leg selector, so a number that flies twice a day is never a guess. The full pipeline, with the alias table and the daily job, is in From a booking email to a watched flight.
How to wire it
Five steps. The receiver is thirty lines; the rest is a table you already have.
Subscribe per distinct flight, not per user
When a flight comes within seven days of departure, subscribe to it once, keyed by its flightKey. Fifty users on the same flight are one subscription; your own table says who they are. Put your own key in metadata so the webhook tells you which row to fan out from, and use one shared secret so the receiver verifies everything against one setting.
POST /v1/subscriptions
x-api-key: $FLIGHTBYTE_KEY
idempotency-key: trips:TK1848:2026-09-12:ATH
{
"carrier": "TK",
"flightNumber": "1848",
"date": "2026-09-12",
"origin": "ATH",
"webhookUrl": "https://api.yourapp.com/hooks/flights",
"secret": "$FLIGHTBYTE_WEBHOOK_SECRET",
"delayThresholdMinutes": 10,
"events": [
"departure.gate.changed", "departure.terminal.changed",
"delay.increased", "delay.decreased", "flight.cancelled",
"flight.departed", "arrival.estimated.changed", "arrival.baggage.changed"
],
"metadata": { "flightRef": "flt_8c1e" }
}Verify, deduplicate, answer, then work
Check the signature against the raw body, drop anything older than five minutes, look the event id up in a processed table, answer 204, and hand the event to a queue. Sending a thousand pushes from inside the request handler is how a slow APNs afternoon turns into retries. The signature guide has the receiver in seven frameworks.
Turn each change into copy
A flight.updated delivery carries the changes and the full snapshot. Map each change type to a sentence, read the times from the snapshot in the airport’s local zone, and send one push per delivery even when it carries three changes: “Gate B14, now departing 14:40” is one notification, not two.
Fan out from your table
The webhook names the flight; your table names the users. Send to every device on that flight, in the language and zone each user chose, and mark the trip with the new gate so the app shows it on the next open too. A user who added the flight after the subscription existed joins the table, not the subscription.
Let it end
After the aircraft is at the gate and the belt is published, FlightByte sends subscription.completed. Archive the flight, keep the trip. A user who deletes a flight leaves your table; the subscription is deleted only when the table for that flight is empty.
What to push, and the copy
Eight of the 23 change types. The rest are noise to a passenger: aircraft swaps, codeshare lists, runway times.
| Event | Push copy | Note |
|---|---|---|
departure.gate.changed | Gate changed to B14 | The one users install the app for. Always send. |
departure.terminal.changed | Terminal changed to 2 | Rare, and it changes where the taxi goes. |
delay.increased | Now departing 14:40, 35 min late | Threshold at 10 to 15 minutes; below that the airline will move it back. |
delay.decreased | Back on time: departing 14:05 | Good news is a notification too, and early aircraft strand people at the gate. |
flight.cancelled | TK1848 is cancelled | Send, and open the rebooking screen. |
flight.departed | TK1848 has departed | For the people at home, and for the Live Activity. |
arrival.estimated.changed | Landing 16:52, 20 min late | Only past a threshold; the estimate drifts by a minute all afternoon. |
arrival.baggage.changed | Bags on belt 7 | The last message of the trip, and the most appreciated. |
What it costs an app
A watched flight is a distinct flight in your users’ upcoming trips, not a user. The plan follows that number.
An indie app or a beta. Nine cents a flight, shared by everyone on it.
A few thousand active users on typical routes. Under four cents a flight.
Tens of thousands of users; extra flights at three cents each rather than a wall.
| Distinct flights a month | Plan | Monthly | Per flight |
|---|---|---|---|
| 100 | Hobby | $9 | 9 cents |
| 500 | Starter | $19 | under 4 cents |
| 2,000 | Growth | $49 | under 2.5 cents |
| 5,000 | Scale | $99 | about 2 cents |
| 10,000 | Scale plus 5,000 extra | $199 | about 2 cents |
| More | A conversation | Custom | Quotas shaped to your traffic |
The count is distinct aircraft, after codeshares are merged, in the month they are subscribed. Followers are free: the fiftieth user on a flight costs nothing, so the cost per user falls as an app grows. Past a plan’s limit, extra flights are billed rather than refused, and nothing already being watched is dropped. Full plans on the pricing page; lookups for import and the trip screen are cached and included.
What it does not do
An itinerary app is assembled from several sources. These are the parts to get elsewhere.
| You may want | FlightByte | Note |
|---|---|---|
| Status, times, gates, terminals, baggage belt | Yes | Scheduled, estimated and actual, in UTC and airport-local form, with typed change events. |
| Aircraft type and tail, operating airline, codeshares | Yes | In every snapshot; aircraft.changed and codeshares.changed when they move. |
| The airline's own history for the flight | Yes | The timeline, newest first, in the snapshot and on its own endpoint. |
| Live aircraft position and a map track | No | Status and times, not where the aircraft is. Use an ADS-B source for the map. |
| On-time history and route reliability scores | No | Live window only: a few days back, a couple ahead. Keep your own history from the webhooks you receive. |
| Minimum connection times | No | You get both flights kept current; the threshold per airport is yours. |
| Fares, seat maps, check-in links | No | Not flight status. |
Guides for app teams
The three pieces of an itinerary integration, each written out in full.
Questions app teams ask
Can we confirm a flight the moment a booking is forwarded?
404 because no operational record exists yet. Keep what the email said, subscribe when the flight comes within seven days, and let subscription.activated confirm it with the full snapshot.Two users hold different flight numbers for the same aircraft. Is that two watched flights?
codeshares, so keep an alias table of the numbers a watched flight answers to and check a new booking against it before subscribing. Either number resolves to the same aircraft, and one subscription covers all of them.Can we warn a user about a missed connection?
arrival.bestTimeUtc from the outbound departure.bestTimeUtc on every delivery, and compare with a threshold per airport. FlightByte does not publish minimum connection times; the guide linked below has the state machine.Does FlightByte send the push notification?
Fifty users have the same flight in their trips. Is that fifty watched flights?
flightKey, create one subscription per key, and send fifty pushes from one webhook. You pay for the flights your users fly, not for the number of users.Users add flights months ahead. Subscriptions only go seven days out.
What about the user's time zone?
utc, local and the airport's timezone. Show the local form for anything at the airport, the device zone for reminders; never convert local again.Can I drive an iOS Live Activity from this?
flight.departed starts the flight state, arrival.estimated.changed updates the landing time, arrival.baggage.changed ends it with the belt. Update the activity from the same webhook that sends the push.Other uses
Ship gate-change pushes this sprint.
A key, one subscription per flight, and a receiver you can copy. The fan-out is your table.
A free key in a minute, no card. On a paid plan? Request a key