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

# Fetch offers

> List Passage offers for the signed-in user with OffersGrid or hooks.

After [OAuth](/sdk/oauth-authentication), you can load every sale the current user can see and render them with the SDK - no hand-written HTTP integration for the common case.

## Prerequisites

* `CoinListProvider` with working `getAccessToken` (session established on your backend)
* User signed in: your `getAccessToken` returns a non-null access token after OAuth (see [Set up OAuth authentication](/sdk/oauth-authentication))

## Recommended: OffersGrid

`OffersGrid` uses `useOffers` internally and renders a responsive grid of offer cards with loading, error, and empty states.

```tsx theme={null}
"use client";

import { OffersGrid } from "@coinlist-co/react";

export function OffersSection() {
  return (
    <OffersGrid
      maxColumns={3}
      onOfferClick={(offer) => {
        // Navigate or open your detail route - offer.id is the API offer id
        console.log("Selected offer", offer.id);
      }}
    />
  );
}
```

Optional props:

* **`maxColumns`** - upper bound for the grid when space allows (default `3`)
* **`loading`**, **`error`**, **`emptyState`** - replace default slots with your own React nodes
* **`onOfferClick`** - when set, cards become interactive; omit for read-only lists
* **`data`** - pre-fetched offers from `CoinListServer.fetchOffers()` for SSR. Pass them in to skip the client-side request on first render.

## Lower-level APIs

Open these sections when you need custom layouts or non-React code.

<AccordionGroup>
  <Accordion title="Hook: useOffers">
    Use `useOffers` from `@coinlist-co/react` when you want full control over markup but still prefer reactive loading state.

    The hook exposes `offersState`:

    * **`LOADING`** - provider not ready or request in flight
    * **`ERROR`** - `reason` is `not-authenticated` or `generic-error`
    * **`CONTENT`** - `offers` is every page of offers merged (same data as `fetchOffers`)

    ```tsx theme={null}
    "use client";

    import { useOffers } from "@coinlist-co/react";

    export function CustomOffersLayout() {
      const { offersState } = useOffers();

      if (offersState.type === "LOADING") return <p>Loading…</p>;
      if (offersState.type === "ERROR") return <p>Could not load offers.</p>;

      return (
        <ul>
          {offersState.offers.map((offer) => (
            <li key={offer.id}>{offer.tagline}</li>
          ))}
        </ul>
      );
    }
    ```

    Pass `data` if you have server-fetched offers to hydrate from: `useOffers({ data })`.
  </Accordion>

  <Accordion title="Client: fetchOffers and fetchOffersPage">
    Inside React you usually reach these through `useCoinList()`:

    * **`coinlist.fetchOffers()`** - iterates every paginated page and returns all offers (throws `NotAuthenticatedError` if the user is not logged in)
    * **`coinlist.fetchOffersPage(params)`** - one page of API results when you implement your own pagination UI

    ```tsx theme={null}
    "use client";

    import { useCoinList } from "@coinlist-co/react";
    import { useEffect } from "react";

    export function OffersDebugLogger() {
      const { coinlist, isReady } = useCoinList();

      useEffect(() => {
        if (!isReady) return;
        void coinlist.fetchOffers().then((offers) => {
          console.log(offers.length);
        });
      }, [coinlist, isReady]);

      return null;
    }
    ```

    For imperative, non-hook usage, create a `CoinListClient` with `createCoinListClient` from `@coinlist-co/react`. Server code (Next.js Route Handlers, Server Components) should use the equivalent `CoinListServer.fetchOffers()` from `@coinlist-co/react/server` instead.
  </Accordion>

  <Accordion title="REST: offers endpoints">
    The SDK calls the same API documented in the [API reference](/api-reference). Use raw HTTP only if you are not using React, or you need fields or flows the SDK does not expose yet. You still need a valid Bearer access token from your OAuth session.
  </Accordion>
</AccordionGroup>

## Next step

<Card title="Display offer details" icon="tag" href="/sdk/sale-details" horizontal>
  Load full offer details for an id from the grid or `fetchOffers`.
</Card>
