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

# TypeScript client

> A single typed file that handles both auth schemes and the API's error envelopes.

# TypeScript client

A minimal, dependency-free TypeScript client. Drop it in, import it,
ship. Handles OAuth bearer and API key auth, discriminates the three
partner error envelopes documented in [Pagination + errors](/concepts/pagination-errors)
(plus the empty-body OAuth 401/403 and the OAuth token-endpoint shape),
and throws a typed `FlynetError` you can match on `status` and `code`.

[**Download `flynet.ts`**](/snippets/flynet.ts): \~220 lines, no
dependencies, MIT-style use-it-however.

## Why use it

Hand-rolling the basics costs \~30 to 60 minutes before you write any business
logic:

| Without `flynet.ts`                               | With `flynet.ts`                                  |
| ------------------------------------------------- | ------------------------------------------------- |
| Build a `fetch` wrapper per auth scheme           | `flynet({ credentials })` per scheme              |
| Discriminate the error envelopes by hand          | `FlynetError` with typed `.code`                  |
| Parse `WWW-Authenticate` for OAuth 401/403 causes | Done; reason surfaces as `err.code`               |
| Track which routes need which header              | Methods only accept the right credential's client |

## What's inside

```ts theme={null}
// Two credentials, one discriminated union
export type FlynetCredentials =
  | { type: "oauth"; accessToken: string }
  | { type: "apikey"; apiKey: string };

// One client factory
export function flynet(opts: FlynetClientOptions): {
  request: <T>(path: string, init?: RequestInit) => Promise<T>;

  // Discovery (API key only)
  listRestaurants, getRestaurant, listRestaurantLocations,
  listLocations, getLocationOpenHours,

  // Member context (OAuth only): subject resolved from the token
  getMe, getMyStatus, getMyWallets, getMyTags, getMyCheckIns,
  listMemberships, listCheckIns,

  // Payments (OAuth + merchant_id)
  createPaymentIntent,
};

// One typed error class, with autocomplete on known codes
export class FlynetError extends Error {
  readonly status: number;
  readonly code?: FlynetErrorCode;  // "MISSING_API_KEY" | "invalid_token" | "payment0030" | ...
  readonly raw: unknown;
}

// Optional helper: decode the member UUID from the token (sub claim)
export function getAuthenticatedUserId(accessToken: string): string;
```

## Configure

Most apps end up with both schemes in the same codebase. The file lets
you spin up one client per scheme and never mix them up:

```ts theme={null}
import { flynet, FlynetError } from "./flynet";

const discovery = flynet({
  credentials: { type: "apikey", apiKey: process.env.API_KEY! },
});

const member = flynet({
  credentials: { type: "oauth", accessToken: userAccessToken },
});
```

`baseUrl` defaults to staging. Override when shipping to production:

```ts theme={null}
const discovery = flynet({
  baseUrl: "https://api.blackbird.xyz/flynet/v1",
  credentials: { type: "apikey", apiKey: process.env.API_KEY! },
});
```

## Use

```ts theme={null}
const restaurants = await discovery.listRestaurants({ page: 0, page_size: 20 });
const wallets = await member.getMyWallets();
const recentVisits = await member.getMyCheckIns({
  sort_order: "desc",
  page_size: 25,
});

console.log(restaurants.restaurants.length, wallets.wallets.length, recentVisits.check_ins.length);
```

## Handle errors

`FlynetError` exposes a typed `.code`, so you can switch on the codes
in [Debugging](/resources/debugging):

```ts theme={null}
try {
  const wallets = await member.getMyWallets();
} catch (err) {
  if (!(err instanceof FlynetError)) throw err;

  switch (err.code) {
    case "invalid_token":
      // expired or malformed: refresh and retry, then re-auth if that fails
      await refreshAccessToken();
      return retry();
    case "insufficient_scope":
      // token lacks read:wallets: re-authorize with the right scope set
      return reauth();
    case "resource_not_found":
      // no matching resource (e.g. no active status for the member)
      return notFoundUI();
    default:
      console.error(err.status, err.code, err.message);
      throw err;
  }
}
```

## Identify the authenticated member

`/users/me/*` resolves the subject from the token, so you usually
don't need the UUID; `member.getMe()` returns the authenticated
member directly. When you do need the UUID for your own state, the
helper decodes it from the access token's `sub` claim, the same pattern
documented in [OAuth → Identify the authenticated member](/concepts/oauth#identify-the-authenticated-member):

```ts theme={null}
import { getAuthenticatedUserId } from "./flynet";

const me = await member.getMe();        // full profile, no UUID needed
const userId = getAuthenticatedUserId(accessToken);  // UUID, if you need it
```

Decode once when you receive a fresh token; cache alongside the token
in memory; re-decode on refresh.

## What's not included

`flynet.ts` is starter code, not a full SDK. Out of scope:

| Feature                                                             | Where to find it                                                                    |
| ------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| 401 → refresh → retry wrapper                                       | [OAuth Step 3](/concepts/oauth#step-3---make-api-calls): the `callApi` wrapper      |
| Refresh-token race-condition handling (singleton in-flight refresh) | Add yourself; see [OAuth Step 4](/concepts/oauth#step-4---refresh) for the contract |
| Webhook verification                                                | Webhooks ship post-launch; see [FAQ](/resources/faq)                                |
| Per-method response typing (vs `unknown`)                           | Generate from the OpenAPI spec; next section                                        |

## Generated types

For exhaustively-typed responses, generate from the OpenAPI spec and
swap `unknown` for the generated types:

```bash theme={null}
npx openapi-typescript https://flynet-dev-portal.mintlify.app/api-reference/openapi.yaml \
  -o flynet-types.ts
```

Then adjust `flynet.ts` return types from `unknown` to `paths["…"]["get"]["responses"]["200"]…`.
Stretch into a full SDK at your own pace. The file is yours.

## Maintenance

`flynet.ts` is a reference implementation, not a maintained library. We
update it when a breaking API change ships; track those in
[Changelog](/resources/changelog). If the snippet drifts from the live
API, file an issue via [Support](/resources/support).
