Build AirHelp

KINDAback to the verdict

0%0 of 20 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A local case file per disrupted flight: journey facts, a distance and delay calculation producing a compensation band, an evidence checklist, generated letters citing the right regulation, and deadline tracking that knows the limitation period differs by jurisdiction.

estimated effort weekendthe files for this build are in the project pack

RuntimeNode 22 with SQLite, running locallyRulesA dated rules table, versioned, separate from the codeDistanceGreat circle distance between airport coordinatesDocumentsMarkdown letters rendered to PDFEvidenceFiles stored alongside the case with a manifest

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the case file, calculations and document generation.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. decidefree, and it is the first task rather than a formality

    Why Phase 1 builds a table of legal figures, and secondary summaries simplify in ways that change outcomes. Reading the text is the difference between a tool that helps and one that misleads you about your own claim.

    Get it Read the regulation on the official legal database for your jurisdiction, and the enforcement body's guidance. Record what you checked and when. open ↗

    Verify The rules table has been corrected against the regulation text and the corrections are recorded

  3. have readyfree with an open dataset

    Why Distance drives the compensation band, so the coordinates must come from a source you can cite and are licensed to use.

    Get it Use an openly licensed airport dataset and record its licence. Verify a few known city pairs against published distances before trusting it.

    Verify Three known city pair distances match published figures

  4. have readyfree

    Why Every phase is meaningless against invented data, and real cases have the awkward details: separate bookings, connections, and a vague airline reason.

    Get it Use your own disruption if you have one, including the actual airline correspondence. Otherwise construct one in detail from a friend's.

    Verify A complete journey with real times and a real airline reason is entered

  5. decidefree

    Why A tool that produces a compensation figure will be read as an answer. If anyone but you uses it, the framing matters more than the calculation.

    Get it Decide whether this stays private. If it does not, write the disclaimer before the first screen, not after someone relies on it.

    Verify The non-advice statement appears on every screen that produces an output

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- cases(id, created_at, state, jurisdiction, regulation, rules_version) · state is drafting, submitted, escalated, settled or refused
- flights(case_id, ordinal, carrier, number, date, origin_iata, destination_iata, scheduled_departure, actual_arrival, distance_km)
- passengers(case_id, name, booking_reference, is_claimant)
- disruption(case_id, kind, delay_minutes_at_destination, airline_reason, reason_category) · kind is delay, cancellation or denied_boarding
- evidence(case_id, kind, path, added_at, notes) · boarding pass, booking confirmation, airline correspondence, receipts
- correspondence(case_id, direction, at, channel, subject, body_path, deadline_at)
- rules(version, effective_from, jurisdiction, band_km_min, band_km_max, amount, currency, notes, source_url, checked_on)
- The rules table carries a source URL and a check date on every row. A figure without both is not usable.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DB_PATHrequired./data/cases.dbCase files, containing passenger names and booking references. Local, and worth encrypting the backup.
EVIDENCE_DIRrequired./data/evidenceWhere evidence files are stored, one folder per case.
DEFAULT_JURISDICTIONrequiredUKWhich rules set to resolve by default. Changing it changes amounts, currency and limitation period.
DEADLINE_WARN_DAYSrequired90How far ahead of the limitation date to warn. Long enough to actually act.
RULES_PATHrequired./data/rules.jsonThe dated rules table. Every figure here carries a source and a check date.

The build, in order

  1. The rules table, before any logic

    Every legal figure lives in dated data with a source, and nothing is hard coded.

    1. As of September 2026 the EU bands are 250 euro under 1,500 km, 400 euro from 1,500 to 3,500 km and 600 euro above 3,500 km, and the UK equivalents are 220, 350 and 520 pounds. Enter them with sources and check them yourself.

      Files data/rules.json

    2. The three hour arrival threshold, and the rule allowing a reduction for a long flight arriving between three and four hours late. Both as data.

      Files data/rules.json

    3. Secondary sources summarise and simplify. Read the text itself for the rows you entered, and record what you changed.

      Files docs/rules-review.md

    done when · tick each as it passes
    watch out
    • A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite.
  2. Journey facts and distance

    A case records the journey accurately, and the distance is computed the way the regulation means it.

    1. With scheduled and actual times per leg, and a flag for whether they share one booking reference.

      Files src/journey.js

    2. First origin to final destination. Verify against a published distance for a city pair you know.

      Files src/distance.js

    3. Actual arrival against scheduled arrival at the final destination. This is the number the regulation uses.

      Files src/delay.js

    done when · tick each as it passes
    watch out
    • Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case.
  3. Eligibility, with the uncertainty visible

    The application says which band applies and, just as clearly, what it cannot decide.

    1. Distance and delay to a band and amount, using the version effective on the flight date.

      Files src/assess.js

    2. Two lists in the output. Anything requiring judgement goes in the second list with a note on where the burden lies.

      Files src/assess.js

    3. Categorised for your own tracking, with the burden of proof noted, and no verdict.

      Files src/reasons.js

    done when · tick each as it passes
    watch out
    • Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it.
  4. Evidence, and what is missing

    The case knows what it has and what it still needs.

    1. Delay, cancellation and denied boarding need different evidence. Keep the lists as data.

      Files data/checklists.json

    2. In a case folder with recorded kinds and notes. The manifest is what you attach.

      Files src/evidence.js

    3. Booking access and app records disappear. Say which and how soon.

      Files data/checklists.json

    done when · tick each as it passes
    watch out
    • Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable.
  5. Letters and the deadline patchwork

    You can send a correctly framed letter and you know when the claim expires.

    1. First claim and escalation, both drawing figures from the rules version rather than restating them.

      Files templates/

    2. From the rules table, with the basis shown next to the date. Never one global period.

      Files src/deadlines.js

    3. A warning with real notice, and a copy of every letter with its date.

      Files src/correspondence.js

    done when · tick each as it passes
    watch out
    • Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more travel.