# StaffTraveler API captures Real request/response pairs captured from the iOS app `com.stafftraveler.webview` v3.12.0 (build 1880000346) against `api.stafftraveler.com` on 2026-04-22. All captures are by user `3NNPesQMiMRNYnPmuQzh6w2YKyh1` (`stafftraveler@treymail.com`, WN/Southwest, airline ST code `st_SWA`). ## Auth All command-API calls are authenticated with: ``` Authorization: Bearer ``` Token issued by `securetoken.google.com/stafftraveler-prod`, `aud=stafftraveler-prod`, ~1h TTL. The captured tokens are already expired — do not try to reuse them. To get a new one, sign in via Firebase Auth REST against API key `AIzaSyC2zG6ArnguzzdWsLYV1qjQznma0zl1Q0s` (needs `X-Android-Package` + `X-Android-Cert` headers or iOS SDK). Only standard headers are sent. No App Check. No SSL pinning. No device/client signing beyond the ID token. ## Endpoint path pattern ``` POST https://api.stafftraveler.com/v1/commands/user/ ``` Note the `user/` segment — the `api_commands_url` Remote Config value is `https://api.stafftraveler.com/v1/commands/user`, and the command name is appended with `/`. I initially documented this as `/v1/commands/` which is wrong. ## Files ### `searchFlightsByRoute_DFW-LAS_*.json` DFW→LAS direct-only search for 2026-04-22. Request body: ```json { "originIata": "DFW", "destinationIata": "LAS", "dates": ["2026-04-22"], "maxConnections": 0, "allowNearbyDepartures": false, "allowNearbyArrivals": false, "payloadType": "passenger", "includeMultipleCarriers": false } ``` Response: `{payload: {directFlights: [...], connectingFlights: [...], numberOfDiscardedFlights, messages, settingsFilteredCount}}`. Each flight carries three IDs (`id`, `id_v2`, `id_v3`), airline info, local/UTC times, equipment, and a `seatsByClass` **aircraft configuration** (NOT current availability — that's a different read path entirely). Corrections vs the api_doc's initial guesses: - `stops` → actually `maxConnections` - `payloadType: "route"` → actually `"passenger"` - No `origin`/`destination`/`date` fields — only the `*Iata` variants + `dates` array. ### `createLoadsRequests_AA2178_*.json` Request loads for AA2178 DFW→LAS 2026-04-22. **Request body** is a bare JSON array of flight objects (no outer wrapper). Each flight is the complete flight object from the search response with one added field: `isPriorityRequest: bool`. **Response shape:** ```json { "payload": { "numberOfRequests": 0, "numberOfRequestsPaidFor": 0, "numberOfRequestsPaidForPriority": 0, "hasSufficientCredits": true, "verifiedRequests": [ { "flightId": "AA_2178_DFW_2026_04_22", "isExisting": true, "isDuplicateForUser": true, "isRequestUpdateForUser": false, "hasDeparted": false, "isCancelled": false, "isPriorityRequest": false, "hasRecentLoads": false, "scheduledFlight": { /* full flight obj */ } } ] } } ``` ### Dedup behavior (important) On this capture, `numberOfRequests: 0` and `isDuplicateForUser: true` — the server recognized this user had already requested this flight and **did NOT charge a credit**. Replaying the same `createLoadsRequests` payload is safe (idempotent). This means: - `numberOfRequests` is the count of *newly created* requests in this call. - `isDuplicateForUser` tells you whether the flight already had an open request for this user. - **No credit is spent on duplicates.** Useful for polling the current request state without cost. ### `hasRecentLoads: false` — where loads actually come from `hasRecentLoads` is the flag the app uses to decide whether load data is available for display. It's `false` here because nobody has submitted loads for AA2178 yet. The actual load data (when `hasRecentLoads: true`) does NOT come through the commands API — it arrives via a Firestore realtime listener on a doc that the user's account has permission to read. To find the exact Firestore path when a flight has loads, one more capture is needed: tap a flight that already has loads populated, and grab the `firestore.googleapis.com/.../Listen/channel` traffic. That will show the watched doc path. ## Known error shapes Not captured yet: - What happens when you send an invalid flight ID - What `createLoadsRequests` returns when credits are insufficient (`hasSufficientCredits: false`) - What `numberOfRequests > 0` responses look like (fresh request, credit actually spent) ## Replay safety To replay any of these: 1. Grab a fresh token from the app or by re-signing-in. 2. Swap it into the curl. 3. For `createLoadsRequests`, same payload = dedup-safe. Different payload = may charge credits.