Files
Flights/api_docs/stafftraveler_captures/url_surface_crawl.md
T
Trey t 6005146e75 Airline integration work: AirlineLoadService updates, docs, JSX scripts
- 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>
2026-04-24 23:21:30 -05:00

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 all trackedFlights with airlineStCode == "st_SWA" and status == "closed". 500+ historic records. But actually reading the per-request load data (openSeats.{first,business,economy}.count, staffListing.count) still requires having personally called createLoadsRequests on 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. autoRequestRecords comes 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 matrix
  • airlinesBySt_full.json — ST code → airline directory (partial if stopped paging)
  • firestore_listen_targets_and_queries.txt — all Listen-channel queries captured from the web client
  • derivedLoadsReports_AA2178_*.json — single known-working load response capture
  • searchFlightsByRoute_DFW-LAS_*.json / createLoadsRequests_AA2178_*.json — HTTP command captures