- AirlineLoadService: pass airport DB for timezone-aware date strings, add browser-shaped headers for United, expand JetBlue/Alaska/Emirates signatures to take origin, log/parse fixes for Korean Air. - FlightsApp: build AirlineLoadService with the airport DB and inject it. - JSX: continued WebView-based fetcher work plus updated JSX_NOTES. - Docs: add AIRLINE_INTEGRATION_GUIDE.md, drop the old AIRLINE_API_SPEC.md, add api_docs/ (StaffTraveler reverse-engineering captures + findings). - Scripts: jsx_cdp_probe, jsx_live_monitor, jsx_swift_smoke for JSX protocol exploration. - .gitignore: exclude airlines/ (local-only APK/IPA reverse-engineering). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.8 KiB
StaffTraveler — URL surface crawl (2026-04-22)
Full enumeration of what is and isn't accessible with a valid user Firebase ID token from the stafftraveler-prod project. Tested as user 3NNPesQMiMRNYnPmuQzh6w2YKyh1 (WN/st_SWA) using fresh tokens from app.stafftraveler.com.
HTTP commands (api.stafftraveler.com/v1/commands/user/<name>)
All 34 commands confirmed via createFirebaseCommand enumeration in the Hermes bundle. Only the user/ prefix exists — tried public/, internal/, admin/, v2/, flights/, loads/, system/, airline/ → all 404.
Read-ish (test with safe payloads):
appActive— heartbeat. Requires{localDateIso: ISO8601, appType: "mobile"|"web"}. Returns{payload: null}.mobile— stub, returns"Command mobile is not implemented".searchFlightsByCode,searchFlightsByRoute— flight search (free, any route).
Everything else is a mutation: createLoadsRequests, deleteLoadsRequest, reopenLoadsRequest, upgradeLoadsRequest, requestLockForLoadsRequest, unlockLoadsRequest, submitLoadsReport, reviseLoadsReport, flagLoadsReport, addAirlineDetails, addComment, addIdentity, addCreditsForPurchase, createTip, deleteUserAccount, disableAllAutoRequestSubscriptions, disableFlightStatusUpdates, enableFlightStatusUpdates, discardHint, omitIdentity, pinFlight, unpinFlight, registerUser, removeComment, reportComment, setAutoRequestSubscription, submitFeedback, unlikeTip, updateUserProfile, appLogout.
api.stafftraveler.com/health returns {message:"ok", serverTime:...} — open.
Firestore collections (firestore.googleapis.com/v1/projects/stafftraveler-prod/databases/(default)/documents)
All reads require Authorization: Bearer <idToken> + X-Firebase-GMPID: 1:628258099825:web:35b20eaab4d441894041d0 + ?key=AIzaSyD82Hiqx-jAbHF8faMGSveqLw8CdX8a-Uc.
Publicly readable, cross-airline (globally listable or get-by-id)
| Path | Content | Size |
|---|---|---|
__system/maintenance |
service maintenance flags | 1 doc |
__system/version |
required client versions (android/ios blacklists) | 1 doc |
__system/supportedCurrencies |
currency support map | 1 doc |
__derived/nonRevAgreementsBySt |
Full interline non-rev agreement matrix: agreements: { st_XXX: [st_YYY, ...], ... } |
300 airline keys — pulled and saved to nonRevAgreementsBySt.json |
airlinesBySt/* |
Airline ST-code directory | 300+ docs — pulled to airlinesBySt_full.json |
airlines/* |
Airline IATA directory | publicly readable per-doc (tested AA) |
airportsByIata/* |
Airport IATA directory | publicly readable per-doc |
airlineNotesBySt/* |
Per-airline non-rev notes | publicly readable per-doc |
flightEquipment/* |
Aircraft type directory | publicly readable per-doc |
conversations/* |
Flight comment threads | 404 for empty (rule allows, doc absent); readable where they exist |
tips/* |
Travel recommendations (restaurants, lounges, etc.) | 5+ docs listable (pagination not tested to completion) |
autoRequestRecords/* |
Global auto-request subscription configs — user uids, trigger schedules, blocked users | 500+ docs listable; flight metadata is NOT in the doc itself (likely derivable from the doc ID hash), but user-activity is |
Airline-scoped (only MY airline == st_SWA)
| Query | Behavior |
|---|---|
trackedFlights where scheduledFlight.airlineStCode == "st_SWA" |
✅ 500+ docs (historic WN load requests, all status=closed) |
trackedFlights where scheduledFlight.airlineStCode == "st_AAL/DAL/UAL/JBU/..." |
❌ 403 — rules scope to own airline |
trackedFlights/{flightId_v3} direct get, same-airline |
✅ readable (even if I'm not the creator) — exposes currentLoadsReportId, reportAuthorList, seatsAvailabilityScore |
trackedFlights/{flightId_v3} direct get, other airline |
❌ 403 |
Strictly user-scoped (me only)
| Path | Notes |
|---|---|
users/{MY uid} |
My profile. Other uids → 403. |
userCredits/{MY uid}, userHints/{MY uid}, userRequestCounters/{MY uid}, userPriorityRequestCounters/{MY uid}, userMetrics/{MY uid}, userAchievements/{MY uid}, userFlightSearchHistory/{MY uid} |
my state only |
users/{MY uid}/pinnedFlights, users/{MY uid}/connectingFlights |
my subcollections only |
derivedLoadsReports where flightId == "<flight I created request on>" |
✅ readable |
derivedLoadsReports/{docId} direct get, my flight |
✅ readable |
derivedLoadsReports where flightId == "<other flight>" |
❌ 403 (cross-checked with direct-get of dVb4zStjhITceArRgLBB — a WN862 load doc I don't own) |
trackedFlights/{flight_I_created}/statusUpdates subcollection |
readable only for flights I own |
Explicitly denied for listing / querying
| Path | Error |
|---|---|
LIST derivedLoadsReports without a matching where-filter |
403 |
LIST trackedFlights without airlineStCode filter |
403 |
runQuery on derivedLoadsReports, trackedFlights, scheduledFlights, flights, flightRecord, userRegistrations, userEmailVerifications, loadsReports (as singular), conversations (list-only, 403) without proper predicates |
403 |
| Collection-group queries on sensitive collections | 403 |
scheduledFlights/{id}, flights/{id}, flightRecord/{id} direct get for ANY flight |
403 |
Other domains on stafftraveler.com (tested unauthenticated GET)
| URL | Response |
|---|---|
app.stafftraveler.com/ |
web app (Next.js); redirects to /login if unauth |
share.stafftraveler.com/ |
200 HTML — share landing page (Next.js). Tried /request/<id>, /flight/<id>, /loads/<id>, /r/<code> → all 404 |
blog.stafftraveler.com/ |
blog (not tested deeply) |
hotels.stafftraveler.com/ |
hotels deals (WebView target) |
carrental.stafftraveler.com/ |
car rentals (WebView target, URL pattern <base>/mobile.html?...) |
support.stafftraveler.com/ |
support/help |
shop.stafftraveler.com/ |
merch |
links.stafftraveler.com/ |
404 not-found page (linktree-style, but empty) |
webhooks.stafftraveler.com/ |
Fastify API; {error:"Not Found"} on /; /request-password-reset returns {error:"Invalid arguments"} on empty body |
stafftraveler.com/r/<code> |
Returns generic HTML (any path) |
images.stafftraveler.com/avatars/* |
Public avatar CDN |
Goal analysis: "find all filled requests"
- For my own airline (WN/
st_SWA): ✅ possible — enumerate alltrackedFlightswithairlineStCode == "st_SWA"andstatus == "closed". 500+ historic records. But actually reading the per-request load data (openSeats.{first,business,economy}.count,staffListing.count) still requires having personally calledcreateLoadsRequestson each flight. - For other airlines: ❌ blocked. The rule
scheduledFlight.airlineStCode == <your airline>is the hard wall. Verified negatively across 9 airline codes (AAL/DAL/UAL/JBU/ACA/KLM/AFR/BAW/DLH). - Community-aggregated filled-request data, cross-airline: not exposed anywhere I found.
autoRequestRecordscomes closest — it shows who's auto-subscribing, not what loads were reported.
The design is intentional: StaffTraveler's value proposition is a credit-gated access to crowd-sourced data on your own airline's routes. The rules don't let you enumerate outside that scope.
Useful artifacts saved
nonRevAgreementsBySt.json— complete 300-airline interline matrixairlinesBySt_full.json— ST code → airline directory (partial if stopped paging)firestore_listen_targets_and_queries.txt— all Listen-channel queries captured from the web clientderivedLoadsReports_AA2178_*.json— single known-working load response capturesearchFlightsByRoute_DFW-LAS_*.json/createLoadsRequests_AA2178_*.json— HTTP command captures