flightbyte 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flightbyte/__init__.py +136 -0
- flightbyte/_client.py +157 -0
- flightbyte/_countries.py +57 -0
- flightbyte/_errors.py +311 -0
- flightbyte/_flights.py +84 -0
- flightbyte/_models.py +644 -0
- flightbyte/_rate_limit.py +75 -0
- flightbyte/_subscriptions.py +317 -0
- flightbyte/_transport.py +429 -0
- flightbyte/_util.py +28 -0
- flightbyte/_version.py +3 -0
- flightbyte/_webhook.py +233 -0
- flightbyte/py.typed +0 -0
- flightbyte-1.0.0.dist-info/METADATA +423 -0
- flightbyte-1.0.0.dist-info/RECORD +17 -0
- flightbyte-1.0.0.dist-info/WHEEL +4 -0
- flightbyte-1.0.0.dist-info/licenses/LICENSE +21 -0
flightbyte/_models.py
ADDED
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
"""The shapes the FlightByte API answers with.
|
|
2
|
+
|
|
3
|
+
Responses are returned exactly as the API sent them, as plain ``dict`` objects:
|
|
4
|
+
keys keep the API's own camelCase names, timestamps stay ISO 8601 strings, and
|
|
5
|
+
nothing is renamed or dropped. These ``TypedDict`` classes describe them, so a
|
|
6
|
+
type checker and your editor know every key. Type names follow the API's
|
|
7
|
+
OpenAPI document.
|
|
8
|
+
|
|
9
|
+
Keys the API may leave out are not required, and values it may send as
|
|
10
|
+
``null`` include ``None``, so ``delayMinutes == 0`` and "no delay reading" stay
|
|
11
|
+
apart. Read a key that may be missing with ``.get()``.
|
|
12
|
+
|
|
13
|
+
String enums (phases, statuses, change and event types) are typed as ``str``:
|
|
14
|
+
a value the API adds after this release is still a valid string, so nothing
|
|
15
|
+
breaks. The values this version knows are listed in ``FLIGHT_PHASES``,
|
|
16
|
+
``SUBSCRIPTION_STATUSES``, ``FLIGHT_CHANGE_TYPES``, ``WEBHOOK_EVENT_TYPES`` and
|
|
17
|
+
``DELIVERY_STATUSES``.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Each model with keys the API may leave out is split in two: a base with the
|
|
21
|
+
# keys that are always there, and the public class with ``total=False`` for
|
|
22
|
+
# the rest. ``typing.NotRequired`` would read better but needs Python 3.11,
|
|
23
|
+
# and this package supports 3.10 without depending on typing_extensions.
|
|
24
|
+
|
|
25
|
+
from typing import Final, TypeAlias, TypedDict
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"DELIVERY_STATUSES",
|
|
29
|
+
"FLIGHT_CHANGE_TYPES",
|
|
30
|
+
"FLIGHT_PHASES",
|
|
31
|
+
"SUBSCRIPTION_STATUSES",
|
|
32
|
+
"WEBHOOK_EVENT_TYPES",
|
|
33
|
+
"AirportRef",
|
|
34
|
+
"AmbiguousLeg",
|
|
35
|
+
"CarrierRef",
|
|
36
|
+
"CodeshareRef",
|
|
37
|
+
"Country",
|
|
38
|
+
"CountryRef",
|
|
39
|
+
"CreatedSubscription",
|
|
40
|
+
"DeliveryRecord",
|
|
41
|
+
"DeliveryStatus",
|
|
42
|
+
"DurationInfo",
|
|
43
|
+
"EndpointTimes",
|
|
44
|
+
"EquipmentInfo",
|
|
45
|
+
"FlightChange",
|
|
46
|
+
"FlightChangeType",
|
|
47
|
+
"FlightEndpoint",
|
|
48
|
+
"FlightLegRef",
|
|
49
|
+
"FlightPhase",
|
|
50
|
+
"FlightSnapshot",
|
|
51
|
+
"FlightStatusInfo",
|
|
52
|
+
"FlightTime",
|
|
53
|
+
"FlightTimeline",
|
|
54
|
+
"Subscription",
|
|
55
|
+
"SubscriptionPage",
|
|
56
|
+
"SubscriptionStatus",
|
|
57
|
+
"TimelineEntry",
|
|
58
|
+
"WebhookEvent",
|
|
59
|
+
"WebhookEventType",
|
|
60
|
+
"best_gate_time",
|
|
61
|
+
"is_terminal_phase",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
FlightPhase: TypeAlias = str
|
|
65
|
+
"""The coarse lifecycle bucket of a flight; one of ``FLIGHT_PHASES`` or a newer value."""
|
|
66
|
+
|
|
67
|
+
SubscriptionStatus: TypeAlias = str
|
|
68
|
+
"""The lifecycle of a subscription; one of ``SUBSCRIPTION_STATUSES`` or a newer value.
|
|
69
|
+
|
|
70
|
+
``active`` polls and delivers, ``pending`` waits for the flight to be published
|
|
71
|
+
upstream, ``paused`` was paused by you, ``completed`` finished after the flight
|
|
72
|
+
did, ``suspended`` was disabled by the API, for example after repeated delivery
|
|
73
|
+
failures.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
FlightChangeType: TypeAlias = str
|
|
77
|
+
"""The kind of change a ``flight.updated`` webhook reports, and what a
|
|
78
|
+
subscription's ``events`` filter is made of; one of ``FLIGHT_CHANGE_TYPES`` or
|
|
79
|
+
a newer value."""
|
|
80
|
+
|
|
81
|
+
WebhookEventType: TypeAlias = str
|
|
82
|
+
"""The type of a webhook event; one of ``WEBHOOK_EVENT_TYPES`` or a newer value."""
|
|
83
|
+
|
|
84
|
+
DeliveryStatus: TypeAlias = str
|
|
85
|
+
"""The outcome of one webhook delivery attempt; one of ``DELIVERY_STATUSES`` or a newer value."""
|
|
86
|
+
|
|
87
|
+
FLIGHT_PHASES: Final[tuple[str, ...]] = (
|
|
88
|
+
"scheduled",
|
|
89
|
+
"boarding",
|
|
90
|
+
"departed",
|
|
91
|
+
"en_route",
|
|
92
|
+
"landed",
|
|
93
|
+
"arrived",
|
|
94
|
+
"cancelled",
|
|
95
|
+
"diverted",
|
|
96
|
+
"redirected",
|
|
97
|
+
"unknown",
|
|
98
|
+
)
|
|
99
|
+
"""Every flight phase this version knows, in the API's order."""
|
|
100
|
+
|
|
101
|
+
SUBSCRIPTION_STATUSES: Final[tuple[str, ...]] = (
|
|
102
|
+
"active",
|
|
103
|
+
"pending",
|
|
104
|
+
"paused",
|
|
105
|
+
"completed",
|
|
106
|
+
"suspended",
|
|
107
|
+
)
|
|
108
|
+
"""Every subscription status this version knows."""
|
|
109
|
+
|
|
110
|
+
FLIGHT_CHANGE_TYPES: Final[tuple[str, ...]] = (
|
|
111
|
+
"status.changed",
|
|
112
|
+
"flight.departed",
|
|
113
|
+
"flight.arrived",
|
|
114
|
+
"flight.cancelled",
|
|
115
|
+
"flight.diverted",
|
|
116
|
+
"flight.reinstated",
|
|
117
|
+
"departure.gate.changed",
|
|
118
|
+
"departure.terminal.changed",
|
|
119
|
+
"arrival.gate.changed",
|
|
120
|
+
"arrival.terminal.changed",
|
|
121
|
+
"arrival.baggage.changed",
|
|
122
|
+
"departure.scheduled.changed",
|
|
123
|
+
"departure.estimated.changed",
|
|
124
|
+
"departure.actual.changed",
|
|
125
|
+
"arrival.scheduled.changed",
|
|
126
|
+
"arrival.estimated.changed",
|
|
127
|
+
"arrival.actual.changed",
|
|
128
|
+
"delay.increased",
|
|
129
|
+
"delay.decreased",
|
|
130
|
+
"aircraft.changed",
|
|
131
|
+
"aircraft.tail.changed",
|
|
132
|
+
"route.changed",
|
|
133
|
+
"codeshares.changed",
|
|
134
|
+
)
|
|
135
|
+
"""Every flight change type this version knows, in the API's order."""
|
|
136
|
+
|
|
137
|
+
WEBHOOK_EVENT_TYPES: Final[tuple[str, ...]] = (
|
|
138
|
+
"subscription.created",
|
|
139
|
+
"subscription.activated",
|
|
140
|
+
"subscription.test",
|
|
141
|
+
"subscription.completed",
|
|
142
|
+
"subscription.suspended",
|
|
143
|
+
"flight.updated",
|
|
144
|
+
)
|
|
145
|
+
"""Every webhook event type this version knows."""
|
|
146
|
+
|
|
147
|
+
DELIVERY_STATUSES: Final[tuple[str, ...]] = ("delivered", "failed")
|
|
148
|
+
"""Every delivery status this version knows."""
|
|
149
|
+
|
|
150
|
+
_TERMINAL_PHASES: Final = frozenset({"arrived", "cancelled", "diverted", "redirected"})
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def is_terminal_phase(phase: FlightPhase) -> bool:
|
|
154
|
+
"""Whether the flight is finished: arrived, cancelled, diverted or redirected."""
|
|
155
|
+
return phase in _TERMINAL_PHASES
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class CountryRef(TypedDict):
|
|
159
|
+
"""A country as it appears inside an airport."""
|
|
160
|
+
|
|
161
|
+
code: str
|
|
162
|
+
"""ISO 3166-1 alpha-2 code, for example ``TR``."""
|
|
163
|
+
name: str
|
|
164
|
+
"""Country name."""
|
|
165
|
+
emoji: str
|
|
166
|
+
"""Flag emoji."""
|
|
167
|
+
flagSvg: str
|
|
168
|
+
"""Absolute URL of the 4:3 SVG flag."""
|
|
169
|
+
flagSvgSquare: str
|
|
170
|
+
"""Absolute URL of the 1:1 SVG flag."""
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class Country(CountryRef):
|
|
174
|
+
"""A country from the ``/countries`` endpoints."""
|
|
175
|
+
|
|
176
|
+
alpha2: str
|
|
177
|
+
"""ISO 3166-1 alpha-2 code."""
|
|
178
|
+
hasFlag: bool
|
|
179
|
+
"""Whether a flag image exists for the country."""
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class _AirportRefRequired(TypedDict):
|
|
183
|
+
iata: str
|
|
184
|
+
"""IATA code, for example ``IST``."""
|
|
185
|
+
name: str
|
|
186
|
+
"""Airport name."""
|
|
187
|
+
country: CountryRef
|
|
188
|
+
"""Country."""
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class AirportRef(_AirportRefRequired, total=False):
|
|
192
|
+
"""An airport."""
|
|
193
|
+
|
|
194
|
+
icao: str | None
|
|
195
|
+
"""ICAO code, for example ``LTFM``."""
|
|
196
|
+
code: str | None
|
|
197
|
+
"""The upstream's own airport code; it may differ from ``iata``."""
|
|
198
|
+
city: str | None
|
|
199
|
+
"""City."""
|
|
200
|
+
state: str | None
|
|
201
|
+
"""State or region."""
|
|
202
|
+
timezone: str | None
|
|
203
|
+
"""IANA time zone, for example ``Europe/Istanbul``."""
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class _FlightTimeRequired(TypedDict):
|
|
207
|
+
utc: str
|
|
208
|
+
"""The instant, ISO 8601 in UTC."""
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class FlightTime(_FlightTimeRequired, total=False):
|
|
212
|
+
"""A point in time, in UTC and in the airport's local time."""
|
|
213
|
+
|
|
214
|
+
local: str | None
|
|
215
|
+
"""Airport-local wall time without an offset, for example ``2026-09-08T12:10:00.000``.
|
|
216
|
+
|
|
217
|
+
Pair it with the airport's time zone.
|
|
218
|
+
"""
|
|
219
|
+
timezone: str | None
|
|
220
|
+
"""Time zone abbreviation, for example ``EEST``."""
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class EndpointTimes(TypedDict, total=False):
|
|
224
|
+
"""The gate and runway times of a departure or an arrival."""
|
|
225
|
+
|
|
226
|
+
scheduledGate: FlightTime | None
|
|
227
|
+
"""Scheduled gate time."""
|
|
228
|
+
estimatedGate: FlightTime | None
|
|
229
|
+
"""Estimated gate time."""
|
|
230
|
+
actualGate: FlightTime | None
|
|
231
|
+
"""Actual gate time."""
|
|
232
|
+
scheduledRunway: FlightTime | None
|
|
233
|
+
"""Scheduled runway time."""
|
|
234
|
+
estimatedRunway: FlightTime | None
|
|
235
|
+
"""Estimated runway time."""
|
|
236
|
+
actualRunway: FlightTime | None
|
|
237
|
+
"""Actual runway time."""
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def best_gate_time(times: EndpointTimes) -> FlightTime | None:
|
|
241
|
+
"""The best known gate time: actual, else estimated, else scheduled."""
|
|
242
|
+
for value in (times.get("actualGate"), times.get("estimatedGate"), times.get("scheduledGate")):
|
|
243
|
+
if value is not None:
|
|
244
|
+
return value
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class _FlightEndpointRequired(TypedDict):
|
|
249
|
+
airport: AirportRef
|
|
250
|
+
"""The airport."""
|
|
251
|
+
times: EndpointTimes
|
|
252
|
+
"""Gate and runway times."""
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class FlightEndpoint(_FlightEndpointRequired, total=False):
|
|
256
|
+
"""The departure or arrival side of a flight."""
|
|
257
|
+
|
|
258
|
+
terminal: str | None
|
|
259
|
+
"""Terminal."""
|
|
260
|
+
gate: str | None
|
|
261
|
+
"""Gate."""
|
|
262
|
+
baggage: str | None
|
|
263
|
+
"""Baggage carousel, on arrival."""
|
|
264
|
+
bestTimeUtc: str | None
|
|
265
|
+
"""Best known time in UTC: actual, else estimated, else scheduled."""
|
|
266
|
+
delayMinutes: int | None
|
|
267
|
+
"""Gate delay in minutes; negative when early."""
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class _CarrierRefRequired(TypedDict):
|
|
271
|
+
iata: str
|
|
272
|
+
"""IATA code, for example ``TK``."""
|
|
273
|
+
name: str
|
|
274
|
+
"""Airline name."""
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class CarrierRef(_CarrierRefRequired, total=False):
|
|
278
|
+
"""The marketing carrier."""
|
|
279
|
+
|
|
280
|
+
icao: str | None
|
|
281
|
+
"""ICAO code, for example ``THY``."""
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class CodeshareRef(TypedDict):
|
|
285
|
+
"""A codeshare flight number sold on the same flight."""
|
|
286
|
+
|
|
287
|
+
carrier: str
|
|
288
|
+
"""Carrier code."""
|
|
289
|
+
name: str
|
|
290
|
+
"""Carrier name."""
|
|
291
|
+
flightNumber: str
|
|
292
|
+
"""Flight number."""
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class _FlightStatusInfoRequired(TypedDict):
|
|
296
|
+
code: str
|
|
297
|
+
"""Upstream status code: ``S``, ``A``, ``L``, ``C``, ``D``, ``R``, ``U`` or ``NO``."""
|
|
298
|
+
label: str
|
|
299
|
+
"""Human-readable status, for example ``Arrived``."""
|
|
300
|
+
phase: FlightPhase
|
|
301
|
+
"""The lifecycle phase."""
|
|
302
|
+
delayed: bool
|
|
303
|
+
"""Whether the flight is delayed."""
|
|
304
|
+
cancelled: bool
|
|
305
|
+
"""Whether the flight is cancelled."""
|
|
306
|
+
diverted: bool
|
|
307
|
+
"""Whether the flight is diverted."""
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class FlightStatusInfo(_FlightStatusInfoRequired, total=False):
|
|
311
|
+
"""The status of a flight."""
|
|
312
|
+
|
|
313
|
+
description: str | None
|
|
314
|
+
"""Longer description, for example ``On time``."""
|
|
315
|
+
color: str | None
|
|
316
|
+
"""Suggested color, for example ``green``."""
|
|
317
|
+
departureGateDelayMinutes: int | None
|
|
318
|
+
"""Departure gate delay in minutes."""
|
|
319
|
+
arrivalGateDelayMinutes: int | None
|
|
320
|
+
"""Arrival gate delay in minutes."""
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class EquipmentInfo(TypedDict, total=False):
|
|
324
|
+
"""The aircraft."""
|
|
325
|
+
|
|
326
|
+
tailNumber: str | None
|
|
327
|
+
"""Registration, for example ``TC-LTP``."""
|
|
328
|
+
iata: str | None
|
|
329
|
+
"""IATA aircraft type, for example ``32Q``."""
|
|
330
|
+
name: str | None
|
|
331
|
+
"""Aircraft name, for example ``Airbus A321neo``."""
|
|
332
|
+
source: str | None
|
|
333
|
+
"""Where the data came from, for example ``Actual``."""
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class DurationInfo(TypedDict, total=False):
|
|
337
|
+
"""Block times in minutes."""
|
|
338
|
+
|
|
339
|
+
scheduledMinutes: int | None
|
|
340
|
+
"""Scheduled duration."""
|
|
341
|
+
estimatedMinutes: int | None
|
|
342
|
+
"""Estimated duration."""
|
|
343
|
+
actualMinutes: int | None
|
|
344
|
+
"""Actual duration."""
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class _TimelineEntryRequired(TypedDict):
|
|
348
|
+
occurredAt: str
|
|
349
|
+
"""When it happened, ISO 8601."""
|
|
350
|
+
title: str
|
|
351
|
+
"""Title, for example ``Time Adjustment``."""
|
|
352
|
+
events: list[str]
|
|
353
|
+
"""What changed, as sentences."""
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class TimelineEntry(_TimelineEntryRequired, total=False):
|
|
357
|
+
"""One entry of a flight's upstream history."""
|
|
358
|
+
|
|
359
|
+
source: str | None
|
|
360
|
+
"""Source, for example ``Airline``."""
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class _FlightLegRefRequired(TypedDict):
|
|
364
|
+
departureIata: str
|
|
365
|
+
"""Origin IATA code."""
|
|
366
|
+
arrivalIata: str
|
|
367
|
+
"""Destination IATA code."""
|
|
368
|
+
selected: bool
|
|
369
|
+
"""Whether this is the leg the snapshot describes."""
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
class FlightLegRef(_FlightLegRefRequired, total=False):
|
|
373
|
+
"""A leg flown under the same flight number on the same date.
|
|
374
|
+
|
|
375
|
+
Flight numbers are not unique per day, so the API lists every leg and
|
|
376
|
+
marks the one the snapshot describes with ``selected``.
|
|
377
|
+
"""
|
|
378
|
+
|
|
379
|
+
providerFlightId: str | None
|
|
380
|
+
"""Upstream flight id; pass it as ``flight_id`` to select this leg."""
|
|
381
|
+
carrier: str | None
|
|
382
|
+
"""Operating carrier code."""
|
|
383
|
+
scheduledDepartureLocal: str | None
|
|
384
|
+
"""Scheduled local departure, ``HH:mm``."""
|
|
385
|
+
scheduledArrivalLocal: str | None
|
|
386
|
+
"""Scheduled local arrival, ``HH:mm``."""
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class _FlightSnapshotRequired(TypedDict):
|
|
390
|
+
flightKey: str
|
|
391
|
+
"""Stable key, for example ``TK1848:2026-09-08`` or ``TK1:2026-09-08@IST-JFK``."""
|
|
392
|
+
carrierCode: str
|
|
393
|
+
"""Carrier code from the request, for example ``TK``."""
|
|
394
|
+
flightNumber: str
|
|
395
|
+
"""Flight number without leading zeros, for example ``1848``."""
|
|
396
|
+
date: str
|
|
397
|
+
"""Local departure date at the origin, ``YYYY-MM-DD``."""
|
|
398
|
+
fetchedAt: str
|
|
399
|
+
"""When the API fetched this data from upstream, ISO 8601."""
|
|
400
|
+
carrier: CarrierRef
|
|
401
|
+
"""Marketing carrier."""
|
|
402
|
+
codeshares: list[CodeshareRef]
|
|
403
|
+
"""Codeshare flight numbers."""
|
|
404
|
+
departure: FlightEndpoint
|
|
405
|
+
"""Departure side."""
|
|
406
|
+
arrival: FlightEndpoint
|
|
407
|
+
"""Arrival side."""
|
|
408
|
+
status: FlightStatusInfo
|
|
409
|
+
"""Status."""
|
|
410
|
+
equipment: EquipmentInfo
|
|
411
|
+
"""Aircraft."""
|
|
412
|
+
duration: DurationInfo
|
|
413
|
+
"""Block times."""
|
|
414
|
+
timeline: list[TimelineEntry]
|
|
415
|
+
"""Upstream history, newest first."""
|
|
416
|
+
alternates: list[FlightLegRef]
|
|
417
|
+
"""Every leg flown under this flight number on this date."""
|
|
418
|
+
fingerprint: str
|
|
419
|
+
"""Hash over the change-relevant fields."""
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class FlightSnapshot(_FlightSnapshotRequired, total=False):
|
|
423
|
+
"""Everything known about one flight leg at one moment."""
|
|
424
|
+
|
|
425
|
+
providerFlightId: str | None
|
|
426
|
+
"""Upstream flight id."""
|
|
427
|
+
operatedBy: str | None
|
|
428
|
+
"""Operating carrier name, when different."""
|
|
429
|
+
diverted: FlightEndpoint | None
|
|
430
|
+
"""Where the flight actually went, when diverted."""
|
|
431
|
+
cached: bool
|
|
432
|
+
"""Whether a lookup was answered from the API's cache.
|
|
433
|
+
|
|
434
|
+
Sent with lookups, not inside webhook events.
|
|
435
|
+
"""
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
class FlightTimeline(TypedDict):
|
|
439
|
+
"""The response of ``flights.timeline``."""
|
|
440
|
+
|
|
441
|
+
flightKey: str
|
|
442
|
+
"""The flight's key."""
|
|
443
|
+
count: int
|
|
444
|
+
"""The number of entries."""
|
|
445
|
+
data: list[TimelineEntry]
|
|
446
|
+
"""History entries, newest first."""
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
class _SubscriptionRequired(TypedDict):
|
|
450
|
+
id: str
|
|
451
|
+
"""Id, for example ``sub_7Kd91mQx0Zb2c3d4e5f6``."""
|
|
452
|
+
status: SubscriptionStatus
|
|
453
|
+
"""Lifecycle status."""
|
|
454
|
+
flightKey: str
|
|
455
|
+
"""The watched flight's key, for example ``TK1848:2026-09-08``."""
|
|
456
|
+
carrier: str
|
|
457
|
+
"""Carrier code."""
|
|
458
|
+
flightNumber: str
|
|
459
|
+
"""Flight number."""
|
|
460
|
+
date: str
|
|
461
|
+
"""Local departure date at the origin, ``YYYY-MM-DD``."""
|
|
462
|
+
webhookUrl: str
|
|
463
|
+
"""Where webhooks are posted."""
|
|
464
|
+
events: list[FlightChangeType]
|
|
465
|
+
"""The change types that trigger a webhook."""
|
|
466
|
+
delayThresholdMinutes: int
|
|
467
|
+
"""Delay swings smaller than this many minutes are ignored."""
|
|
468
|
+
createdAt: str
|
|
469
|
+
"""When it was created, ISO 8601."""
|
|
470
|
+
updatedAt: str
|
|
471
|
+
"""When it last changed, ISO 8601."""
|
|
472
|
+
deliveredCount: int
|
|
473
|
+
"""Webhooks delivered."""
|
|
474
|
+
failedCount: int
|
|
475
|
+
"""Webhook attempts that failed."""
|
|
476
|
+
flightResolved: bool
|
|
477
|
+
"""``False`` while the flight is not found upstream yet."""
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
class _SubscriptionOptional(TypedDict, total=False):
|
|
481
|
+
headers: dict[str, str] | None
|
|
482
|
+
"""Extra headers sent with every delivery."""
|
|
483
|
+
metadata: dict[str, str] | None
|
|
484
|
+
"""Your own data, echoed back in every webhook."""
|
|
485
|
+
lastPolledAt: str | None
|
|
486
|
+
"""When the flight was last polled, ISO 8601."""
|
|
487
|
+
lastChangeAt: str | None
|
|
488
|
+
"""When a change was last detected, ISO 8601."""
|
|
489
|
+
lastDeliveryAt: str | None
|
|
490
|
+
"""When a webhook was last delivered, ISO 8601."""
|
|
491
|
+
statusReason: str | None
|
|
492
|
+
"""Why the subscription was suspended or completed."""
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
class Subscription(_SubscriptionRequired, _SubscriptionOptional, total=False):
|
|
496
|
+
"""A watched flight.
|
|
497
|
+
|
|
498
|
+
The API polls it and posts a signed webhook to ``webhookUrl`` whenever
|
|
499
|
+
something about it changes.
|
|
500
|
+
"""
|
|
501
|
+
|
|
502
|
+
secretHint: str
|
|
503
|
+
"""The masked signing secret, for example ``whsec_AbCdE...wxyz``."""
|
|
504
|
+
secret: str
|
|
505
|
+
"""The full signing secret.
|
|
506
|
+
|
|
507
|
+
Only the creation response carries it; read it again with
|
|
508
|
+
``subscriptions.secret``.
|
|
509
|
+
"""
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
class _CreatedSubscriptionRequired(_SubscriptionRequired, _SubscriptionOptional):
|
|
513
|
+
secret: str
|
|
514
|
+
"""The full webhook signing secret.
|
|
515
|
+
|
|
516
|
+
Returned in full only here and from ``subscriptions.secret``; store it
|
|
517
|
+
where your webhook receiver can read it.
|
|
518
|
+
"""
|
|
519
|
+
secretHint: str
|
|
520
|
+
"""The masked signing secret."""
|
|
521
|
+
reused: bool
|
|
522
|
+
"""``True`` when an earlier request with the same idempotency key was replayed.
|
|
523
|
+
|
|
524
|
+
Within 24 hours, the original subscription is returned and nothing new is
|
|
525
|
+
created or billed.
|
|
526
|
+
"""
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
class CreatedSubscription(_CreatedSubscriptionRequired, total=False):
|
|
530
|
+
"""The response of ``subscriptions.create``.
|
|
531
|
+
|
|
532
|
+
It has every key of ``Subscription``, with ``secret`` and ``secretHint``
|
|
533
|
+
always present.
|
|
534
|
+
"""
|
|
535
|
+
|
|
536
|
+
flight: FlightSnapshot | None
|
|
537
|
+
"""The flight as first seen, when it was found. Absent when ``reused``."""
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
class SubscriptionPage(TypedDict):
|
|
541
|
+
"""One page of ``subscriptions.list``."""
|
|
542
|
+
|
|
543
|
+
total: int
|
|
544
|
+
"""The number of subscriptions matching the filters."""
|
|
545
|
+
limit: int
|
|
546
|
+
"""The page size used."""
|
|
547
|
+
offset: int
|
|
548
|
+
"""The number of subscriptions skipped."""
|
|
549
|
+
data: list[Subscription]
|
|
550
|
+
"""The subscriptions on this page, newest first."""
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
class _DeliveryRecordRequired(TypedDict):
|
|
554
|
+
eventId: str
|
|
555
|
+
"""The delivered event's id."""
|
|
556
|
+
attempt: int
|
|
557
|
+
"""Attempt number, starting at 1."""
|
|
558
|
+
status: DeliveryStatus
|
|
559
|
+
"""The outcome."""
|
|
560
|
+
durationMs: int
|
|
561
|
+
"""How long the attempt took, in milliseconds."""
|
|
562
|
+
at: str
|
|
563
|
+
"""When the attempt was made, ISO 8601."""
|
|
564
|
+
changeTypes: list[FlightChangeType]
|
|
565
|
+
"""The change types the event carried."""
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
class DeliveryRecord(_DeliveryRecordRequired, total=False):
|
|
569
|
+
"""One webhook delivery attempt."""
|
|
570
|
+
|
|
571
|
+
httpStatus: int | None
|
|
572
|
+
"""The receiver's HTTP status, when it answered."""
|
|
573
|
+
error: str | None
|
|
574
|
+
"""What went wrong, when the attempt failed."""
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
# ``from`` is a Python keyword, so this one is spelled the functional way.
|
|
578
|
+
_FlightChangeRequired = TypedDict(
|
|
579
|
+
"_FlightChangeRequired",
|
|
580
|
+
{
|
|
581
|
+
"type": FlightChangeType,
|
|
582
|
+
"path": str,
|
|
583
|
+
"from": str | int | float | bool | None,
|
|
584
|
+
"to": str | int | float | bool | None,
|
|
585
|
+
"message": str,
|
|
586
|
+
},
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
class FlightChange(_FlightChangeRequired, total=False):
|
|
591
|
+
"""One change inside a ``flight.updated`` event.
|
|
592
|
+
|
|
593
|
+
Keys: ``type`` (what changed), ``path`` (dotted path into the snapshot,
|
|
594
|
+
for example ``departure.gate``), ``from`` and ``to`` (the old and the new
|
|
595
|
+
value; times are ISO 8601 UTC strings), ``message`` (a sentence describing
|
|
596
|
+
the change) and ``deltaMinutes``.
|
|
597
|
+
"""
|
|
598
|
+
|
|
599
|
+
deltaMinutes: int | None
|
|
600
|
+
"""Signed minute delta, for time changes."""
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
class _WebhookEventRequired(TypedDict):
|
|
604
|
+
id: str
|
|
605
|
+
"""Event id, ``evt_<uuid>``. The same across delivery retries: deduplicate on it."""
|
|
606
|
+
type: WebhookEventType
|
|
607
|
+
"""The event type."""
|
|
608
|
+
createdAt: str
|
|
609
|
+
"""When the event was created, ISO 8601."""
|
|
610
|
+
subscriptionId: str
|
|
611
|
+
"""The subscription it belongs to."""
|
|
612
|
+
flightKey: str
|
|
613
|
+
"""The flight's key."""
|
|
614
|
+
changes: list[FlightChange]
|
|
615
|
+
"""What changed. Empty except on ``flight.updated``."""
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
class WebhookEvent(_WebhookEventRequired, total=False):
|
|
619
|
+
"""A webhook event, as posted to your receiver and as listed by ``subscriptions.events``."""
|
|
620
|
+
|
|
621
|
+
flight: FlightSnapshot | None
|
|
622
|
+
"""The flight after the change. Missing on some lifecycle events."""
|
|
623
|
+
previousFetchedAt: str | None
|
|
624
|
+
"""When the previous snapshot was fetched, on ``flight.updated``."""
|
|
625
|
+
metadata: dict[str, str] | None
|
|
626
|
+
"""The subscription's metadata."""
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
class _AmbiguousLegRequired(TypedDict):
|
|
630
|
+
origin: str
|
|
631
|
+
"""Origin IATA code."""
|
|
632
|
+
destination: str
|
|
633
|
+
"""Destination IATA code."""
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
class AmbiguousLeg(_AmbiguousLegRequired, total=False):
|
|
637
|
+
"""One of the legs listed by an ambiguous-flight ``BadRequestError``."""
|
|
638
|
+
|
|
639
|
+
carrier: str
|
|
640
|
+
"""Operating carrier."""
|
|
641
|
+
scheduledDepartureLocal: str
|
|
642
|
+
"""Scheduled local departure, ``HH:mm``."""
|
|
643
|
+
flightId: str
|
|
644
|
+
"""Upstream flight id."""
|