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

# TypeScript

> Use the Dodo Payments TypeScript SDK to create checkout sessions, customers, subscriptions, and usage events from Node.js and other JavaScript runtimes.

The TypeScript SDK gives server-side TypeScript and JavaScript code typed access to the Dodo Payments REST API. It includes type definitions for every request and response, typed errors, automatic retries, timeouts, and auto-pagination.

## Installation

Install the `dodopayments` package with your package manager:

<CodeGroup>
  ```bash npm theme={null}
  npm install dodopayments
  ```

  ```bash yarn theme={null}
  yarn add dodopayments
  ```

  ```bash pnpm theme={null}
  pnpm add dodopayments
  ```
</CodeGroup>

## Quick Start

Create a client, then create a checkout session:

```javascript theme={null}
import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  bearerToken: process.env['DODO_PAYMENTS_API_KEY'], // This is the default and can be omitted
  environment: 'test_mode', // defaults to 'live_mode'
});

const checkoutSessionResponse = await client.checkoutSessions.create({
  product_cart: [{ product_id: 'pdt_123', quantity: 1 }],
});

console.log(checkoutSessionResponse.session_id);
```

If you omit `bearerToken`, the client reads the `DODO_PAYMENTS_API_KEY` environment variable. If you omit `environment`, the client connects to live mode. A test mode API key works only with `environment: 'test_mode'`.

<Warning>
  Keep API keys in environment variables or a secrets manager. Never commit them to version control or expose them in client-side code.
</Warning>

## Core Features

<CardGroup cols={2}>
  <Card title="TypeScript First" icon="shield-check">
    Type definitions for every request parameter and response field, shown in your editor.
  </Card>

  <Card title="Auto-Pagination" icon="arrows-rotate">
    List methods fetch the next page for you when you iterate with `for await...of`.
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation">
    A typed error class for each HTTP error status, with the status, headers, and response body.
  </Card>

  <Card title="Smart Retries" icon="repeat">
    Two retries by default, with exponential backoff, for connection errors and retryable status codes.
  </Card>
</CardGroup>

## Configuration

### Environment Variables

Store your API key in an environment variable:

```bash .env theme={null}
DODO_PAYMENTS_API_KEY=your_api_key_here
```

The client reads these variables when you don't pass the matching option:

| Variable | Client option | Purpose |
| - | - | - |
| `DODO_PAYMENTS_API_KEY` | `bearerToken` | Your API key. The client throws an error if neither is set. |
| `DODO_PAYMENTS_WEBHOOK_KEY` | `webhookKey` | Your webhook signing secret, used to verify webhooks. |
| `DODO_PAYMENTS_BASE_URL` | `baseURL` | A custom API URL that replaces the `environment` URL. |
| `DODO_PAYMENTS_LOG` | `logLevel` | The log level. See [Logging](#logging). |

If a base URL is set and you also pass `environment`, the constructor throws an "Ambiguous URL" error. To use `environment` in that case, pass `baseURL: null`.

To verify a webhook, pass the raw request body and headers to `client.webhooks.unwrap(rawBody, { headers })`. It checks the signature with your webhook key and returns the parsed event. `client.webhooks.unsafeUnwrap(rawBody)` parses the body without verifying it, so use it only for testing. See [Webhooks](/developer-resources/webhooks).

### Timeout Configuration

Requests time out after 1 minute by default. Set `timeout`, in milliseconds, on the client or on a single request:

```typescript theme={null}
// Configure default timeout for all requests (default is 1 minute)
const client = new DodoPayments({
  timeout: 20 * 1000, // 20 seconds
});

// Override per-request
await client.checkoutSessions.create(
  { product_cart: [{ product_id: 'pdt_123', quantity: 1 }] },
  { timeout: 5 * 1000 },
);
```

When a request times out, the SDK throws `APIConnectionTimeoutError`. Timed-out requests are retried, so a call can take longer than `timeout` before it fails.

### Retry Configuration

Set `maxRetries` on the client or on a single request:

```javascript theme={null}
// Configure default for all requests (default is 2 retries)
const client = new DodoPayments({
  maxRetries: 0, // disable retries
});

// Override per-request
await client.checkoutSessions.create(
  { product_cart: [{ product_id: 'pdt_123', quantity: 1 }] },
  { maxRetries: 5 },
);
```

<Tip>
  The SDK retries connection errors and responses with status 408, 409, 429, or 500 and above. It retries twice by default, with exponential backoff.
</Tip>

When a request still fails, the SDK throws a subclass of `DodoPayments.APIError`. Each error has `status`, `headers`, and `error` (the response body) properties. Check for a specific class with `instanceof`, for example `err instanceof DodoPayments.RateLimitError`:

| Status | Error class |
| - | - |
| 400 | `BadRequestError` |
| 401 | `AuthenticationError` |
| 403 | `PermissionDeniedError` |
| 404 | `NotFoundError` |
| 409 | `ConflictError` |
| 422 | `UnprocessableEntityError` |
| 429 | `RateLimitError` |
| 500 and above | `InternalServerError` |
| No response | `APIConnectionError` |

## Common Operations

The examples in this section use the `client` from [Quick Start](#quick-start).

### Create a Checkout Session

Create a checkout session, then redirect the customer to the returned `checkout_url`:

```typescript theme={null}
const session = await client.checkoutSessions.create({
  product_cart: [
    {
      product_id: 'pdt_123',
      quantity: 1
    }
  ],
  return_url: 'https://yourdomain.com/return'
});

console.log('Redirect to:', session.checkout_url);
```

Each `checkout_url` works once and expires after 24 hours. For every session option, see [Checkout Sessions](/developer-resources/checkout-session).

### Manage Customers

Create a customer with an email address and name, then retrieve it by ID:

```typescript theme={null}
// Create a customer
const customer = await client.customers.create({
  email: 'customer@example.com',
  name: 'John Doe',
  metadata: {
    user_id: '12345'
  }
});

// Retrieve customer
const retrieved = await client.customers.retrieve('cus_123');
console.log(`Customer: ${retrieved.name} (${retrieved.email})`);
```

### Handle Subscriptions

Create a subscription, charge an on-demand subscription, and read a subscription's usage history.

<Warning>
  `POST /subscriptions` (the SDK's `subscriptions.create` method) is **deprecated**. It still works for existing integrations, but new integrations should create subscriptions through a [Checkout Session](/developer-resources/checkout-session).
</Warning>

```typescript expandable theme={null}
// Create a subscription
const subscription = await client.subscriptions.create({
  billing: {
    country: 'US',
    city: 'San Francisco',
    state: 'CA',
    street: '1 Market St',
    zipcode: '94105',
  },
  customer: {
    customer_id: 'cus_123', // or pass { email, name } to create a new customer
  },
  product_id: 'pdt_456',
  quantity: 1,
});

// Charge an on-demand subscription
// product_price is in the lowest currency denomination (e.g., 2500 = $25.00 USD)
const chargeResponse = await client.subscriptions.charge(subscription.subscription_id, {
  product_price: 2500,
});

// Retrieve subscription usage history (for metered subscriptions)
const usageHistory = await client.subscriptions.retrieveUsageHistory(subscription.subscription_id, {
  start_date: '2024-01-01T00:00:00Z',
  end_date: '2024-03-31T23:59:59Z',
});
```

<Info>
  `billing` requires only `country`, a two-letter ISO country code. `customer` takes `{ customer_id }` to attach an existing customer or `{ email, name? }` to create one. `charge` is for [on-demand subscriptions](/developer-resources/ondemand-subscriptions), and `product_price` is in the smallest currency unit. `retrieveUsageHistory` returns a paginated list, which you can iterate as shown in [Auto-Pagination](#auto-pagination).
</Info>

## Usage-Based Billing

### Ingest Usage Events

Send usage events for a customer:

```typescript theme={null}
await client.usageEvents.ingest({
  events: [
    {
      event_id: 'api_call_12345',
      customer_id: 'cus_abc123',
      event_name: 'api_request',
      timestamp: new Date().toISOString(),
      metadata: {
        endpoint: '/api/v1/users',
        method: 'GET',
        tokens_used: '150'
      }
    }
  ]
});
```

<Info>
  The `event_id` is the idempotency key, so give each event a unique value. If the same `event_id` appears twice in one request, the whole request is rejected. If an `event_id` was already ingested, the new event is ignored. A request accepts up to 1,000 events. `timestamp` defaults to the current time and is rejected if it's more than 1 hour in the past or more than 5 minutes in the future.
</Info>

### Retrieve Usage Events

Retrieve a single event by its `event_id`, or list events filtered by customer, event name, and time range:

```typescript theme={null}
// Get a specific event
const event = await client.usageEvents.retrieve('api_call_12345');

// List events with filtering
const events = await client.usageEvents.list({
  customer_id: 'cus_abc123',
  event_name: 'api_request',
  start: '2024-01-14T10:30:00Z',
  end: '2024-01-15T10:30:00Z'
});
```

`usageEvents.list` also accepts `meter_id`, and returns a paginated list.

## Proxy Configuration

To send requests through a proxy, pass your runtime's proxy settings in `fetchOptions`.

### Node.js (Using Undici)

Pass an undici `ProxyAgent` as the `dispatcher`:

```typescript theme={null}
import DodoPayments from 'dodopayments';
import * as undici from 'undici';

const proxyAgent = new undici.ProxyAgent('http://localhost:8888');
const client = new DodoPayments({
  fetchOptions: {
    dispatcher: proxyAgent,
  },
});
```

### Bun

Set the `proxy` option:

```typescript theme={null}
import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  fetchOptions: {
    proxy: 'http://localhost:8888',
  },
});
```

### Deno

Create an HTTP client with `Deno.createHttpClient` and pass it as `client`:

```typescript theme={null}
import DodoPayments from 'npm:dodopayments';

const httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } });
const client = new DodoPayments({
  fetchOptions: {
    client: httpClient,
  },
});
```

## Logging

Set the log level with the `logLevel` client option or the `DODO_PAYMENTS_LOG` environment variable. The client option overrides the environment variable.

<Warning>
  At the `debug` level, the SDK logs every HTTP request and response, including headers and bodies. Some authentication headers are redacted, but sensitive data in bodies may still be visible.
</Warning>

```typescript theme={null}
// Via client option
const client = new DodoPayments({
  logLevel: 'debug', // Show all log messages
});
```

```bash theme={null}
# Via environment variable
export DODO_PAYMENTS_LOG=debug
```

The log levels, from most to least verbose, are:

* `'debug'`: Debug messages, info, warnings, and errors.
* `'info'`: Info messages, warnings, and errors.
* `'warn'`: Warnings and errors. This is the default.
* `'error'`: Errors only.
* `'off'`: No logging.

The SDK logs to `console` by default. To use `pino`, `winston`, or another logging library, pass your logger as the `logger` option; `logLevel` still controls which messages reach it. Log messages are for debugging only, and their format can change between releases.

## Migration from Node.js SDK

If you use the legacy Node.js SDK, follow the migration guide to upgrade. The current SDK uses the built-in `fetch` API instead of `node-fetch`, requires Node.js 20, TypeScript 4.9, and Jest 28 or later, and includes a migration tool that updates most of your code.

<Card title="View Migration Guide" icon="arrow-right" href="https://github.com/dodopayments/dodopayments-typescript/blob/main/MIGRATION.md">
  Learn how to migrate from the Node.js SDK to the TypeScript SDK
</Card>

## Auto-Pagination

List methods return paginated results. Iterate with `for await...of` to get items from every page. The SDK requests the next page when it needs it:

```typescript theme={null}
async function fetchAllPayments() {
  const allPayments = [];
  // Automatically fetches more pages as needed.
  for await (const paymentListResponse of client.payments.list()) {
    allPayments.push(paymentListResponse);
  }
  return allPayments;
}
```

To work with one page at a time, read `page.items` and call `hasNextPage()` and `getNextPage()`:

```typescript theme={null}
let page = await client.payments.list();
for (const paymentListResponse of page.items) {
  console.log(paymentListResponse);
}

// Convenience methods are provided for manually paginating:
while (page.hasNextPage()) {
  page = await page.getNextPage();
  // Process page.items here
}
```

To set the page size, pass `page_size` to the list method, for example `client.payments.list({ page_size: 50 })`.

## Requirements

The SDK supports TypeScript 4.9 or later and these runtimes:

* Web browsers (up-to-date Chrome, Firefox, Safari, Edge, and others)
* Node.js 20 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions
* Deno 1.28.0 or later
* Bun 1.0 or later
* Cloudflare Workers
* Vercel Edge Runtime
* Jest 28 or later with the `"node"` environment (the `"jsdom"` environment isn't supported)
* Nitro 2.6 or later

React Native isn't supported.

## Resources

<CardGroup cols={2}>
  <Card title="GitHub Repository" icon="github" href="https://github.com/dodopayments/dodopayments-typescript">
    Source code, releases, and the full method list.
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    Every endpoint, parameter, and response.
  </Card>

  <Card title="Discord Community" icon="discord" href="https://discord.gg/bYqAp4ayYh">
    Ask questions and talk with other developers.
  </Card>

  <Card title="Report Issues" icon="bug" href="https://github.com/dodopayments/dodopayments-typescript/issues">
    Report bugs or request features.
  </Card>
</CardGroup>

## Support

For help with the TypeScript SDK:

* **Discord**: Join the [community server](https://discord.gg/bYqAp4ayYh) for real-time help.
* **Email**: Contact [support@dodopayments.com](mailto:support@dodopayments.com).
* **GitHub**: Open an issue on the [repository](https://github.com/dodopayments/dodopayments-typescript).

## Contributing

To contribute, read the [contributing guidelines](https://github.com/dodopayments/dodopayments-typescript/blob/main/CONTRIBUTING.md).


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