> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flynet.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> From zero to three authenticated calls in five minutes.

**Prep time:** \~5 minutes\
**You need:** `curl`, an OAuth access token, and an API key. (`/users/me/*` resolves the subject from your JWT server-side, so no member ID is needed.)

<Note>
  This is the wire-level tour — three raw calls so you can see exactly what the API returns. When you build, use the TypeScript SDK instead of hand-rolling these requests: [`@flynetdev/core`](/concepts/typescript-client) handles both credentials, OAuth, errors, and pagination, and [`@flynetdev/react`](/recipes/mains/member-dining-app) adds drop-in components. Building with an AI agent? Start at [Build with AI](/build-with-ai/overview).
</Note>

## First course: set credentials

Two credentials are not interchangeable. API-key routes (`/restaurants*`, `/locations*`, and the `/check_ins` venue feed including `/check_ins/{id}`) take the API key. Member-scoped routes (`/users/me/*`, `/payment_intents*`) take an OAuth access token. See [OAuth](/concepts/oauth) for the full flow and [API keys](/concepts/api-keys) for the server-to-server side.

```bash theme={null}
export API_BASE_URL="https://api.staging.blackbird.xyz/flynet/v1"
export API_KEY="fly_test_..."       # from your onboarding email; paste exactly as received
export ACCESS_TOKEN="..."           # from /oauth/token; see OAuth concept
```

<Note>
  **Tasting note** - `/users/me/*` is canonical for member-context calls. The JWT subject is resolved server-side; no UUID parameter needed. Decode the `sub` claim from your access token only if you need the member's UUID for your own state.
</Note>

## Second course: list restaurants

The simplest authenticated call. Pure server-to-server: no member context, no OAuth.

```bash theme={null}
curl -sS "$API_BASE_URL/restaurants?page=0&page_size=5" \
  -H "X-API-Key: $API_KEY"
```

Expected (abbreviated, first restaurant only):

```json theme={null}
{
  "restaurants": [
    {
      "id": "fa6b9307-3051-4246-9d8a-1565efe7a6c9",
      "object": "restaurant",
      "name": "a16z Cafe",
      "cuisine": [],
      "cohort": "qsr",
      "asset": { "preview_1x": "https://...", "web_2x": "https://...", "full_3x": "https://..." }
    }
  ],
  "pagination": { "total_count": 269, "current_page": 0, "next_page": 1, "page_size": 5 }
}
```

`total_count` shifts as the seed list grows. Treat the exact number as illustrative.

A **missing** key returns an empty-body `401` with a `WWW-Authenticate: Bearer` header (reason in the header, not JSON). An **invalid** key returns a JSON `401` with `code: "invalid_api_key"`. Discovery routes reject OAuth bearer tokens either way.

## Main course: fetch your wallets

Switches to OAuth. The subject is resolved from your JWT: `/users/me/wallets` returns the wallets of the token holder, no UUID in the path. Every Blackbird member has a `MEMBERSHIP` wallet and a `SPENDING` wallet, auto-provisioned on their first OAuth completion.

```bash theme={null}
curl -sS "$API_BASE_URL/users/me/wallets" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

Expected:

```json theme={null}
{
  "wallets": [
    { "object": "user_wallet", "wallet_type": "MEMBERSHIP", "address": "0x6441FCaBB1bA6b26301e04beC1147E0fF2ee239b" },
    { "object": "user_wallet", "wallet_type": "SPENDING",   "address": "0xecb241bA29D219a753E37bEE253236e2Fbd1Fa45" }
  ]
}
```

This route needs the `read:wallets` scope. Wrong scope returns `403` with `WWW-Authenticate: Bearer error="insufficient_scope"`: the route exists, your token just isn't authorized for it. A `401` (rather than `403`) returns an **empty body** with a `WWW-Authenticate: Bearer` header: the cause sits in the header, not in JSON.

## Final course: your check-in history

Your own visit history, subject resolved from the JWT. Each row embeds the full `location`, `restaurant`, and `neighborhood`, plus timestamps: one call powers a complete feed row.

```bash theme={null}
curl -sS "$API_BASE_URL/users/me/check_ins?page=0&page_size=10" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

Expected (abbreviated, one row):

```json theme={null}
{
  "check_ins": [
    {
      "id": "{uuid}",
      "object": "check_in",
      "blackbird_pay_enabled": true,
      "created_at": "2026-04-17T14:31:17Z",
      "ended_at": "2026-04-17T16:02:40Z",
      "location": { "object": "location", "name": "Anton's West Village", "restaurant": { "name": "Anton's" }, "neighborhood": { "name": "West Village" } }
    }
  ],
  "pagination": { "total_count": 562, "current_page": 0, "next_page": 1, "page_size": 10 }
}
```

This route needs the `read:user_checkins` scope. Wrong scope returns `403` with `WWW-Authenticate: Bearer error="insufficient_scope"`: the route still exists, your token just isn't authorized for it.

<Note>
  **Tasting note** - Pagination is zero-indexed. Start with `page=0`. Timestamps are ISO 8601 strings.
</Note>

<Note>
  **Tasting note** - The collection route `/check_ins` is an anonymized venue feed (no member identity) and accepts optional filters (`[restaurant, location, created_after, created_before]`), but a bare `GET /check_ins` is valid and returns the full set. Unknown query params are silently ignored: a typo like `restaurants=` instead of `restaurant=` is treated as no filter supplied. `created_after` / `created_before` accept ISO 8601 only.
</Note>

## Staging fixtures

Working staging UUIDs you can paste into the interactive API playground's "Try It" buttons. These resources exist in staging seed data and are stable.

| Resource                        | UUID                                   | Notes                                                                                                                                                                                         |
| ------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| User (Ekow)                     | `13a014d0-31de-474b-89f9-d9a32b0d42b8` | 562 check-ins; 1 `industry` tag; MEMBERSHIP + SPENDING wallets. Member routes resolve the subject from your token (`/users/me/*`), so this UUID is reference only; it's not a path parameter. |
| Restaurant (Anton's)            | `14339db3-2e7a-42c4-aa98-4c0fb18679eb` | 57 check-ins; populated `asset`                                                                                                                                                               |
| Location (Anton's West Village) | `c54a3b6a-c31b-49b4-8af1-2dfb70ff3eec` | 57 check-ins                                                                                                                                                                                  |

Staging data may change without notice. If a UUID stops working, ping [Support](/resources/support).

## Hitting an error?

See [Debugging](/resources/debugging): every observed `error_code` mapped to root cause + fix.

## What's next

<CardGroup cols={2}>
  <Card title="Build with the SDK" icon="code" href="/concepts/typescript-client">
    The same three calls in typed TypeScript — `@flynetdev/core` handles credentials, errors, and pagination for you.
  </Card>

  <Card title="Ship with Claude" icon="rocket" href="/build-with-ai/walkthrough">
    The full path with an AI agent: build on the SDK, handle secrets safely, deploy on Vercel.
  </Card>

  <Card title="Restaurant explorer" icon="map" href="/recipes/mains/restaurant-explorer">
    Chain restaurants → locations → weekly hours for a discovery surface.
  </Card>

  <Card title="OAuth, end-to-end" icon="lock" href="/concepts/oauth">
    The full Token-Mediated Backend flow with PKCE and refresh rotation.
  </Card>
</CardGroup>
