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

# TypeScript SDK

> Install, configure, and authenticate with the Podium TypeScript SDK

## Installation

```bash theme={null}
npm install @podium-sdk/node-sdk
```

The SDK is published as `@podium-sdk/node-sdk` on npm with full TypeScript type definitions.

## Configuration

```typescript theme={null}
import { createPodiumClient } from '@podium-sdk/node-sdk'

const client = createPodiumClient({
  apiKey: process.env.PODIUM_API_KEY
})
```

The SDK auto-detects the environment from your key prefix:

| Key Prefix     | Routes To                                      |
| -------------- | ---------------------------------------------- |
| `podium_test_` | `https://podium-staging.up.railway.app/api/v1` |
| `podium_live_` | `https://api.podium.build/api/v1`              |

### Environment Setup

Add your API key to your `.env` file:

```bash theme={null}
# Get your key from https://app.podium.build/onboarding
PODIUM_API_KEY=podium_test_sk_your_key_here
```

<Warning>
  Never commit API keys to version control. Always load them from environment variables or a secrets manager.
</Warning>

### Custom Base URL

Override the auto-detected base URL for local development or custom deployments:

```typescript theme={null}
const client = createPodiumClient({
  apiKey: process.env.PODIUM_API_KEY,
  baseUrl: 'http://localhost:3030/api/v1'
})
```

## Multi-Tenant Context

Every API call is scoped to the organization that owns your API key. You don't need to pass an `organizationId` — the Podium middleware extracts it from your key automatically.

If your organization manages multiple creators, all creator-scoped operations (products, orders, campaigns) are accessible through your single API key:

```typescript theme={null}
const creatorA = await client.merchant.getBySlug({ slug: 'brand-a' })
const creatorB = await client.merchant.getBySlug({ slug: 'brand-b' })
```

## SDK Namespaces

The SDK is organized into namespaces that mirror the API structure:

| Namespace                 | Covers                                                |
| ------------------------- | ----------------------------------------------------- |
| `client.product`          | Product listing, buy-now                              |
| `client.merchantProducts` | Product CRUD, variants, publish lifecycle, analytics  |
| `client.merchant`         | Creator profiles, stats                               |
| `client.merchantOrders`   | Creator order management, shipping labels             |
| `client.user`             | User creation, profile, points, orders, notifications |
| `client.userOrder`        | Checkout (Stripe, Coinbase, embedded wallet)          |
| `client.userOrders`       | Order listing, creation                               |
| `client.campaigns`        | Campaign CRUD, voting, surveys, analytics             |
| `client.nftRewards`       | Reward programs, minting, grants, publishing          |
| `client.userNfTs`         | User reward redemption, earned/redeemable queries     |
| `client.agentic`          | Agentic product feed, checkout sessions               |
| `client.search`           | Product search with filters                           |
| `client.universalSearch`  | Cross-entity search with LLM-powered answers          |
| `client.x402`             | x402 USDC payment processing                          |
| `client.apiKeys`          | API key rotation and management                       |
| `client.organizations`    | Organization settings                                 |

## Basic Usage

```typescript theme={null}
const products = await client.product.list({ page: 1, limit: 20 })

const creator = await client.merchant.getBySlug({ slug: 'clean-beauty-co' })

const user = await client.user.create({
  requestBody: {
    email: 'user@example.com',
    privyId: 'did:privy:abc123'
  }
})

const balance = await client.user.getPoints({ id: user.id })
```

## Pagination

List endpoints return paginated results. Use `page` and `limit` parameters:

```typescript theme={null}
const page1 = await client.product.list({ page: 1, limit: 20 })

const page2 = await client.product.list({ page: 2, limit: 20 })
```

### Pagination Response Shape

All paginated responses include a `pagination` object:

```typescript theme={null}
interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
  };
}
```

### Iterate All Pages

```typescript theme={null}
async function getAllProducts() {
  const products = []
  let page = 1
  let hasMore = true

  while (hasMore) {
    const result = await client.product.list({ page, limit: 100 })
    products.push(...result.data)
    hasMore = result.data.length === 100
    page++
  }

  return products
}
```

## Error Handling

The SDK throws typed errors with HTTP status codes and structured error bodies:

```typescript theme={null}
import { ApiError } from '@podium-sdk/node-sdk'

try {
  const products = await client.product.list({ page: 1 })
} catch (error) {
  if (error instanceof ApiError) {
    console.error(`API Error ${error.status}: ${error.message}`)
    console.error('Response body:', error.body)

    if (error.status === 401) console.log('Invalid API key')
    if (error.status === 429) console.log('Rate limited, retry later')
    if (error.status === 402) console.log('x402 payment required')
  }
}
```

### Error Codes

| Status | Meaning                                               |
| ------ | ----------------------------------------------------- |
| `400`  | Bad request — invalid parameters                      |
| `401`  | Unauthorized — invalid or missing API key             |
| `402`  | Payment required — x402 payment needed                |
| `403`  | Forbidden — insufficient permissions                  |
| `404`  | Not found                                             |
| `409`  | Conflict — duplicate resource                         |
| `422`  | Validation error — request body failed Zod validation |
| `429`  | Rate limited — check `Retry-After` header             |
| `500`  | Server error                                          |

### Validation Errors (422)

Podium uses Zod for request validation. Validation errors include field-level details:

```json theme={null}
{
  "status": 422,
  "error": "Validation Error",
  "details": [
    {
      "path": ["email"],
      "message": "Invalid email",
      "code": "invalid_string"
    },
    {
      "path": ["amount"],
      "message": "Expected number, received string",
      "code": "invalid_type"
    }
  ]
}
```

## Retry and Backoff

The SDK does not include automatic retries. Implement retry logic for transient errors:

```typescript theme={null}
async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  baseDelayMs = 1000
): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error: any) {
      const isRetryable = [429, 500, 502, 503].includes(error.status);
      if (!isRetryable || attempt === maxRetries) throw error;

      const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * 500;
      await new Promise(r => setTimeout(r, delay));
    }
  }
  throw new Error('Unreachable');
}

const products = await withRetry(() =>
  client.product.list({ page: 1 })
)
```

For `429` responses, respect the `Retry-After` header when available.

## Rate Limits

Rate limits are per-API-key and based on your subscription tier:

| Tier             | Calls/Month | Burst Rate |
| ---------------- | ----------- | ---------- |
| Builder (Free)   | 10,000      | 10 req/s   |
| Growth (\$99/mo) | 100,000     | 50 req/s   |
| Pro (\$499/mo)   | 1,000,000   | 200 req/s  |
| Enterprise       | Custom      | Custom     |

Rate limit headers are included in every response:

```
X-RateLimit-Limit: 50
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1709827260
```

## SDK Generation

The SDK uses a hybrid generation approach:

1. **OpenAPI spec** generated from Zod schemas via a build script
2. **Service classes** auto-generated via `openapi-typescript-codegen`
3. **Wrapper layer** maps generated services to clean namespaces

The SDK is regenerated from the Podium API's OpenAPI spec whenever the API surface changes. Published versions on npm always reflect the latest API.

## Versioning

The SDK follows semver. Breaking changes increment the major version:

```bash theme={null}
npm install @podium-sdk/node-sdk@latest
```

Check the [Podium API Playground](https://api.podium.build/docs) for the latest endpoint documentation. The playground is always in sync with the production API.
