Skip to main content
This guide covers credentials, installing the SDK, and how it is organized. When you are ready to wire CoinList sign-in, continue with Set up OAuth authentication, then Fetch offers, then Display offer details. Examples use Next.js App Router patterns; you can adapt the same pieces to other React frameworks as long as you have a dedicated backend.
Clone the Partner Demo GitHub repo and follow this tutorial alongside the running example integration. You see it live at partner.coinlist.dev.

Get OAuth credentials

You need values from Passage before you write code:
  • client_id - public identifier for your app
  • redirect_uri - exact callback URL registered with Passage (must match what you pass in the SDK)
  • client_secret - server-only; never expose this to the browser or commit it to frontend bundles
Request access and registration through Support or your Passage contact. Store secrets in server environment variables (for example COINLIST_CLIENT_SECRET), and use NEXT_PUBLIC_ only for values that are safe in the client (typically client_id and redirect_uri).

Install the SDK

Peer dependencies: react and react-dom ^18 || ^19, and viem ^2. React 20 is outside the supported range. The package is ESM-only. Bundlers such as Next.js, Vite and webpack consume it natively. In Node it requires ^20.19.0 || >=22.12.0, the versions that can require() an ES module, so CommonJS code on those versions can still load it. See Package overview for all import paths and the full namespace map.

How the SDK is organized

The SDK splits three ways, in this order: environment, then level, then feature. Learn those three and you can find anything in it.

Level: how much you want to build

Every feature is available at several levels. Lower levels provide more control but require more implementation. Start with L3 components and use L2 or L1 when you need more control.
  1. L3 - React components (highest). You get UI and behavior together, working. You provide the input, plus an optional config object, and you are done. <CheckoutContainer offer optionId chain wallets /> is an entire purchase flow: wallet selection, amount entry, approval, review, confirmation. optionId is the offer option the user chose, and it is required on every checkout: the SDK never picks an option for you. Others: <OffersGridContainer>, <RequirementsChecklistContainer>, <ClaimContainer>, <CoinListSignInCardContainer>.
  2. L2 - React hooks (mid). You get the behavior: data fetching, state, polling, error handling, and the on-chain sequencing. You provide the UI. useOffers() hands you a LOADING / ERROR / CONTENT state machine and you render whatever you like from it; useOndoBuyCheckoutViewModel() runs the whole Ondo flow behind your own markup.
  3. L1 - TypeScript clients (low). You get the logic: typed API calls, domain mapping, and total on-chain flows that return a step-tagged result instead of throwing. You build the state management and the UI. PassageClient in the browser, PassageServer on your backend, no React anywhere. Use it for a non-React app, a custom state layer, or full control.
Below L1 there is no SDK. If you need something the client does not model, call the HTTP API directly. A feature may stop at a rung, but it never skips one: a feature with a component always has the hooks and the namespace beneath it, so you can drop a rung whenever the one above does not fit. What is public, per feature: the root container (L3), the viewmodel behind it and the data hooks it is built from (L2), and the namespaces (L1). The views, steps and sub-screens a container composes are not exported. You either take the SDK’s UI with its behavior, or bring your own UI over the SDK’s state. The exceptions are a few presentational pieces you lay out yourself: CoinListSignInButton, OfferAssetCard and OfferSaleCard. RequirementsChecklistView is also still exported, as a loading placeholder, but is due to be removed, and PassageStyleScope is the styling wrapper every SDK component already puts around itself.

Environment: where code runs

Environment comes first in the split because it is a security boundary, not a convenience: PassageServer holds your client_secret, and it must never reach a browser bundle. That boundary is exactly the three import paths you type. Not every feature is available at every level. L2 and L3 exist only in the browser, because a backend renders nothing. L1 is the only level present everywhere, which is why passage.offers.list() reads the same on your server as in your React tree.

Feature

Inside each level, the code is grouped by feature, and the same features appear at every rung: auth, offers, requirements, and the provider journeys checkout and claim. So passage.requirements (L1), useRequirements() (L2) and <RequirementsChecklistContainer> (L3) are three abstraction levels for the same feature. Checkout and claim are grouped one level deeper, by provider and product - Superstate swap, Ondo buy and sell, Centrifuge buy and sell, CoinList token sale - because every provider’s flow differs in roughly a fifth of its substance. At L1 each provider has its own namespace (passage.superstate, passage.ondo, passage.centrifuge, passage.tokenSale). <CheckoutContainer> and <ClaimContainer> are what save you from caring, so you integrate once. <CheckoutContainer> routes on offer.type (and, for Ondo and Centrifuge, on a side prop). <ClaimContainer> takes one async offer or a list of any mix of them, and each offer’s type decides which provider reads it, with every row in one card.
Recommendation: start with components, drop to hooks when you need custom UI, and reach for the clients when you are outside React or need full control. The HTTP API remains the contract underneath, for bespoke stacks or endpoints the SDK does not wrap yet.

Next step

SDK structure

The patterns every part of the SDK follows: namespaces, containers, hook state, and flows that return instead of throwing. Skippable if you would rather start building.
After OAuth, follow Fetch offers to render OffersGridContainer, then Display offer details for a single offer. Then Requirements for what a user must satisfy before participating, Wallets to plug in your wallet stack, and Building the Checkout flow to render the purchase itself. Use the API reference when you need raw request and response details.