> For the complete documentation index, see [llms.txt](https://developer.paddle.com/llms.txt).

# Get started with Paddle in Node.js

Install the Paddle Node.js SDK, initialize a client, make your first request, and verify webhook signatures.

---

{% version-badge sdk="paddle-node" /%}

This quickstart walks through installing the Paddle Node.js SDK, initializing a client, making your first read-only API call, and verifying webhook signatures. By the end, you'll have a working sandbox client and webhook handler.

## Before you begin

- A [Paddle sandbox account](https://developer.paddle.com/sdks/sandbox.md).
- An [API key](https://developer.paddle.com/api-reference/about/api-keys#create-api-key.md) generated in your sandbox dashboard.
- Node.js 20 or later.

## 1. Install the SDK {% step=true %}

Install `@paddle/paddle-node-sdk` using your package manager.

{% code-group %}

```sh {% title="npm" %}
npm install @paddle/paddle-node-sdk
```

```sh {% title="pnpm" %}
pnpm add @paddle/paddle-node-sdk
```

```sh {% title="yarn" %}
yarn add @paddle/paddle-node-sdk
```

```sh {% title="bun" %}
bun add @paddle/paddle-node-sdk
```

{% /code-group %}

## 2. Initialize the client {% step=true %}

Import the SDK and create a `Paddle` client. Pass your API key directly, and set the environment to `sandbox` while you're building.

```typescript
import { Environment, LogLevel, Paddle } from '@paddle/paddle-node-sdk'

const paddle = new Paddle(process.env.PADDLE_API_KEY!, {
  environment: Environment.sandbox,
  logLevel: LogLevel.verbose,
})
```

For production, drop the `environment` option (or set it to `Environment.production`) and lower `logLevel` to `LogLevel.error`:

```typescript
const paddle = new Paddle(process.env.PADDLE_API_KEY!)
```

{% callout type="note" %}
Sandbox API keys contain `_sdbx`. Sandbox and live keys are separate — using one against the other API returns a `forbidden` error.
{% /callout %}

## 3. Make your first request {% step=true %}

List products to confirm the client is wired up. The SDK returns a `ProductCollection` that paginates lazily — call `.next()` to fetch the first page.

```typescript
import { Paddle, Product, ProductCollection } from '@paddle/paddle-node-sdk'

const paddle = new Paddle(process.env.PADDLE_API_KEY!)

async function listProducts() {
  const productCollection: ProductCollection = paddle.products.list()
  const firstPage: Product[] = await productCollection.next()
  console.log('First page of products:', firstPage)
}

listProducts()
```

If your sandbox account is empty, the array will be empty — that's fine, the call still succeeds. Create a product in the dashboard or via `paddle.products.create()` to see results.

## 4. Verify webhooks {% step=true %}

Paddle pushes webhook events to your endpoint when subscriptions, transactions, and customers change state. The SDK exposes a `paddle.webhooks.unmarshal()` helper that verifies the signature and parses the event in one call. Always verify the signature before acting on the payload.

```typescript
import { Paddle, EventName } from '@paddle/paddle-node-sdk'
import express, { Request, Response } from 'express'

const paddle = new Paddle(process.env.PADDLE_API_KEY!)
const app = express()

app.post(
  '/webhooks',
  express.raw({ type: 'application/json' }),
  async (req: Request, res: Response) => {
    const signature = (req.headers['paddle-signature'] as string) || ''
    const rawRequestBody = req.body.toString()
    const secretKey = process.env.PADDLE_WEBHOOK_SECRET!

    try {
      const eventData = await paddle.webhooks.unmarshal(
        rawRequestBody,
        secretKey,
        signature,
      )
      switch (eventData.eventType) {
        case EventName.TransactionCompleted:
          // Provision access, send a receipt, etc.
          break
        case EventName.SubscriptionUpdated:
          // Sync the subscription to your database.
          break
      }
      res.status(200).send('ok')
    } catch (err) {
      res.status(400).send('invalid signature')
    }
  },
)

app.listen(3000)
```

Use `express.raw()` middleware on the webhook route — `unmarshal` needs the raw request body string to verify the signature. For the full webhook setup flow, see [Verify webhook signatures](https://developer.paddle.com/webhooks/signature-verification.md).

## Next steps

{% card-group cols=2 %}
{% card title="Build with subscriptions" icon="carbon:repeat" url="/build/subscriptions" %}
Lifecycle, plan changes, pause and resume, addons.
{% /card %}
{% card title="Set up notification destinations" icon="carbon:notification" url="/webhooks/notification-destinations" %}
Configure where Paddle sends webhook events.
{% /card %}
{% card title="API reference" icon="carbon:api" url="/api-reference/overview" %}
Browse every endpoint Paddle exposes.
{% /card %}
{% card title="Node.js SDK reference" icon="nodejs" url="/sdks/libraries/node" %}
Full SDK reference — version, repo, install, auth.
{% /card %}
{% /card-group %}

{% callout type="note" %}
**Building a full-stack app?** The [Next.js SaaS starter kit](https://developer.paddle.com/get-started/starter-kits/nextjs-saas.md) ships a working Paddle integration — checkout, webhooks, customer portal — on top of Vercel and Supabase.
{% /callout %}