Data Models

The field-by-field specification of every entity in the system. Each entity is defined here in pseudo-schema notation. The canonical JSON Schema files live in flighthelp/schema; this document is the human-readable summary.

For the philosophy behind these schemas, see SCHEMAS.md. For how the entities are served, see API.md. For how they map to repositories and files, see REPOSITORIES.md.

Every fact-carrying entity (Airline, Airport, BaggageRule, ContactMethod, Fee, Scenario, Regulation) includes a common provenance block:

last_verified_at    ISO 8601 timestamp
verifier_count      integer ≥ 0
sources             array of source_id
confidence_score    decimal 0–1 (derived from age, verifier count, source quality)
revisions           array of revision_id

Provenance is enforced at the schema level. Data cannot be published without it.

Airline

The top-level entity. Roughly 400 active scheduled carriers worldwide; the schema also supports defunct carriers for historical reference.

id                       slug, e.g. "lufthansa" (lowercase, hyphen-separated)
iata_code                string, exactly 2 alphanumeric chars, uppercase
icao_code                string, exactly 3 alphabetic chars, uppercase
legal_name               string, the legal entity name
common_name              localizable string, the marketing name
country_of_origin        ISO 3166-1 alpha-2 country code
alliance                 enum: star, oneworld, skyteam, none
hub_airports             array of airport_id
secondary_hubs           array of airport_id
fleet_size               integer ≥ 0, optional
fare_classes             array of fare_class_id
status                   enum: active, suspended, defunct, restructuring
operates_in              array of ISO 3166-1 country codes
founded_year             integer ≥ 1900
ceased_year              integer, optional, for defunct carriers
parent_company           string, optional
subsidiaries             array of airline_id
website                  URL
support_website          URL, optional
logo_url                 URL (low-resolution, stored under nominative fair use)
languages_supported      array of ISO 639-1 codes (languages the airline operates in)
plus provenance block

The fare_classes array references separate FareClass entities (see below) because fare structures vary too much by airline to flatten into a simple enum.

FareClass

Fare classes are airline-specific. American's Basic Economy is not Lufthansa's Economy Light. The schema models them as first-class entities.

id                       composite: {airline_id}:{slug}, e.g. "lufthansa:economy-light"
airline_id               airline_id
name                     localizable string, the airline's marketing name
internal_code            string, optional (e.g. RBD codes)
cabin                    enum: economy, premium_economy, business, first
restrictions             markdown, localizable (changes, cancellations, refundability)
included                 structured object: {
                           baggage: {personal_item, carry_on, checked}
                           seat_selection: enum(free, paid, paid_after_check_in)
                           meal: enum(none, snack, full)
                           lounge_access: bool
                           priority_boarding: bool
                         }
sortable_rank            integer (1 = lowest in the cabin)
notes                    markdown
plus provenance block

The structured included object is the bridge between the fare class and the BaggageRule entity — it specifies which baggage rules apply by default to this fare.

Airport

Roughly 500 major airports get full pages; smaller airports get stub records. The schema supports both.

id                       slug, e.g. "lhr"
iata_code                string, exactly 3 alphabetic chars, uppercase
icao_code                string, exactly 4 alphabetic chars, uppercase
name                     localizable string
short_name               localizable string, optional
city                     localizable string
country                  ISO 3166-1 alpha-2
region                   string, optional (state/province/county)
coordinates              {latitude: decimal -90..90, longitude: decimal -180..180}
elevation_m              integer, optional
timezone                 IANA timezone string
terminals                array of Terminal objects
runway_count             integer ≥ 1
size_class               enum: small, medium, large, mega (passengers per year buckets)
status                   enum: active, closed, under_construction
amenities                array of Amenity objects
ground_transport         array of GroundTransport objects
lounges                  array of lounge_id
lost_and_found           array of contact_id
notes                    markdown, localizable
plus provenance block

Terminal object:
  id                     slug, scoped to airport
  name                   localizable string
  airlines               array of airline_id
  connected_terminals    array of terminal_id (in same airport, for transit)
  pre_security_amenities array of amenity_id
  post_security_amenities array of amenity_id

Amenity object:
  type                   enum: shower, sleep_pod, prayer_room, quiet_room,
                               baggage_storage, medical, family_room,
                               smoking_lounge, pet_relief, charging_station,
                               wifi, food_24h, bank, atm, currency_exchange
  location_description   localizable string
  hours                  structured hours object
  cost                   {amount, currency} or "free"
  notes                  markdown

GroundTransport object:
  type                   enum: metro, train, bus, taxi, rideshare, shuttle,
                               rental_car, walking
  destination            string (e.g. "city center", "main station")
  duration_minutes       integer range {min, max}
  cost                   {amount, currency} range {min, max}
  operating_hours        structured hours object
  url                    URL, optional
  notes                  markdown

BaggageRule

The most-edited entity in the system. One rule per (airline × fare_class × bag_type) combination.

id                       uuid
airline_id               airline_id
fare_class_id            fare_class_id
bag_type                 enum: personal_item, carry_on, checked, oversized,
                              sports_equipment, musical_instrument,
                              pet_cabin, pet_cargo, mobility_aid,
                              stroller, car_seat, infant
max_dimensions_cm        {length, width, height} (all decimals)
max_dimensions_in        {length, width, height} (auto-converted, for display)
max_weight_kg            decimal ≥ 0
max_weight_lb            decimal (auto-converted)
max_count                integer ≥ 0 (how many of this type allowed)
linear_dimensions_cm     decimal, optional (total of l+w+h, some airlines use this)
fee_if_exceeded          reference to Fee entity
fee_to_add               reference to Fee entity, optional (for paid carry-on etc.)
sizer_photo_urls         array of URL (Cloudflare R2, contributor-uploaded)
enforcement_strictness   enum: lax, normal, strict, brutal (community-rated)
route_restrictions       array of RouteRestriction objects
effective_from           date (YYYY-MM-DD)
effective_to             date, nullable
notes                    markdown, localizable
plus provenance block

RouteRestriction object:
  applies_to             enum: only_these, except_these
  routes                 array of {
                           origin: airport_id or country code or "any"
                           destination: airport_id or country code or "any"
                         }
  reason                 markdown

The route_restrictions field handles cases like "Ryanair 10kg cabin bag rule on UK domestic only" or "Air Canada different rules to/from US versus international." Without this, the model cannot represent the reality.

The enforcement_strictness field captures the gap between published policy and gate reality. A community rating of "brutal" means the airline actually weighs and measures; "lax" means they typically don't bother. This is what makes the data uniquely useful versus airline-published data.

ContactMethod

One record per (airline × purpose × channel × country_scope) combination. Designed so the API can return contact methods sorted by what works.

id                       uuid
airline_id               airline_id
purpose                  enum: reservations, baggage, special_assistance,
                              refunds, corporate, media, accessibility,
                              loyalty_program, group_bookings,
                              compensation_claims, complaints
channel                  enum: phone, whatsapp, x_twitter, facebook,
                              instagram, email, live_chat, web_form,
                              physical_desk, postal_address
value                    string (phone number in E.164, handle, URL, email)
country_scope            array of ISO 3166-1 codes (which country's callers
                         should use this); empty array means global
language                 array of ISO 639-1 codes
hours                    structured Hours object with timezone
typical_wait_minutes     integer, crowdsourced rolling 30-day average
success_rate             decimal 0–1, crowdsourced thumbs up/down ratio
sample_size              integer (how many ratings the success_rate is based on)
notes                    markdown, localizable (e.g. "press 3 then 7 for human")
ivr_path                 ordered array of strings, optional (the menu sequence)
plus provenance block

Hours object:
  monday..sunday         array of {open: HH:MM, close: HH:MM} (multiple ranges OK)
  timezone               IANA timezone
  exceptions             array of {date_range, hours_override}
  notes                  markdown

The success_rate field is the killer feature for travelers. Contact methods can be sorted by it. A number that reaches a human ranks above the published "official" number that rings forever.

Fee

Every fee an airline charges, structured.

id                       uuid
airline_id               airline_id
fee_type                 enum: change, cancel, no_show, name_change,
                              seat_select_standard, seat_select_extra_legroom,
                              seat_select_exit_row, priority_boarding,
                              early_boarding, meal_economy, meal_premium,
                              alcoholic_beverage, lounge_access,
                              bag_overweight, bag_oversize,
                              extra_bag, carry_on, infant_in_lap,
                              child_unaccompanied, pet_cabin, pet_cargo,
                              sports_equipment, musical_instrument,
                              mobility_aid, oxygen, wifi, entertainment
amount                   decimal ≥ 0
currency                 ISO 4217 code
amount_range             {min, max}, optional (when fee varies)
conditions               structured Conditions object
charged_per              enum: passenger, segment, direction, bag_unit, hour, mb
collected_by             enum: airline, airport, ground_handler, third_party
refundable               bool
refund_window_hours      integer, optional
notes                    markdown, localizable
plus provenance block

Conditions object:
  route_origins          array of airport_id or country, optional
  route_destinations     array of airport_id or country, optional
  fare_classes           array of fare_class_id, optional
  advance_purchase_days  {min, max}, optional
  member_only            bool
  online_only            bool
  payment_method         array of enum (some fees vary by card type)

Scenario

A passenger-rights scenario. Roughly 80 core scenarios at launch, expanding as regulations and community input shape the list.

id                       slug, e.g. "eu-cancellation-less-than-14-days"
title                    localizable string
short_summary            localizable string, one sentence
jurisdiction             array of regulation_id
applies_when             structured Eligibility object
your_rights              markdown, localizable
step_by_step             ordered array of Action objects
template_messages        array of TemplateMessage objects
escalation_path          ordered array of EscalationStep objects
typical_outcomes         {
                           compensation_range: {min, max, currency}
                           success_rate_percent: integer 0–100
                           time_to_resolution_days: {median, p90}
                         }
case_examples            array of CaseExample objects (anonymized)
common_pushback          array of {
                           airline_claim: markdown
                           counter_response: markdown
                           legal_basis: regulation_id + article
                         }
related_scenarios        array of scenario_id
plus provenance block

Eligibility object:
  flight_type            array of enum (commercial, charter, cargo, etc.)
  origin_in              array of country codes (where flight originated)
  destination_in         array of country codes
  carrier_jurisdiction   array of country codes
  delay_min_hours        integer, optional
  cancellation_notice_max_days integer, optional
  additional_conditions  markdown

Action object:
  step_number            integer
  title                  localizable string
  description            markdown, localizable
  time_limit             string, optional (e.g. "within 2 hours of disruption")
  proof_to_collect       array of strings (e.g. "boarding pass", "photo of board")

TemplateMessage object:
  channel                enum: email, postal, web_form, chat, in_person
  language               ISO 639-1
  subject                string, optional (for email)
  body                   markdown with placeholders
  placeholders           array of {name, description, example}

EscalationStep object:
  level                  integer (1 = first attempt, 2 = next, etc.)
  target                 enum: airline_direct, national_regulator, eu_body,
                                court_small_claims, court_civil, court_aviation,
                                consumer_protection_agency, ombudsman, media
  contact                contact_id or markdown if not in dataset
  expected_timeframe     string
  notes                  markdown

CaseExample object:
  flight_route           string (anonymized: e.g. "LHR-JFK")
  carrier                airline_id (if public)
  disruption             markdown
  outcome                markdown
  compensation_received  {amount, currency}, optional
  time_to_resolution_days integer
  contributor_id         contributor_id, optional

Regulation

The legal frameworks that ground every compensation determination.

id                       slug, e.g. "eu-261-2004"
title                    localizable string, full title
short_name               string, common usage (e.g. "EU 261")
jurisdiction             array of ISO 3166-1 codes
effective_date           date
amended_dates            array of date
full_text_urls           array of {language, url}
plain_language           markdown, localizable
key_provisions           array of Provision objects
enforcement_body         array of contact_id
amendments               array of Amendment objects
superseded_by            regulation_id, optional
plus provenance block

Provision object:
  article                string (e.g. "Article 7", "§250.5")
  title                  localizable string
  summary                markdown, localizable
  applies_to             markdown
  triggers               structured object (referenced by rules engine)

Amendment object:
  date                   date
  description            markdown
  source_url             URL
  changed_articles       array of strings

The key_provisions[].triggers field is what the rules engine reads. See RULES-ENGINE.md for how triggers are structured.

Contributor

id                       uuid
display_name             string (chosen, can be pseudonymous; profanity-filtered)
joined_at                ISO 8601 timestamp
home_airport             airport_id, optional
languages                array of ISO 639-1 codes
country                  ISO 3166-1, optional (for moderator timezone routing)
reputation_score         integer (computed nightly)
trust_tier               enum: new, regular, trusted, moderator, core
edits_submitted          integer ≥ 0
edits_approved           integer ≥ 0
edits_rejected           integer ≥ 0
verifications            integer ≥ 0
photos_uploaded          integer ≥ 0
airlines_flown           array of airline_id (self-reported)
airports_visited         array of airport_id (self-reported)
badges                   array of badge_id
public_profile           bool
flight_credentials       array of Credential objects (verified)

Credential object:
  type                   enum: cabin_crew, gate_agent, pilot, dispatcher,
                                aviation_lawyer, frequent_flyer_status,
                                consumer_advocate, regulator_staff
  airline_id             airline_id, optional
  verification_method    enum: id_check, employer_verification, peer_vouch
  verified_at            timestamp
  expires_at             timestamp, optional

Edit / Revision

Every change to any entity creates a Revision. The full edit history is queryable; no data is ever destroyed, only superseded.

id                       uuid
entity_type              enum (matches all entity types)
entity_id                string (the id of the entity being edited)
field_path               string (dot-notation, e.g. "baggage_rules.carry_on.max_weight_kg")
prior_value              json
new_value                json
contributor_id           contributor_id
submitted_at             ISO 8601 timestamp
source_urls              array of URL
source_photos            array of upload_id
source_notes             markdown
moderator_id             contributor_id, nullable
moderation_state         enum: pending, approved, rejected, disputed, withdrawn
moderation_decided_at    timestamp, nullable
moderation_note          markdown, nullable
moderation_reason_code   enum, nullable (when rejected; standard reason codes)
verification_votes       {confirm: integer, dispute: integer, sample: integer}
parent_revision_id       revision_id, nullable (for chained edits)

Source

The provenance record. Every claim traces to at least one Source.

id                       uuid
url                      URL
type                     enum: airline_official, regulator_official,
                                court_ruling, news_article, photo,
                                user_report, leaked_internal, academic_paper,
                                wikipedia_archived, scraper_snapshot
captured_at              ISO 8601 timestamp
snapshot_url             URL (archive.org, internal archive, or R2)
title                    string
publisher                string
language                 ISO 639-1
reliability_tier         enum: primary, secondary, tertiary
notes                    markdown

Reliability tiers:

  • Primary: airline's own page, regulator's own page, court document, photo with timestamp/geotag.
  • Secondary: reputable news article, contributor user report with evidence.
  • Tertiary: Wikipedia, forum post, screenshot without verification.

Claims supported only by tertiary sources are flagged in the API response and visibly marked on the site.

Badge

id                       slug
name                     localizable string
description              localizable string
criteria                 structured Rule (the engine that decides who has it)
rarity                   enum: common, uncommon, rare, legendary
icon_url                 URL (SVG, monochrome, on hairline background)
introduced_on            date
retired_on               date, nullable

Roughly 80 badges at launch. Examples: "First Edit," "Verified at 50 airports," "Translated a scenario into 3 languages," "Survived a Ryanair gate check," "Documented during a strike," "Found a broken scraper."