> ## 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.

# Hello OAuth

> Confirm your OAuth access token works against an OAuth-protected route.

# Hello OAuth

**Goal:** Confirm your OAuth access token works and you know what an OAuth 401 looks like.\
**Prep time:** \~1 minute

## What you'll use

* `GET /flynet/v1/users/me/wallets` with `Authorization: Bearer <access_token>`

The subject is resolved from your token, with no member ID in the path. The
token itself comes from the OAuth + PKCE flow — `FlynetOAuth.exchangeCode()`
returns it. See [OAuth](/concepts/oauth) for the full handshake.

## With the SDK

`FlynetMemberClient` carries the bearer token and normalizes auth failures into
a typed `FlynetError` — no header parsing.

```typescript theme={null}
import { FlynetMemberClient, FlynetError } from "@flynetdev/core";

const member = new FlynetMemberClient({ accessToken: process.env.ACCESS_TOKEN! });

try {
  const { wallets } = await member.listWallets();
  console.log(wallets);
} catch (err) {
  if (err instanceof FlynetError) {
    // "unauthorized" = bad/expired token; "insufficient_scope" = missing read:wallets
    console.log(err.kind);
  } else throw err;
}
```

## Under the hood

The raw request, to see what an OAuth 401/403 looks like on the wire:

```typescript theme={null}
const res = await fetch(
  "https://api.staging.blackbird.xyz/flynet/v1/users/me/wallets",
  { headers: { Authorization: `Bearer ${process.env.ACCESS_TOKEN}` } },
);

if (res.status === 401 || res.status === 403) {
  console.log(res.status, res.headers.get("www-authenticate"));
} else {
  console.log(res.status, await res.json());
}
```

```bash theme={null}
curl -i "https://api.staging.blackbird.xyz/flynet/v1/users/me/wallets" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

`200` returns `{ "wallets": [...] }` with one MEMBERSHIP and one
SPENDING wallet. `401` returns an empty body with a
`WWW-Authenticate: Bearer` header; the cause sits in that header,
not in JSON. A `403` means your token is valid but lacks the
`read:wallets` scope this route requires (`error="insufficient_scope"`).

<Warning>
  Don't try to parse JSON on an OAuth 401/403; the body is empty.
  Inspect the `WWW-Authenticate` header for the reason, such as
  `error="invalid_token"` or `error="insufficient_scope"`.
</Warning>

**Next:** [Fetch a restaurant list](/recipes/appetizers/restaurant-list): verify the API-key side of the credential model.
