---
name: create-seller-account
description: Create a Paddle seller account for a user on your platform via the partner API — eligibility screening, `POST /accounts/validate`, `POST /accounts`, and storing the returned `id`, `user.id`, `api_key`, and `user_api_key` in both sandbox and live.
metadata:
  internal: true
---

# Create a Paddle seller account

## When to use this skill

Use this skill when a user on your platform is ready to monetize — typically when they say something like "I want to add payments to my app." You'll provision a Paddle seller account for them through the partner API, so they never leave your platform. When a signup is attributed to a partner, Paddle suppresses its usual onboarding emails so you can own the experience.

This skill uses your **partner API key** (`psk_...` + `Paddle-PartnerID`). See [`partner-api-basics`](/partners/embed-billing/get-started/api-basics) for the auth and environment model. Do everything here in **both** sandbox and live.

## Onboard a seller in three steps

### 1. Check eligibility

Before you offer Paddle, confirm the user is:

- Based in a [supported country](/partners/embed-billing/get-started/supported-sellers).
- Selling a supported product category (digital goods and services — not physical goods, and within Paddle's acceptable use policy).

Block ineligible users in your frontend _before_ they start onboarding, so they don't build an integration they can't use. Physical goods and non-digital products are the top causes of later verification failure.

### 2. Collect details and pre-validate

Collect the user's name, email, business name and address, product category, and confirmation they accept the [Paddle terms of service](https://www.paddle.com/legal/terms). Then send the payload to `POST /accounts/validate`, which runs the same validation as account creation without creating anything. Validate against **both** sandbox and live.

A `204` means the payload is valid. A `400` returns an error object describing what to fix.

```bash
curl -X POST https://sandbox-api.paddle.com/accounts/validate \
  -H "Authorization: Bearer $PARTNER_API_KEY" \
  -H "Paddle-PartnerID: $PARTNER_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "user": { "first_name": "Sam", "last_name": "Miller", "email": "sam@example.com", "marketing_consent": false },
    "business": { "name": "AeroEdit", "address": { "country_code": "GB" } },
    "display_name": "AeroEdit",
    "product_categories": ["apps"],
    "accepted_terms_of_service": true
  }'
```

Common problems to surface in your UI:

- **Email already in use** — emails are unique _within_ an environment. Validate against both sandbox and live, and reject if the email exists in either. Suggest an alias like `sam+paddle@example.com`.
- **Business name conflict** — another Paddle seller already uses this business name. Ask for another.
- **Invalid characters in business name** — punctuation such as colons is rejected (for example `"Habitual: Track Your Day"`). Strip or sanitize before submitting.

### 3. Create the account and store keys

Once validation passes in both environments, send the same payload to `POST /accounts`. You can additionally include an `external_id` to link the Paddle account to a record on your platform. A `201` returns the created account.

```bash
curl -X POST https://sandbox-api.paddle.com/accounts \
  -H "Authorization: Bearer $PARTNER_API_KEY" \
  -H "Paddle-PartnerID: $PARTNER_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "user": { "first_name": "Sam", "last_name": "Miller", "email": "sam@example.com", "marketing_consent": false },
    "business": { "name": "AeroEdit", "address": { "country_code": "GB" } },
    "display_name": "AeroEdit",
    "product_categories": ["apps"],
    "accepted_terms_of_service": true,
    "external_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
  }'
```

```json
{
  "data": {
    "id": "sel_01jqx8k5z9r2m4n6p8s0t2v4w6",
    "display_name": "AeroEdit",
    "user": {
      "id": "165042",
      "first_name": "Sam",
      "last_name": "Miller",
      "email": "sam@example.com",
      "marketing_consent": false
    },
    "business": { "name": "AeroEdit", "address": { "country_code": "GB" } },
    "api_key": "pdl_sdbx_apikey_01jqx8k5z9r2m4n6p8s0t2v4w6_aB3dEf5gHi7jKl9mNpQrS2_Xyz",
    "user_api_key": "pdl_sdbx_apikey_01jqy2m7b4c6d8e0f2g4h6j8k0_zZ9yX8w7V6u5T4s3R2q1P0_oNm"
  },
  "meta": { "request_id": "87df0819-0213-44c5-90d7-f15162738a9c" }
}
```

Store, per environment:

- **`id`** (the `sel_...` seller ID) against the account in your database.
- **`user.id`** against your user record — this identifies the Paddle user who initiated onboarding.
- **`api_key`** — the seller key. Acts on behalf of the seller: catalog, settings, metrics.
- **`user_api_key`** — the user key. Mints magic sessions for dashboard access and the verification embed.

Both keys grant full access to the seller's account, so store them securely (a secrets manager, encrypted at rest).

## Common pitfalls

- **Parsing `user.id` as an integer.** Paddle currently returns an integer as a string but is migrating to a Paddle ID (`usr_...`). Treat it as an opaque string.
- **Validating only one environment.** An email free in sandbox may be taken in live. Validate — and create — in both.
- **Storing keys in plaintext or client-side.** Leaked keys are auto-revoked when found on GitHub.
- **Skipping eligibility screening.** Onboarding a physical-goods seller wastes their time and fails verification later.

## Verify

- `POST /accounts/validate` returns `204` for a good payload in both sandbox and live.
- `POST /accounts` returns `201` with `id`, `api_key`, and `user_api_key` in both environments.
- A follow-up read with the seller's `api_key` (for example `GET /settings/account`) returns `200` — confirming the stored key works.

## Related docs

- [Create a seller account](/partners/embed-billing/onboard-sellers/create-seller)
- [Sandbox and live environments](/partners/embed-billing/get-started/environments)
- [Supported seller countries and product categories](/partners/embed-billing/get-started/supported-sellers)
- Next: [`configure-seller-account`](/partners/embed-billing/onboard-sellers/configure-account)
