# airbnb.com — Airbnb (Hosting)

Host schedule and money: reservations table, per-listing earnings, booking
stats, payout rows — via Airbnb's undocumented persisted-query GraphQL API.

## Auth

agent-browser session with restore:

```bash
agent-browser --session <key> --restore open --headed https://www.airbnb.com/login?redirect_url=%2Fhosting
# human logs in (email/password, Google SSO, or Apple) — no AI in the auth loop
```

Session TTL ~24h. Freshness probe: `IsHostQuery` → `viewer.user` present.

## Why this is a browser recipe, not a curl recipe

Airbnb gates payout rows behind a **payments SCA airlock** (`payments_sca_v2`
— OTP + security question). The airlock's completion state lives in the
browser session and **cannot be replayed from an exported cookie header** —
raw replay gets HTTP 420 `client_error_info.airlock`. So the recipe runs its
GraphQL fetches *inside the page* via `eval`, carrying the full session state.

## Data tiers

| Op | Tier | Requires |
|---|---|---|
| `reservations` | SCA-free | session only |
| `listing-earnings` | SCA-free | session only |
| `booking-stats` | SCA-free | session only |
| `payouts` | **SCA-gated** | human completes 2-step check in browser once |

## The GraphQL bit

`GET /api/v3/<OperationName>/<sha256Hash>?operationName=...&locale=en&currency=EUR&variables=<json>&extensions=<persistedQuery>`

Persisted-query hashes are captured from real dashboard traffic (no
introspection). The schema subset is hand-maintained from live HAR recordings;
gql.tada gives typed results. Types are inference-only — runtime sends
operationName + hash, never the document.
