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

# TypeScript SDK

Typed clients for all Immutable APIs and services.

> [!NOTE]
>
> **Who is this for?** Web developers and TypeScript developers looking to build web3 applications and integrate blockchain features seamlessly.

## Packages

Install only what you need:

| Package | Purpose | Install |
| - | - | - |
| `@imtbl/auth` | Authentication & sessions | `npm i @imtbl/auth` |
| `@imtbl/wallet` | Embedded wallets & transactions | `npm i @imtbl/wallet` |
| `@imtbl/auth-next-server` | Next.js server-side auth (Auth.js v5) | `npm i @imtbl/auth-next-server` |
| `@imtbl/auth-next-client` | Next.js client-side hooks & components | `npm i @imtbl/auth-next-client` |
| `@imtbl/orderbook` | NFT trading | `npm i @imtbl/orderbook` |
| `@imtbl/blockchain-data` | On-chain data queries (Indexer) | `npm i @imtbl/blockchain-data` |
| `@imtbl/minting-backend` | Server-side minting | `npm i @imtbl/minting-backend` |
| `@imtbl/contracts` | Smart contract ABIs & types | `npm i @imtbl/contracts` |
| `@imtbl/webhook` | Webhook signature validation | `npm i @imtbl/webhook` |
| `@imtbl/config` | Environment configuration | `npm i @imtbl/config` |

> [!NOTE]
>
> Install individual packages instead of `@imtbl/sdk` for smaller bundles and better tree-shaking.

## Installation

> [tab] npm
>
> ```bash theme={null}
> npm install @imtbl/sdk
> ```

> [tab] yarn
>
> ```bash theme={null}
> yarn add @imtbl/sdk
> ```

> [tab] pnpm
>
> ```bash theme={null}
> pnpm add @imtbl/sdk
> ```

Or install individual packages for smaller bundles:

```bash theme={null}
npm install @imtbl/auth @imtbl/wallet @imtbl/config
```

## Framework Setup

> [tab] Next.js (App Router)
>
> Use `@imtbl/auth-next-server` and `@imtbl/auth-next-client` for full Next.js integration with server-side session management, automatic token refresh, and route protection.
>
> > [!NOTE]
> >
> > These packages require Next.js 14+ with the App Router. For Pages Router or other React frameworks, use `@imtbl/auth` directly.
>
> ### Prerequisites
>
> * **Client ID** from your [Passport client in Hub](https://docs.immutable.com/docs/products/passport/clients.md)
> * Next.js 14 or 15 with App Router
> * An `AUTH_SECRET` environment variable (any random string, minimum 32 characters)
>
> ### Installation
>
> ```bash theme={null}
> npm install @imtbl/auth-next-server @imtbl/auth-next-client next-auth@5
> ```
>
> ### Setup
>
> **1. Create auth configuration**
>
> Create your auth configuration:
>
> ```typescript theme={null}
> // lib/auth.ts
> import { NextAuth, createAuthConfig } from "@imtbl/auth-next-server";
>
> export const { handlers, auth, signIn, signOut } = NextAuth({
>   ...createAuthConfig({
>     clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
>     redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
>   }),
>   secret: process.env.AUTH_SECRET,
>   trustHost: true,
> });
> ```
>
> Create the API route handler:
>
> ```typescript theme={null}
> // app/api/auth/[...nextauth]/route.ts
> import { handlers } from "@/lib/auth";
>
> export const { GET, POST } = handlers;
> ```
>
> Wrap your app with `SessionProvider`:
>
> ```tsx theme={null}
> // app/layout.tsx
> import { SessionProvider } from "next-auth/react";
>
> export default function RootLayout({ children }: { children: React.ReactNode }) {
>   return (
>     <html lang="en">
>       <body>
>         <SessionProvider>{children}</SessionProvider>
>       </body>
>     </html>
>   );
> }
> ```
>
> Set environment variables:
>
> ```env theme={null}
> # .env.local
> NEXT_PUBLIC_IMMUTABLE_CLIENT_ID=your_client_id
> NEXT_PUBLIC_BASE_URL=http://localhost:3000
> AUTH_SECRET=your-secret-key-min-32-characters
> ```
>
> **2. Create a callback page**
>
> Handle the OAuth redirect after login.
>
> ```tsx theme={null}
> // app/callback/page.tsx
> 'use client';
>
> import { CallbackPage, type ImmutableAuthConfig } from '@imtbl/auth-next-client';
>
> const config: ImmutableAuthConfig = {
>   clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
>   redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
>   audience: 'platform_api',
>   scope: 'openid profile email offline_access transact',
> };
>
> export default function Callback() {
>   return (
>     <CallbackPage
>       config={config}
>       loadingComponent={<p>Completing authentication...</p>}
>     />
>   );
> }
> ```
>
> **3. Set environment variables**
>
> ```env theme={null}
> # .env.local
> NEXT_PUBLIC_IMMUTABLE_CLIENT_ID=your_client_id
> NEXT_PUBLIC_BASE_URL=http://localhost:3000
> AUTH_SECRET=your-secret-key-min-32-characters
> ```
>
> For login, logout, session management, and wallet operations using the client hooks, see the **Next.js** tabs on the [Authentication](https://docs.immutable.com/docs/products/passport/authentication.md) and [Wallet](https://docs.immutable.com/docs/products/passport/wallet.md) pages.

> [tab] Vite
>
> ```bash theme={null}
> npm install @imtbl/auth @imtbl/wallet
> ```
>
> ### Initialization
>
> ```typescript theme={null}
> // src/lib/immutable.ts
> import { Auth } from '@imtbl/auth';
> import { connectWallet, ZkEvmProvider } from '@imtbl/wallet';
>
> export const auth = new Auth({
>   clientId: import.meta.env.VITE_CLIENT_ID,
>   redirectUri: `${window.location.origin}/callback`,
>   logoutRedirectUri: window.location.origin,
>   audience: 'platform_api',
>   scope: 'openid offline_access email transact',
> });
>
> let providerPromise: Promise<ZkEvmProvider> | null = null;
>
> export function getProvider() {
>   if (!providerPromise) {
>     providerPromise = connectWallet({ auth });
>   }
>   return providerPromise;
> }
> ```
>
> ### Environment Variables
>
> ```env theme={null}
> VITE_CLIENT_ID=your_client_id
> ```
>
> ### Vite Configuration (Required)
>
> > [!WARNING]
> >
> > Vite requires Node.js polyfills for the SDK. Without this configuration, builds will fail.
>
> ```bash theme={null}
> npm install --save-dev vite-plugin-node-polyfills
> ```
>
> ```typescript theme={null}
> // vite.config.ts
> import { defineConfig } from 'vite';
> import { nodePolyfills } from 'vite-plugin-node-polyfills';
>
> export default defineConfig({
>   plugins: [
>     nodePolyfills()
>   ]
> });
> ```

> [tab] wagmi Integration
>
> Use Passport with wagmi for React hooks:
>
> ```bash theme={null}
> npm install wagmi @imtbl/auth @imtbl/wallet
> ```
>
> ### Configuration
>
> ```typescript theme={null}
> import { createConfig, http } from 'wagmi';
> import { immutableZkEvmTestnet } from 'wagmi/chains';
> import { injected } from 'wagmi/connectors';
>
> export const wagmiConfig = createConfig({
>   chains: [immutableZkEvmTestnet],
>   connectors: [injected()],
>   transports: {
>     [immutableZkEvmTestnet.id]: http(),
>   },
> });
> ```
>
> ### Component Example
>
> ```tsx theme={null}
> import { useAccount, useConnect } from 'wagmi';
>
> function WalletButton() {
>   const { address, isConnected } = useAccount();
>   const { connect, connectors } = useConnect();
>
>   if (isConnected) {
>     return <span>{address?.slice(0, 8)}...</span>;
>   }
>
>   return (
>     <button onClick={() => connect({ connector: connectors[0] })}>
>       Connect
>     </button>
>   );
> }
> ```

## Next.js Configuration

### `createAuthConfig`

Creates an Auth.js v5 configuration object for Immutable authentication.

| Option | Type | Required | Default |
| - | - | - | - |
| `clientId` | `string` | Yes (when config provided) | Sandbox client ID |
| `redirectUri` | `string` | Yes (when config provided) | `origin + '/callback'` |
| `audience` | `string` | No | `"platform_api"` |
| `scope` | `string` | No | `"openid profile email offline_access transact"` |
| `authenticationDomain` | `string` | No | `"https://auth.immutable.com"` |

When called with no arguments, `createAuthConfig()` uses sandbox defaults for quick prototyping.

> [!WARNING]
>
> Zero-config mode uses a shared public sandbox client ID. For production, always use your own client ID from [Immutable Hub](https://hub.immutable.com).

### Extending the configuration

You can spread the base config and add any Auth.js options. This is the pattern used in the Passport sample app to support multiple environments:

```typescript theme={null}
import { NextAuth, createAuthConfig } from "@imtbl/auth-next-server";

export const { handlers, auth, signIn, signOut } = NextAuth({
  ...createAuthConfig({
    clientId: process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!,
    redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/callback`,
  }),
  secret: process.env.AUTH_SECRET,
  trustHost: true,
  basePath: "/api/auth",
});
```

## Next.js Server Utilities

Use these in Server Components and Server Actions to handle authentication on the server.

### `getAuthProps`

Pass authentication state to a Client Component when data fetching happens client-side.

```typescript theme={null}
// app/dashboard/page.tsx
import { auth } from "@/lib/auth";
import { getAuthProps } from "@imtbl/auth-next-server";
import { redirect } from "next/navigation";
import { DashboardClient } from "./DashboardClient";

export default async function DashboardPage() {
  const authProps = await getAuthProps(auth);

  if (authProps.authError) {
    redirect("/login");
  }

  return <DashboardClient {...authProps} />;
}
```

### `getAuthenticatedData`

Fetch data server-side for faster initial loads, with automatic client-side fallback when the token is expired.

```typescript theme={null}
// app/profile/page.tsx
import { auth } from "@/lib/auth";
import { getAuthenticatedData } from "@imtbl/auth-next-server";
import { redirect } from "next/navigation";

async function fetchProfile(accessToken: string) {
  const res = await fetch("https://api.immutable.com/v1/user/profile", {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  return res.json();
}

export default async function ProfilePage() {
  const result = await getAuthenticatedData(auth, fetchProfile);

  if (result.authError) redirect("/login");

  return <ProfileClient {...result} />;
}
```

### `createProtectedFetchers`

Define auth error handling once and reuse it across all protected pages.

```typescript theme={null}
// lib/protected.ts
import { auth } from "@/lib/auth";
import { createProtectedFetchers } from "@imtbl/auth-next-server";
import { redirect } from "next/navigation";

export const { getAuthProps, getData } = createProtectedFetchers(
  auth,
  (error) => redirect(`/login?error=${error}`),
);
```

```typescript theme={null}
// app/settings/page.tsx
import { getAuthProps } from "@/lib/protected";

export default async function SettingsPage() {
  const authProps = await getAuthProps();
  return <SettingsClient {...authProps} />;
}
```

### `getValidSession`

Get fine-grained control over different authentication states.

```typescript theme={null}
import { auth } from "@/lib/auth";
import { getValidSession } from "@imtbl/auth-next-server";

export default async function AccountPage() {
  const result = await getValidSession(auth);

  switch (result.status) {
    case "authenticated":
      return <FullAccountPage session={result.session} />;
    case "token_expired":
      return <AccountPageSkeleton session={result.session} />;
    case "unauthenticated":
      return <LoginPrompt />;
    case "error":
      return <AuthErrorPage error={result.error} />;
  }
}
```

## Next.js Route Protection

### Middleware

Protect entire sections of your app at the routing level before pages render. Use middleware when you have groups of pages that all require authentication (such as `/dashboard/*` or `/settings/*`) and you want to redirect unauthenticated users before any page code runs.

> [!NOTE]
>
> Do not use middleware for pages that show different content for authenticated vs unauthenticated users, or for public pages with optional authenticated features. Use page-level checks with `getAuthProps` or `getValidSession` instead.

```typescript theme={null}
// middleware.ts
import { createAuthMiddleware } from "@imtbl/auth-next-server";
import { auth } from "@/lib/auth";

export default createAuthMiddleware(auth, {
  loginUrl: "/login",
  publicPaths: ["/", "/about", "/api/public"],
});

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
```

| Option | Type | Description |
| - | - | - |
| `loginUrl` | `string` | Redirect target for unauthenticated users (default: `"/login"`) |
| `protectedPaths` | `(string \| RegExp)[]` | Paths that require authentication |
| `publicPaths` | `(string \| RegExp)[]` | Paths that skip authentication (takes precedence) |

### Protected API Routes

Use `withAuth` to protect individual Route Handlers or Server Actions.

```typescript theme={null}
// app/api/user/inventory/route.ts
import { auth } from "@/lib/auth";
import { withAuth } from "@imtbl/auth-next-server";
import { NextResponse } from "next/server";

export const GET = withAuth(auth, async (session, request) => {
  const inventory = await fetchUserInventory(session.accessToken);
  return NextResponse.json(inventory);
});
```

```typescript theme={null}
// app/actions/transfer.ts
"use server";

import { auth } from "@/lib/auth";
import { withAuth } from "@imtbl/auth-next-server";

export const transferAsset = withAuth(auth, async (session, formData: FormData) => {
  const assetId = formData.get("assetId") as string;
  const toAddress = formData.get("toAddress") as string;

  return await executeTransfer({
    from: session.user.sub,
    to: toAddress,
    assetId,
    accessToken: session.accessToken,
  });
});
```

> [!NOTE]
>
> Inside `withAuth` handlers, the `session` object includes `accessToken` directly since these run server-side. This is different from the client-side `useImmutableSession` hook where you must use `getAccessToken()`.

## Next.js Session Type Reference

The server-side session (available in `withAuth` handlers and `getAuthenticatedData` fetchers) includes the following fields:

| Field | Type | Description |
| - | - | - |
| `user.sub` | `string` | Immutable user ID |
| `user.email` | `string?` | User's email address |
| `user.nickname` | `string?` | User's display name |
| `accessToken` | `string` | Current access token (server-side only) |
| `refreshToken` | `string?` | Refresh token |
| `accessTokenExpires` | `number` | Token expiry timestamp (ms) |
| `zkEvm.ethAddress` | `string?` | zkEVM wallet address |
| `zkEvm.userAdminAddress` | `string?` | Admin wallet address |
| `error` | `string?` | `"TokenExpired"` or `"RefreshTokenError"` |

> [!WARNING]
>
> The client-side session from `useImmutableSession` intentionally omits `accessToken` to prevent use of stale tokens. Use `getAccessToken()` for API calls or `getUser()` for wallet integration.

> [!NOTE]
>
> The `idToken` is not stored in the session cookie to stay within CDN header size limits. On the client, `@imtbl/auth-next-client` persists it in `localStorage` so wallet operations can access it via `getUser()`.

## Next.js Exported Utilities

The server package exports low-level utilities for manual token handling:

```typescript theme={null}
import {
  isTokenExpired,
  refreshAccessToken,
  extractZkEvmFromIdToken,
} from "@imtbl/auth-next-server";
```

| Utility | Description |
| - | - |
| `isTokenExpired(expiresAt)` | Check if an access token has expired |
| `refreshAccessToken(token)` | Manually refresh tokens using a refresh token |
| `extractZkEvmFromIdToken(idToken)` | Extract zkEVM claims (ethAddress, userAdminAddress) from an ID token |

## Environment Configuration

| Environment | Chain | API Base |
| - | - | - |
| `SANDBOX` | Immutable Testnet | `api.sandbox.immutable.com` |
| `PRODUCTION` | Immutable Mainnet | `api.immutable.com` |

## Next Steps

### Getting Started

> [card] [Configure Your Project](https://hub.immutable.com)
>
> Set up credentials and OAuth client in Immutable Hub

> [card] [Authentication](https://docs.immutable.com/docs/products/passport/authentication.md)
>
> Initialize Passport and implement login

> [card] [Wallet Operations](https://docs.immutable.com/docs/products/passport/wallet.md)
>
> Access wallet addresses and balances

> [card] [Minting API](https://docs.immutable.com/docs/products/asset-contracts/minting-api.md)
>
> Server-side minting for NFTs at scale

### Additional Resources

> [card] [Architecture](https://docs.immutable.com/docs/products/passport/architecture.md)
>
> Understand the security model and smart contract wallets

> [card] [FAQ](https://docs.immutable.com/docs/sdks/typescript/faq.md)
>
> Common questions and troubleshooting

> [card] [GitHub](https://github.com/immutable/ts-immutable-sdk)
>
> SDK source code

> [card] [npm](https://www.npmjs.com/package/@imtbl/sdk)
>
> Package registry


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.