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

# API reference

> The Pocketsflow HTTP API — base URL, authentication, conventions, and a complete endpoint catalog.

The Pocketsflow HTTP API lets you manage products, checkout, orders, payments,
subscribers, customers, discounts, upsells, refunds, webhooks, newsletters,
your Link in Bio page, images, and the partner program — all from your own
systems.

<Card title="Open the interactive API reference" icon="server" href="https://api.pocketsflow.com/docs" horizontal>
  Browse every endpoint, see request/response schemas, and try calls live at
  **api.pocketsflow\.com/docs**.
</Card>

The raw OpenAPI specification is served at
[`https://api.pocketsflow.com/docs.json`](https://api.pocketsflow.com/docs.json) —
import it into Postman/Insomnia or generate a typed client from it.

## Base URL

Every request is made over HTTPS to:

```
https://api.pocketsflow.com
```

There is no version prefix in the path today — endpoints are addressed directly
(for example `GET /orders`). New fields are added in a backwards-compatible way;
treat unknown fields as optional and never hard-code assumptions about field
order.

## Authentication

Every endpoint requires authentication via the `Authorization` header using the
**Bearer** scheme. Two credential types are accepted:

| Credential    | Format                    | Typical use                                     |
| ------------- | ------------------------- | ----------------------------------------------- |
| **API key**   | `pk_live_…` / `pk_test_…` | Server-to-server integrations, scripts, agents. |
| **Auth0 JWT** | `Bearer eyJ…`             | First-party dashboard / session-based calls.    |

Create an API key in the dashboard under **Developers → API keys**, then send it
on every request:

```bash theme={null}
curl https://api.pocketsflow.com/users/me \
  -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxxxxxx"
```

| Prefix      | Mode | Data                                                      |
| ----------- | ---- | --------------------------------------------------------- |
| `pk_live_…` | Live | Real products, customers, payments, and payouts.          |
| `pk_test_…` | Test | Sandbox data for development — never charges a real card. |

All data is automatically scoped to the account that owns the key (and to that
key's test/live mode) — you never pass a user id. Keys are stored hashed, can be
given an expiry, and can be archived/revoked at any time; a revoked or expired
key returns `401`.

<Warning>
  Treat secret API keys like passwords. Never expose them in client-side code,
  public repositories, or URLs. Prefer a dedicated, revocable key per
  integration, and rotate a key immediately if it leaks.
</Warning>

### Authentication errors

Missing or malformed credentials return `401` with a JSON body:

```json theme={null}
{
  "error": "Invalid API key",
  "message": "The provided API key is invalid, inactive, or has been archived"
}
```

Common cases: no `Authorization` header, a header that doesn't start with
`Bearer `, an empty token, and an inactive/archived/expired key.

## Making requests

* Send JSON bodies with `Content-Type: application/json` (the only exception is
  `POST /images`, which uses `multipart/form-data`).
* Responses are JSON. IDs are Mongo ObjectId strings (24 hex characters) unless
  noted (Stripe subscription ids are `sub_…`; payment-processor ids are
  `plan_…`, `pay_…`, `mem_…`).
* Timestamps are ISO 8601 UTC strings (`2026-01-24T05:35:38.103Z`).
* Money on the **Payments**, **Subscribers**, and **Orders** resources is
  returned in **major currency units** (e.g. `12.1` = \$12.10). Money inside a
  **webhook** payload is in the **smallest** unit (e.g. cents) — see
  [Webhook events](/api-webhooks/events).

## Pagination

List endpoints paginate in one of the following ways.

| Style        | Endpoints                                            | Query params                                    | Response envelope          |
| ------------ | ---------------------------------------------------- | ----------------------------------------------- | -------------------------- |
| Page-based   | `/orders`, `/payments`, `/subscriptions/subscribers` | `page` (default `1`), `pageSize` (default `20`) | `{ <items>, pagination }`  |
| Page + limit | `/partners/referrals`, `/partners/referrals/sales`   | `page` (default `1`), `limit` (default `20`)    | paginated list             |
| None         | `/customers`, `/reviews`                             | —                                               | full array in one response |

The page-based `pagination` object looks like:

```json theme={null}
{
  "totalCount": 137,
  "totalPages": 7,
  "currentPage": 1,
  "pageSize": 20,
  "hasMore": true
}
```

## Errors

Errors use standard HTTP status codes and a JSON body carrying an `error`
message (some endpoints also return a machine-readable `code` or a human
`message`):

```json theme={null}
{ "error": "Product not found", "code": "not_found" }
```

| Status        | Meaning                                                         |
| ------------- | --------------------------------------------------------------- |
| `200` / `201` | Success.                                                        |
| `400`         | Invalid request (missing/!valid parameters).                    |
| `401`         | Missing, malformed, expired, or revoked credentials.            |
| `403`         | Authenticated but not permitted (e.g. unverified email sender). |
| `404`         | Resource not found or not owned by you.                         |
| `409`         | Conflict (e.g. already a partner).                              |
| `500` / `502` | Server-side error — safe to retry with backoff.                 |

## Rate limits

Requests authenticated with a valid API key get **600 requests per minute per
account**. Unauthenticated traffic shares a stricter per-IP allowance, so
always send your key. Exceeding a limit returns `429` with a JSON body that
includes `retryAfter` (seconds) — back off and retry after that delay. Beyond
that, use reasonable concurrency, back off on `5xx`, and cache where you can.
Some endpoints have their own caps — for example
[`POST /newsletters/send`](/api-reference/send-email) accepts at most
**100 recipients** per request.

## Endpoint catalog

Everything below is available with an API key. Full request/response schemas
live in the [interactive reference](https://api.pocketsflow.com/docs); the most
important response shapes are shown here.

### Products

| Method   | Path                    | Description                                 |
| -------- | ----------------------- | ------------------------------------------- |
| `GET`    | `/products`             | List all products.                          |
| `POST`   | `/products`             | Create a product. Requires `name`, `price`. |
| `GET`    | `/products/{id}`        | Get a product by id.                        |
| `POST`   | `/products/update/{id}` | Update a product.                           |
| `DELETE` | `/products/{id}`        | Delete a product.                           |

`POST /products` accepts (among others) `name`, `price`, `description`,
`subtitle`, `published`, `slug`, `thumbnail`, `images[]`, `payWant`, `minPrice`,
`maxPrice`, `showSales`, `showReviews`, `refundPolicy`, `hasFirstName`,
`hasLastName`.

### Checkout

| Method | Path                 | Description                                                     |
| ------ | -------------------- | --------------------------------------------------------------- |
| `POST` | `/checkout/sessions` | Create a hosted checkout session and redirect the buyer to pay. |

```bash theme={null}
curl https://api.pocketsflow.com/checkout/sessions \
  -H "Authorization: Bearer pk_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "665f1c2a9b3e4a0012ab34cd",
    "successUrl": "https://acme.com/thanks",
    "cancelUrl": "https://acme.com/pricing",
    "customerEmail": "buyer@example.com",
    "discountCode": "LAUNCH20"
  }'
```

```json theme={null}
{
  "id": "cs_665f1c2a9b3e4a0012ab99aa",
  "url": "https://acme.pocketsflow.com/checkout/cs_665f1c2a9b3e4a0012ab99aa"
}
```

Required: `productId`, `successUrl`, `cancelUrl`. Optional: `customerEmail`,
`discountCode`, `metadata` (echoed back on the resulting `order.completed`
webhook).

### Orders

| Method | Path                         | Description                                                                                                                                   |
| ------ | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET`  | `/orders`                    | List orders. Filters: `startDate`, `endDate`, `productId`, `page`, `pageSize`.                                                                |
| `GET`  | `/orders/{id}`               | Get an order by id.                                                                                                                           |
| `GET`  | `/orders/subscriptions/{id}` | Get a subscriber with its subscription offer, customer, live `status`, and full `payments` history. `{id}` is the SubscriptionCustomer `_id`. |

```json theme={null}
{
  "orders": [
    {
      "_id": "6a43ce05fae6c4f0cb5a2193",
      "buyerEmail": "buyer@example.com",
      "gross": 12.1,
      "net": 8.46,
      "taxes": 2.1,
      "createdAt": "2026-01-24T05:35:38.103Z",
      "product": { "_id": "665f1c2a9b3e4a0012ab34cd", "name": "Starter Kit" },
      "customer": { "_id": "665f1c2a9b3e4a0012abaa11", "buyerEmail": "buyer@example.com" }
    }
  ],
  "pagination": { "totalCount": 42, "totalPages": 3, "currentPage": 1, "pageSize": 20, "hasMore": true }
}
```

### Payments

The unified payment ledger — **one-time product purchases and subscription
charges (initial + every renewal)** in a single resource. Every payment is a
`Sale`.

| Method | Path             | Description                                                                                                                                        |
| ------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET`  | `/payments`      | List payments. Filters: `type` (`one_time` \| `subscription` \| `all`), `startDate`, `endDate`, `productId`, `subscriptionId`, `page`, `pageSize`. |
| `GET`  | `/payments/{id}` | Get a payment by id. Pass `?live=true` to also fetch the authoritative live payment from the payment processor as `whopLive`.                      |

A single **Payment** object:

```json theme={null}
{
  "_id": "665f1c2a9b3e4a0012ab34cd",
  "isSubscription": true,
  "billingReason": "renewal",
  "amount": 12.1,
  "grossAmount": 12.1,
  "amountBeforeTax": 10,
  "taxAmount": 2.1,
  "currency": "usd",
  "buyerEmail": "buyer@example.com",
  "firstName": "Jane",
  "lastName": "Buyer",
  "isRefunded": false,
  "isDisputed": false,
  "productId": "",
  "subscriptionId": "665f1c2a9b3e4a0012ab7788",
  "whopPaymentId": "pay_rehv02B3gdIZT0",
  "whopPlanId": "plan_vlD50mPbsqJLp",
  "whopMembershipId": "mem_UCTD32gDLGTgb",
  "createdAt": "2026-01-24T05:35:38.103Z"
}
```

<ResponseField name="isSubscription" type="boolean">
  `true` for subscription payments (initial + renewals); `false` for one-time
  product purchases.
</ResponseField>

<ResponseField name="billingReason" type="string">
  Present only on subscription payments: `initial` for the first charge that
  activates the membership, `renewal` for every recurring charge after it.
</ResponseField>

<ResponseField name="productId" type="string">
  Set on one-time purchases; empty on subscription payments (use
  `subscriptionId` instead).
</ResponseField>

<ResponseField name="whopPaymentId / whopPlanId / whopMembershipId" type="string">
  The processor's receipt, plan, and membership ids backing the sale.
  `whopMembershipId` is present on subscription payments only.
</ResponseField>

`GET /payments` returns `{ payments: [Payment], pagination }`. `GET
/payments/{id}?live=true` returns a **PaymentDetail**:

```json theme={null}
{
  "payment": {
    "_id": "665f1c2a9b3e4a0012ab34cd",
    "isSubscription": true,
    "billingReason": "renewal",
    "amount": 12.1,
    "amountBeforeTax": 10,
    "taxAmount": 2.1,
    "currency": "usd",
    "net": 8.46,
    "fee": 0.94,
    "metadata": { "id": "pay_rehv02B3gdIZT0", "status": "paid", "...": "raw stored processor payload" }
  },
  "product": null,
  "subscription": { "_id": "665f1c2a9b3e4a0012ab7788", "name": "Pro Membership", "price": 29, "frequency": "monthly" },
  "customer": { "_id": "665f1c2a9b3e4a0012abaa11", "buyerEmail": "buyer@example.com" },
  "whop": { "id": "pay_rehv02B3gdIZT0", "total": 12.1, "subtotal": 10 },
  "whopLive": { "id": "pay_rehv02B3gdIZT0", "status": "paid", "amount_after_fees": 8.46 }
}
```

`net` = `amountBeforeTax − affiliateCut − stripeFee`; `fee` is the processor fee.
`whopLive` is `null` if the live lookup failed — the stored `whop` block still
applies, so `?live=true` never fails the request.

### Subscriptions and subscribers

Two distinct concepts: a **subscription offer** is the recurring product you
sell; a **subscriber** is a buyer's membership in one of those offers.

| Method | Path                              | Description                                                                                                                    |
| ------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `POST` | `/subscriptions`                  | Create a subscription **offer**. Requires `name`, `price`, and `frequency` (`weekly`, `monthly`, or `yearly`).                 |
| `GET`  | `/subscriptions`                  | List your subscription **offers**.                                                                                             |
| `GET`  | `/subscriptions/{id}`             | Get a subscription **offer** by id.                                                                                            |
| `GET`  | `/subscriptions/subscribers`      | List **subscribers**. Filters: `status`, `buyerEmail` (alias `email`), `subscriptionId`, `page`, `pageSize`.                   |
| `GET`  | `/subscriptions/subscribers/{id}` | Get a subscriber + `subscription`, `customer`, live `status`, and full `payments` history. `?live=true` adds `whopMembership`. |
| `POST` | `/subscriptions/{id}/cancel`      | Cancel a subscriber's subscription now. `{id}` is the Stripe subscription id (`sub_…`).                                        |
| `POST` | `/subscriptions/{id}/pause`       | Pause collection on a subscription (`sub_…`).                                                                                  |
| `POST` | `/subscriptions/{id}/resume`      | Resume a paused subscription (`sub_…`).                                                                                        |

<Note>
  Subscription offer creation and `/subscriptions/{id}` accept both an API key
  and an Auth0 JWT. In SDK v1.2.0 and later, Node.js and TypeScript users can
  call `pocketsflow.subscriptionOffers.create()`. See the [SDK
  guide](/api-reference/sdk) for installation and usage; use the REST request
  above if your installed SDK predates v1.2.0.
</Note>

A **Subscriber** object carries a processor-sourced live `status`
(`incomplete`, `incomplete_expired`, `trialing`, `active`, `past_due`,
`canceled`, `unpaid`, `succeeded`, `refunded`, `paused`) and the joined offer:

```json theme={null}
{
  "_id": "665f1c2a9b3e4a0012abccdd",
  "buyerEmail": "buyer@example.com",
  "status": "active",
  "active": true,
  "whopSubscriptionId": "plan_vlD50mPbsqJLp",
  "subscriptionId": "665f1c2a9b3e4a0012ab7788",
  "firstName": "Jane",
  "lastName": "Buyer",
  "country": "NL",
  "createdAt": "2026-01-24T05:35:38.103Z",
  "subscription": { "_id": "665f1c2a9b3e4a0012ab7788", "name": "Pro Membership", "price": 29, "frequency": "monthly" }
}
```

`GET /subscriptions/subscribers/{id}?live=true`:

```json theme={null}
{
  "subscriber": { "_id": "665f1c2a9b3e4a0012abccdd", "status": "active", "buyerEmail": "buyer@example.com" },
  "subscription": { "_id": "665f1c2a9b3e4a0012ab7788", "name": "Pro Membership", "price": 29, "frequency": "monthly" },
  "customer": { "_id": "665f1c2a9b3e4a0012abaa11", "buyerEmail": "buyer@example.com" },
  "status": "active",
  "payments": [
    { "_id": "665f...01", "billingReason": "initial", "amount": 29, "createdAt": "2026-05-24T10:00:00.000Z" },
    { "_id": "665f...02", "billingReason": "renewal", "amount": 29, "createdAt": "2026-06-24T10:00:00.000Z" }
  ],
  "whopMembership": { "id": "mem_UCTD32gDLGTgb", "status": "active", "valid": true }
}
```

### Discounts

| Method   | Path                                          | Description                                                                             |
| -------- | --------------------------------------------- | --------------------------------------------------------------------------------------- |
| `GET`    | `/discounts`                                  | List discounts.                                                                         |
| `POST`   | `/discounts`                                  | Create a discount. Requires `name`, `code`, `value`, `mainProductIds`.                  |
| `GET`    | `/discounts/{id}`                             | Get a discount by id.                                                                   |
| `POST`   | `/discounts/{id}`                             | Update a discount (full update — send every field you want to keep).                    |
| `DELETE` | `/discounts/{id}`                             | Delete a discount.                                                                      |
| `GET`    | `/discounts/apply/{discountCode}/{productId}` | Public (no API key) — validate a code against a product; returns the discount or `404`. |
| `GET`    | `/discounts/check-product/{productId}`        | Public (no API key) — returns `true`/`false` whether any discount targets the product.  |

`valueType` is `percentage` (default) or `fixed`; optional `active`, `expiration`.

### Upsells

| Method   | Path            | Description                                                                    |
| -------- | --------------- | ------------------------------------------------------------------------------ |
| `GET`    | `/upsells`      | List upsells.                                                                  |
| `POST`   | `/upsells`      | Create an upsell. Requires `mainProductIds`, `upsellProductId`, `upsellPrice`. |
| `GET`    | `/upsells/{id}` | Get an upsell by id.                                                           |
| `POST`   | `/upsells/{id}` | Update an upsell (full update — send every field you want to keep).            |
| `DELETE` | `/upsells/{id}` | Delete an upsell.                                                              |

Optional: `name`, `offer`, `upsellDescription`, `primaryButtonText`,
`secondaryButtonText`, `active`.

### Refunds

| Method | Path            | Description                                                                 |
| ------ | --------------- | --------------------------------------------------------------------------- |
| `POST` | `/refunds`      | Create a refund. Requires `orderId`; optional `amount` (partial), `reason`. |
| `GET`  | `/refunds`      | List refunds.                                                               |
| `GET`  | `/refunds/{id}` | Get a refund by id.                                                         |

### Reviews

| Method | Path       | Description                                               |
| ------ | ---------- | --------------------------------------------------------- |
| `GET`  | `/reviews` | List all your product reviews, newest first (no filters). |

### Webhooks

| Method   | Path                  | Description                                                                         |
| -------- | --------------------- | ----------------------------------------------------------------------------------- |
| `GET`    | `/webhooks`           | List webhook endpoints (secrets omitted).                                           |
| `POST`   | `/webhooks`           | Create a webhook. Requires `url` (HTTPS), `events[]`. Returns the signing `secret`. |
| `GET`    | `/webhooks/{id}`      | Get a webhook by id.                                                                |
| `PATCH`  | `/webhooks/{id}`      | Update a webhook.                                                                   |
| `DELETE` | `/webhooks/{id}`      | Delete a webhook.                                                                   |
| `POST`   | `/webhooks/{id}/test` | Send a sample event to the endpoint.                                                |

See [Webhook events](/api-webhooks/events) for the full event list and payloads,
and [Authentication & security](/api-webhooks/authentication-and-security) for
signature verification.

### Newsletters

| Method   | Path                            | Description                                                                                |
| -------- | ------------------------------- | ------------------------------------------------------------------------------------------ |
| `GET`    | `/newsletters/posts`            | List posts. Filter by `status` (`draft` \| `published`).                                   |
| `POST`   | `/newsletters/posts`            | Create a post. Requires `title`, `content`.                                                |
| `GET`    | `/newsletters/posts/{id}`       | Get a post by id.                                                                          |
| `POST`   | `/newsletters/posts/{id}`       | Update a post (sent posts can no longer be edited).                                        |
| `DELETE` | `/newsletters/posts/{id}`       | Delete a post.                                                                             |
| `POST`   | `/newsletters/posts/{id}/send`  | Send a post to all subscribers.                                                            |
| `POST`   | `/newsletters/send`             | Send a one-off email to up to 100 recipients. See [Send email](/api-reference/send-email). |
| `GET`    | `/newsletters/subscribers`      | List newsletter subscribers.                                                               |
| `GET`    | `/newsletters/subscribers/{id}` | Get a subscriber by id.                                                                    |
| `DELETE` | `/newsletters/subscribers/{id}` | Remove a subscriber.                                                                       |

### Link in Bio (creator pages)

| Method   | Path                        | Description                           |
| -------- | --------------------------- | ------------------------------------- |
| `GET`    | `/creator-pages`            | Get your Link in Bio page.            |
| `POST`   | `/creator-pages`            | Update page settings and content.     |
| `POST`   | `/creator-pages/links`      | Add a link (`title`, `url` required). |
| `PUT`    | `/creator-pages/links/{id}` | Update a link.                        |
| `DELETE` | `/creator-pages/links/{id}` | Delete a link.                        |

### Images

| Method   | Path           | Description                                                                   |
| -------- | -------------- | ----------------------------------------------------------------------------- |
| `POST`   | `/images`      | Upload an image (`multipart/form-data`, field `file`). Returns `{ url, id }`. |
| `GET`    | `/images`      | List uploaded images.                                                         |
| `DELETE` | `/images/{id}` | Delete an image.                                                              |

### Customers

| Method | Path              | Description                                                                                                                                                                                                      |
| ------ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET`  | `/customers`      | List customers (full list, deduplicated by email, with `numberOfOrders`). Filter by `productId`; sort with `sortBy` (`email` \| `country` \| `numberOfOrders` \| `createdAt`) and `sortOrder` (`asc` \| `desc`). |
| `GET`  | `/customers/{id}` | Get a customer by id.                                                                                                                                                                                            |

### Account

| Method | Path        | Description                    |
| ------ | ----------- | ------------------------------ |
| `GET`  | `/users/me` | Get the authenticated account. |

```json theme={null}
{
  "userId": "auth0|68fd3a499605389e7305cdc1",
  "email": "creator@example.com",
  "subdomain": "acme",
  "testMode": false,
  "hasSales": true,
  "createdFirstProduct": true,
  "stripeAccountId": "acct_1Nz...",
  "stripeAccountStatus": "active"
}
```

Use `subdomain` to build product/checkout URLs
(`https://{subdomain}.pocketsflow.com/...`).

### Partners

| Method | Path                          | Description                                            |
| ------ | ----------------------------- | ------------------------------------------------------ |
| `POST` | `/partners`                   | Become a partner. Returns your `referralCode`.         |
| `GET`  | `/partners`                   | Get your partner profile.                              |
| `POST` | `/partners/register-referral` | Record a referral signup (`referralCode`).             |
| `GET`  | `/partners/referrals`         | List referred users (`page`, `limit`).                 |
| `GET`  | `/partners/referrals/sales`   | List sales from referred users (`page`, `limit`).      |
| `GET`  | `/partners/stats`             | Live-computed signups, sales, revenue, and commission. |

## Connect an AI agent

Prefer to drive Pocketsflow from an AI assistant? The
[MCP server](/api-reference/mcp-server) exposes this entire API as Model
Context Protocol tools — connect Claude, Cursor, or your own agent with an API
key and it can manage your account in natural language.

## Building an integration?

If you're connecting Pocketsflow to an external store or platform (for example
WooCommerce), start with the [Integrations](/integrations/overview) section — it
walks through the end-to-end pattern with code examples.

## Related topics

* [MCP server](/api-reference/mcp-server)
* [Send email](/api-reference/send-email)
* [Webhooks & API overview](/api-webhooks/overview)
* [Webhook events](/api-webhooks/events)
* [Authentication & security](/api-webhooks/authentication-and-security)
* [Integrations overview](/integrations/overview)
