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.