Using the rules engine
@flighthelp/rules-engine is the passenger-rights logic as a pure, dependency-
free TypeScript library. It takes a structured flight-disruption Scenario and
returns a Determination: eligibility, legal basis, compensation, time limits,
rights, and next steps — each tied to a specific article of law. It is licensed
MIT and embeds anywhere (server, edge, browser).
Install
npm install @flighthelp/rules-engine
Evaluate
import { evaluate } from "@flighthelp/rules-engine";
const determination = evaluate({
flight: { operating_carrier: "LH", origin: "FRA", destination: "JFK" },
disruption: { type: "delay", delay_minutes: 240, reason: "technical_aircraft" },
passenger: { count: 1 },
});
determination.best_outcome;
// { regime: "eu-261", compensation: { amount: 600, currency: "EUR" }, ... }
Input: Scenario
{
flight: {
operating_carrier: string; // IATA code, e.g. "LH"
origin: string; // IATA airport code
destination: string; // IATA airport code
scheduled_arrival?: string; // ISO 8601
actual_arrival?: string; // ISO 8601 (used to derive arrival delay)
};
disruption: {
type: "delay" | "cancellation" | "denied_boarding" | "missed_connection"
| "diversion" | "tarmac_delay"
| "baggage_lost" | "baggage_delayed" | "baggage_damaged";
delay_minutes?: number; // arrival delay
cancellation_notice_days?: number; // for cancellations
reason?: string; // e.g. "technical_aircraft", "weather"
rebooking?: { offered: boolean; accepted: boolean; new_arrival?: string };
};
passenger: {
count: number;
ticket_price?: { amount: number; currency: string }; // enables US §250.5 math
};
}
Output: Determination
{
engine_version: string;
applicable_regimes: Array<{
regime: string; // "eu-261", "uk-261", "montreal", "brazil-400", "us-dot"
applies: boolean;
reason: string;
compensation?: { amount: number; currency: string; confidence: string };
rights: Array<{
type: string;
description: string;
legal_basis: Array<{ regulation_id: string; article: string; description: string; url?: string }>;
}>;
time_limits: Array<{ action: string; deadline_days: number; starts_from: string }>;
}>;
best_outcome: { regime: string; compensation: { amount: number; currency: string } } | null;
next_steps: Array<{ order: number; action: string; who_to_contact: string }>;
warnings: Array<{ level: string; message: string }>;
}
best_outcome ranks fixed entitlements across regimes (normalized to EUR for
comparison only — displayed amounts stay in their native currency). Regimes that
model liability as a cap on proven loss (Montreal; US denied boarding without a
known fare) surface as rights with a maximum and do not feed best_outcome.
Regimes
| Regime id | Law | Notes |
|---|---|---|
eu-261 |
EC 261/2004 | Fixed EUR 250/400/600 by distance band |
uk-261 |
Retained EU 261 | Fixed GBP 220/350/520 |
montreal |
Montreal Convention 1999 | Baggage + delay liability cap (SDR) |
brazil-400 |
ANAC 400/2016 | Material assistance + re-accommodation |
us-dot |
14 CFR | Denied boarding (§250.5), 2024 refund rule, tarmac, baggage |
A single flight can trigger several regimes at once (e.g. a UK↔EU flight on an EU carrier triggers both EU 261 and UK 261); the engine returns all of them and picks the best fixed outcome.
Auditing the law
Every rule cites its legal basis in the output. Each regime folder also ships a
legal-references.md (and case-law.md for EU 261) documenting the articles,
thresholds, and the cases that shape interpretation. Legal updates are made
test-first, and an interpretation change that affects existing claims is a major
version bump.