{"openapi":"3.0.3","info":{"title":"PreBoard public data API","version":"1.0.0","description":"Security wait data for US airports (TSA) and selected international hubs (airport-operated security; airport objects include country and IANA timezone). Every value is labeled by source: measured (airport/traveler feeds) vs modeled. Where no real source exists the API returns no number. Free, unauthenticated, best-effort; please cache and keep requests modest. Not affiliated with TSA."},"servers":[{"url":"https://preboard.ai"}],"paths":{"/api/v1/airports":{"get":{"summary":"Airport list, or ranked typeahead search with ?q=.","parameters":[{"name":"q","in":"query","required":false,"description":"City, name, state or IATA code. Returns up to `limit` ranked matches.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"1–20, default 8 (only with q).","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/airports/{code}":{"get":{"summary":"Airport, checkpoints and current waits (each wait carries source_type and data_tier).","parameters":[{"name":"code","in":"path","required":true,"description":"IATA code, e.g. ATL","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/airports/{code}/live":{"get":{"summary":"Per-checkpoint, per-lane current waits with trend, confidence and source.","parameters":[{"name":"code","in":"path","required":true,"description":"IATA code","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/airports/{code}/pattern":{"get":{"summary":"Typical standard-lane wait by local hour and day of week, from MEASURED history only. `publishable` is false until ≥14 days of data exist.","parameters":[{"name":"code","in":"path","required":true,"description":"IATA code","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/airports/{code}/history":{"get":{"summary":"Raw readings (≤7 days, `hours`) or hourly rollups (`days`, up to 365).","parameters":[{"name":"code","in":"path","required":true,"description":"IATA code","schema":{"type":"string"}},{"name":"hours","in":"query","required":false,"description":"1–168 (raw)","schema":{"type":"string"}},{"name":"days","in":"query","required":false,"description":"1–365 (rollup)","schema":{"type":"string"}},{"name":"checkpoint","in":"query","required":false,"description":"Optional checkpoint id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/airports/{code}/predict":{"get":{"summary":"4-hour forecast (15-minute steps). Only exists where real history supports it.","parameters":[{"name":"code","in":"path","required":true,"description":"IATA code","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/map/overview":{"get":{"summary":"All airports with worst current measured standard wait (null when no live data).","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/map/worst":{"get":{"summary":"Airports with the longest measured waits right now.","parameters":[{"name":"limit","in":"query","required":false,"description":"1–50, default 10","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/stats/national":{"get":{"summary":"National aggregates over fresh measured waits.","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/coverage":{"get":{"summary":"Which airports have real, fresh measurements right now, by source, and which do not.","parameters":[],"responses":{"200":{"description":"JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Coverage"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/leave-by":{"get":{"summary":"When to leave / arrive: deterministic plan. Security uses a Live/Traveler reading only when security starts within 2 hours; further out it uses the 'Typical' measured wait for that hour of the week, otherwise a clearly labeled 'Estimated' baseline (the current reading is returned as security.live_now_minutes for reference). Also accepts POST with the same fields as JSON. Anonymous: 30 req/min/IP; with X-API-Key: your key's limit.","parameters":[{"name":"airport","in":"query","required":false,"description":"IATA code (required)","schema":{"type":"string"}},{"name":"departure_time","in":"query","required":false,"description":"ISO 8601 with timezone offset (required), e.g. 2026-10-12T08:30:00-04:00","schema":{"type":"string"}},{"name":"flight_type","in":"query","required":false,"description":"domestic | international","schema":{"type":"string"}},{"name":"program","in":"query","required":false,"description":"none | precheck | clear | both","schema":{"type":"string"}},{"name":"checked_bags","in":"query","required":false,"description":"0–10","schema":{"type":"string"}},{"name":"kids","in":"query","required":false,"description":"0–8","schema":{"type":"string"}},{"name":"travel_minutes","in":"query","required":false,"description":"Door-to-airport minutes; enables leave_home_at","schema":{"type":"string"}},{"name":"cushion","in":"query","required":false,"description":"standard | extra","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveBy"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/door-to-gate":{"get":{"summary":"One door-to-gate answer: every leg (drive, parking, bags, security, walk, gate, cushion) with its own source and confidence, FAA disruption status, arrive_by_options (80/90% on-time) and a prediction_id. Same parameters as leave-by plus drive_minutes and parking_minutes (caller-supplied; Preboard does not know traffic).","parameters":[{"name":"airport","in":"query","required":false,"description":"IATA code","schema":{"type":"string"}},{"name":"departure_time","in":"query","required":false,"description":"ISO 8601 with offset","schema":{"type":"string"}},{"name":"drive_minutes","in":"query","required":false,"description":"0-600, optional","schema":{"type":"string"}},{"name":"parking_minutes","in":"query","required":false,"description":"0-120, optional","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/door-to-gate/ics":{"get":{"summary":"Same inputs as door-to-gate; returns a leave-by calendar event (.ics, text/calendar) with a 15-minute reminder and every leg in the notes.","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/pilot-requests":{"post":{"summary":"Request a pilot / API key (organization, contact, use case). Stored for review; we reply by email.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"org":"Acme Parking","contact_name":"Jo","email":"jo@acme.example","use_case":"Show leave-by time in booking confirmations","airports":"ATL,ORD","expected_rpm":60}}}},"parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/mcp":{"post":{"summary":"Model Context Protocol server (Streamable HTTP, JSON responses). Public tools: get_wait, leave_by, door_to_gate, compare_checkpoints, coverage, track_record, report_outcome, faa_delays, airport_conditions, border_waits, check_in_info. When enabled, trip tools (list_trips, add_trip, get_trip_plan, delete_trip, import_trip_email) with Authorization: Bearer pba_.... GET returns 405.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"jsonrpc":"2.0","id":1,"method":"tools/list"}}}},"parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/embed":{"get":{"summary":"Embeddable leave-by card (HTML, no JS/cookies). Params: a, d (ISO or epoch minutes), f=d|i, p=n|p|c|b, b, k, t, theme=light|dark, tz.","parameters":[{"name":"a","in":"query","required":false,"description":"IATA code","schema":{"type":"string"}},{"name":"d","in":"query","required":false,"description":"Departure ISO with offset or epoch minutes UTC","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/devices":{"post":{"summary":"Register an iOS device for future alerts (no push is sent yet). Returns a one-time device_secret. 10 req/min/IP.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"apns_token":"0000000000000000000000000000000000000000000000000000000000000000","airport":"ATL","threshold_minutes":45,"timezone":"America/New_York"}}}},"parameters":[],"responses":{"201":{"description":"JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceRegistration"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/devices/{id}":{"put":{"summary":"Update alert preferences. Authorization: Bearer <device_secret>.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"airport":"ATL","threshold_minutes":60}}}},"parameters":[{"name":"id","in":"path","required":true,"description":"device_id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"delete":{"summary":"Delete this device record and token (Delete my data). Authorization: Bearer <device_secret>.","parameters":[{"name":"id","in":"path","required":true,"description":"device_id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/trips/parse":{"post":{"summary":"Stateless: extract flight-leg CANDIDATES from pasted confirmation text or .ics contents. Nothing is stored. Confirm before saving.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"text":"Flight DL 1234 Atlanta (ATL) to Los Angeles (LAX) Departs Oct 20, 2026 8:30 AM"}}}},"parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/devices/{id}/trips":{"get":{"summary":"List this device's upcoming trips. Authorization: Bearer <device_secret>.","parameters":[{"name":"id","in":"path","required":true,"description":"device_id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"post":{"summary":"Save a confirmed trip (max 20; no names, confirmation codes or credentials). Authorization: Bearer <device_secret>.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"airport":"ATL","departure_time":"2026-10-20T08:30:00-04:00","carrier":"DL","number":"1234","destination":"LAX","program":"precheck","checked_bags":1,"kids":0,"source":"paste"}}}},"parameters":[{"name":"id","in":"path","required":true,"description":"device_id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/devices/{id}/trips/{tripId}":{"get":{"summary":"One trip with an automatic current leave-by and carrier check-in links. Authorization: Bearer <device_secret>.","parameters":[{"name":"id","in":"path","required":true,"description":"device_id","schema":{"type":"string"}},{"name":"tripId","in":"path","required":true,"description":"trip id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"delete":{"summary":"Delete a trip. Authorization: Bearer <device_secret>.","parameters":[{"name":"id","in":"path","required":true,"description":"device_id","schema":{"type":"string"}},{"name":"tripId","in":"path","required":true,"description":"trip id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/accuracy":{"get":{"summary":"Public track record: our predicted security waits vs later measured waits (n, mean error, % within 5/10 min). Numbers are published only after enough predictions are scored; otherwise it reports that it is still collecting.","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/outcomes":{"post":{"summary":"Report how long security actually took for a prediction_id returned by leave-by. Anonymous; 20/hour per reporter; only after the predicted security time. Body: {prediction_id, actual_minutes 0-300}. Used to score and improve estimates.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"prediction_id":"00000000-0000-0000-0000-000000000000","actual_minutes":18}}}},"parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/health":{"get":{"summary":"Service and feed health.","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/airports/{code}/checkpoints":{"get":{"summary":"Checkpoints for an airport with their current waits (each wait carries its source).","parameters":[{"name":"code","in":"path","required":true,"description":"IATA code","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/airports/{code}/context":{"get":{"summary":"Airport-day context from open public sources, each part labeled by the server: weather (METAR/TAF + active NWS alerts, official), CBP customs processing waits by hour (TYPICAL from recent weeks, NOT live) and live airport rail departures where a feed exists. Any part is null when no source exists.","parameters":[{"name":"code","in":"path","required":true,"description":"IATA code","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/border-waits":{"get":{"summary":"CBP land-border crossing waits (CBP-published), optionally filtered.","parameters":[{"name":"border","in":"query","required":false,"description":"canada | mexico","schema":{"type":"string"}},{"name":"q","in":"query","required":false,"description":"Port name filter","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/reports":{"post":{"summary":"Submit an anonymous traveler wait report. Shown only as 'Traveler reports' (never Live) once enough reports agree. lane_type: standard | precheck | clear; wait_minutes 0–300; note ≤280 chars. No location required. 10 per hour per IP. Returns 201 {id, status}.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"airport_code":"ATL","checkpoint":"Main","lane_type":"standard","wait_minutes":15,"note":"optional"}}}},"parameters":[],"responses":{"201":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/reports/flag":{"post":{"summary":"Flag a traveler report as wrong, spam, abusive or other (reports with enough flags are hidden). Returns 202 whether or not the id exists.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"report_id":"00000000-0000-0000-0000-000000000000","reason":"wrong"}}}},"parameters":[],"responses":{"202":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/alerts/subscribe":{"post":{"summary":"Long-wait email alert sign-up (double opt-in). Requires consent:true (and a Turnstile token when configured). Email sending is currently switched off; sign-ups are stored and nothing is sent until it is enabled. Confirm/unsubscribe happen via the links in the emails (web forms).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"email":"you@example.com","airport":"ATL","consent":true}}}},"parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/auth/apple/native":{"post":{"summary":"iOS Sign in with Apple: exchange an Apple identity token (+ SHA-256 of the raw nonce) for a Preboard session token. Returns {session_token, expires_at}; send it as Authorization: Bearer on /api/account calls. Accounts are optional.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"identity_token":"<Apple JWT>","nonce_sha256":"<hex>","full_name":"optional"}}}},"parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/auth/logout":{"post":{"summary":"End the current session (cookie or Bearer token).","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/account":{"get":{"summary":"Current account: {signed_in:false} when anonymous, otherwise the user's email, preferences and saved trips. Cookie or Bearer session.","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"put":{"summary":"Save trip preferences (validated; unknown keys dropped): home_airport, program, checked_bags, kids, drive_minutes, extra_cushion, flight_type, alerts. Bearer or same-origin cookie.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"prefs":{"home_airport":"CLT","program":"precheck","checked_bags":1,"drive_minutes":25}}}}},"parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"delete":{"summary":"Permanently delete the account: user, sign-in identities, sessions, preferences and trips.","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/account/export":{"get":{"summary":"Download everything stored for this account as JSON.","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/account/trips":{"post":{"summary":"Save a trip to the account (max 50). DELETE /api/account/trips?id=... removes one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"airport":"CLT","departure_time":"2026-10-20T08:30:00-04:00"}}}},"parameters":[],"responses":{"201":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"delete":{"summary":"Delete one saved trip (?id=).","parameters":[{"name":"id","in":"query","required":false,"description":"trip id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/checkin":{"get":{"summary":"Public, stateless: when online check-in opens (and closes, where the airline publishes a cutoff) for a flight, plus the airline's own check-in page. Preboard never checks anyone in. Without parameters, lists the airline rules (each with the date it was verified).","parameters":[{"name":"carrier","in":"query","required":false,"description":"2-character IATA airline code, e.g. DL","schema":{"type":"string"}},{"name":"departure_time","in":"query","required":false,"description":"ISO 8601 with offset","schema":{"type":"string"}},{"name":"flight_type","in":"query","required":false,"description":"domestic | international","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckIn"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/me/trips":{"get":{"summary":"The traveler's upcoming trips with check-in windows. Authorization: Bearer pba_<personal agent token> (scope trips:read). Off (404) until the trips/agent API is enabled. 60 req/min per token.","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"post":{"summary":"Save a trip (scope trips:write). The same flight on the same day updates the existing trip. No names, confirmation codes or airline logins are accepted. Returns 201 saved / 200 updated with the check-in window.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"airport":"ATL","departure_time":"2026-11-20T08:30:00-05:00","carrier":"DL","number":"1234","destination":"LAX","program":"precheck","checked_bags":1}}}},"parameters":[],"responses":{"201":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/me/trips/{tripId}":{"get":{"summary":"One trip with a fresh leave-by plan (security labeled Live / Typical / Estimated as in /leave-by) and its check-in window (scope trips:read).","parameters":[{"name":"tripId","in":"path","required":true,"description":"trip id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"delete":{"summary":"Delete a trip (scope trips:write).","parameters":[{"name":"tripId","in":"path","required":true,"description":"trip id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/v1/me/trips/import":{"post":{"summary":"Push a confirmation email the user's agent already has (raw .eml, HTML or text) or .ics contents. Reads schema.org FlightReservation JSON-LD, then microdata, then text. save:false (default, trips:read) previews; save:true (trips:write) saves high/medium-confidence legs and removes cancelled ones. Only airline, flight number, airports and times are kept; the email is never stored. Max ~1 MB.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"email":"<raw RFC 822 message or HTML>","save":false,"defaults":{"program":"precheck","checked_bags":1}}}}},"parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/account/agent-tokens":{"get":{"summary":"List the signed-in user's personal agent tokens (never the token values). Off (404) until the trips/agent API is enabled.","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"post":{"summary":"Create a personal agent token for the user's own AI agent. Shown once; only its hash is stored. Body: name, scopes (trips:read, trips:write), days (1-365, default 90). Max 10 active. Same-origin cookie or Bearer session.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"name":"My Claude","scopes":["trips:read","trips:write"],"days":90}}}},"parameters":[],"responses":{"201":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"delete":{"summary":"Revoke a personal agent token (?id=).","parameters":[{"name":"id","in":"query","required":false,"description":"token id","schema":{"type":"string"}}],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/api/account/trip-inbox":{"get":{"summary":"Whether the user has a private trip-forwarding address. Off (404) until email import is enabled.","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"post":{"summary":"Create or rotate the private forwarding address (trips+<alias>@preboard.ai), shown once. Forwarded airline confirmations become trips; the email is not stored.","parameters":[],"responses":{"201":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}},"delete":{"summary":"Delete the forwarding address (mail to it is then dropped).","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/a2a":{"post":{"summary":"A2A (Agent2Agent) JSON-RPC: SendMessage (v1.0) or message/send (v0.3), synchronous. Send one data part {skill, arguments}; skills are the MCP tools. No tasks, no streaming. Card: /.well-known/agent-card.json.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"role":"ROLE_USER","messageId":"1","parts":[{"data":{"skill":"check_in_info","arguments":{"carrier":"DL","departure_time":"2026-11-20T08:30:00-05:00"}}}]}}}}}},"parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}},"/.well-known/agent-card.json":{"get":{"summary":"A2A v1.0 Agent Card (also at /.well-known/agent.json).","parameters":[],"responses":{"200":{"description":"JSON"},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found"},"429":{"description":"Rate limited (see Retry-After)"}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"Lane":{"type":"object","properties":{"wait_minutes":{"type":"number","description":"Minutes; may be fractional"},"label":{"type":"string","enum":["Live","Traveler reports"]},"source":{"type":"string"},"updated_at":{"type":"string","format":"date-time"},"age_minutes":{"type":"integer"}}},"Checkpoint":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"terminal":{"type":"string","nullable":true},"lanes":{"type":"object","properties":{"standard":{"$ref":"#/components/schemas/Lane"},"precheck":{"$ref":"#/components/schemas/Lane"},"clear":{"$ref":"#/components/schemas/Lane"}}}}},"Coverage":{"type":"object","properties":{"fresh_window_minutes":{"type":"integer"},"total_airports":{"type":"integer"},"airports_with_live_data":{"type":"integer"},"airports_without_live_data":{"type":"array","items":{"type":"string"}},"generated_at":{"type":"string","format":"date-time"}}},"LeaveBy":{"type":"object","properties":{"security":{"type":"object","properties":{"lane":{"type":"string"},"minutes":{"type":"integer"},"label":{"type":"string","enum":["Live","Traveler reports","Typical","Predicted","Estimated"]},"note":{"type":"string"},"live_now_minutes":{"type":"integer","nullable":true,"description":"Current reading NOT used because security is more than 2 h away"},"live_now_label":{"type":"string","nullable":true}}},"steps":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"minutes":{"type":"integer"},"label_type":{"type":"string","enum":["Live","Traveler reports","Typical","Predicted","Estimated"]}}}},"airport":{"type":"string"},"departure_time":{"type":"string"},"leave_home_at":{"type":"string","nullable":true},"arrive_at_airport_by":{"type":"string"},"bag_drop_cutoff":{"type":"string","nullable":true},"total_minutes":{"type":"integer"},"warnings":{"type":"array","items":{"type":"string"}},"data_notes":{"type":"array","items":{"type":"string"}},"disclaimer":{"type":"string"},"prediction_id":{"type":"string","nullable":true},"arrive_by_options":{"type":"array","items":{"type":"object","properties":{"on_time_probability":{"type":"number"},"extra_minutes":{"type":"integer"},"arrive_at_airport_by":{"type":"string"},"basis":{"type":"string","enum":["calibrated","assumed"]},"note":{"type":"string"}}}},"trust":{"$ref":"#/components/schemas/Trust"}}},"Trust":{"type":"object","properties":{"confidence":{"type":"string","enum":["high","medium","low","none"]},"basis":{"type":"string"},"data_age_minutes":{"type":"integer","nullable":true},"abstain":{"type":"boolean"},"agent_guidance":{"type":"string"},"track_record":{"type":"object","nullable":true,"properties":{"n":{"type":"integer"},"published":{"type":"boolean"},"mae_minutes":{"type":"number","nullable":true},"within_5_min_pct":{"type":"number","nullable":true},"within_10_min_pct":{"type":"number","nullable":true},"note":{"type":"string"}}}}},"CheckIn":{"type":"object","properties":{"carrier":{"type":"string","nullable":true},"airline":{"type":"string","nullable":true},"status":{"type":"string","enum":["not_yet_open","open","closed","departed","not_operating","unknown_airline"]},"opens_at":{"type":"string","format":"date-time","nullable":true},"closes_at":{"type":"string","format":"date-time","nullable":true,"description":"null when the airline publishes no single online cutoff"},"url":{"type":"string","nullable":true,"description":"The airline's own check-in page"},"url_verified":{"type":"string","enum":["fetched","airline-page"],"nullable":true},"automated":{"type":"boolean","enum":[false],"description":"Preboard never checks anyone in"},"remind_at":{"type":"string","format":"date-time","nullable":true},"notes":{"type":"array","items":{"type":"string"}},"source":{"type":"string","nullable":true},"verified_on":{"type":"string"}}},"DeviceRegistration":{"type":"object","required":["device_id","device_secret"],"properties":{"device_id":{"type":"string","format":"uuid"},"device_secret":{"type":"string","description":"Shown once. Send as Bearer token to update/delete."}}}}}}