{
  "openapi": "3.0.0",
  "info": {
    "title": "Pocketsflow Public API",
    "version": "1.0.0",
    "description": "Manage your Pocketsflow store programmatically: products and subscription offers, checkout sessions, orders and the payment ledger, customers, discounts, upsells, refund policies, reviews, webhooks, newsletters, your Link in Bio page, the partner program, and what your plan unlocks.\n\n### Authentication\n\nSend an API key as a Bearer token on every request: `Authorization: Bearer pk_live_…`. Create keys in the dashboard under **Developers → API keys**. Every request is scoped to the account that owns the key — you never pass a user id.\n\nThe key prefix decides the mode: a `pk_test_…` key always reads and writes **test-mode** data and a `pk_live_…` key always **live** data, whatever the test-mode toggle in the dashboard says: lists return that mode's data and anything you create lands in it. `GET /users/me` reports the key's mode as `testMode`. Reads and updates by id act on your own items in either mode, so keep test and live ids apart in your integration.\n\nA missing, malformed, inactive, archived or expired key returns `401` (`{ \"error\": \"…\", \"message\": \"…\" }`). Accounts in a country the API does not serve get `403` with `code: \"COUNTRY_RESTRICTED\"`. The few endpoints that need no key say **PUBLIC** in their description.\n\n### Errors\n\nErrors use standard HTTP status codes and a JSON body. Most endpoints return `{ \"message\": \"…\" }`; authentication failures return `{ \"error\": \"…\", \"message\": \"…\" }`; some add a machine-readable `code` (for example `ACCOUNT_BLOCKED`, `SEND_ALLOWANCE_EXCEEDED`, `EMAIL_SENDING_PAUSED`, `DAILY_SEND_LIMIT`), and a few older endpoints only set `error`. Read `message`, fall back to `error`, and branch on `code` when it is present. A handful of validation errors are returned as a plain-text body.\n\n| Status | Meaning |\n| --- | --- |\n| `400` | Invalid request — malformed JSON, an invalid id, or a failed validation |\n| `401` | Missing, malformed, expired or revoked API key |\n| `402` | A plan allowance would be exceeded (newsletter sends) |\n| `403` | Authenticated but not allowed (restricted account, unverified sender, plan limit, or `FEATURE_LOCKED`) |\n| `404` | Not found, not yours, or from the other test/live mode |\n| `409` | Conflict (for example: already a partner) |\n| `410` | The account was deleted |\n| `429` | Rate limit exceeded — wait `Retry-After` seconds |\n| `5xx` | Server or upstream error — safe to retry reads with exponential backoff |\n\n### Plans and `FEATURE_LOCKED`\n\nSelling, checkout, payouts, discount codes, exports, the API, webhooks and MCP work on every plan. Some features need a paid plan: Starter unlocks custom domains, premium templates and ad pixels; Growth unlocks upsells, abandoned checkout recovery and affiliates. A write that would turn on a feature the account's plan does not include answers `403` with `code: \"FEATURE_LOCKED\"`, `upgradeRequired: true` and `details: { feature, currentPlan, requiredPlan, requiredPlanName, requiredPlanPrice }` (the `FeatureLocked` schema). Nothing is saved, and retrying will not help: the creator has to upgrade at https://app.pocketsflow.com/profile/billing. Reading, turning a feature off and deleting are never locked. Call `GET /users/entitlements` to check before you write, and `GET /users/billing-tiers` for the plans catalogue.\n\n### Pagination\n\n`GET /orders`, `GET /payments` and `GET /subscriptions/subscribers` are page-based: `page` (1-based, default `1`) and `pageSize` (default `20`; maximum `100`). They respond with `{ <items>, pagination: { totalCount, totalPages, currentPage, pageSize, hasMore } }` — keep requesting `page + 1` while `hasMore` is `true`. The partner referral lists use `page` + `limit` (maximum `100`) and answer `{ data, pagination: { currentPage, totalPages, totalItems, hasNextPage, hasPrevPage, limit } }`. Every other list endpoint returns the full array in one response.\n\n### Rate limits\n\nRequests authenticated with an API key get **600 requests per minute per account**. Unauthenticated requests (public endpoints, or a request whose key is rejected) share **200 requests per minute per IP address**. Responses carry `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` (seconds until the window resets) and `RateLimit-Policy` headers. Exceeding a limit returns `429` with a `Retry-After` header (seconds) and `{ \"message\": \"…\", \"retryAfter\": 60 }`. Some endpoints have their own caps — `POST /newsletters/send` accepts at most 100 recipients per request.\n\n### Conventions\n\n- Ids are 24-character hex MongoDB ObjectId strings unless noted. Payments-partner ids keep their prefixes (`pay_…`, `plan_…`, `mem_…`); checkout session ids start with `cs_` and webhook event ids with `evt_`.\n- Money is a decimal amount in major units of the record's currency (`29.99` = $29.99), never cents — except fields whose name ends in `Cents`. Webhook payloads have their own rules; see the **Webhook events** section.\n- Timestamps are ISO 8601 UTC strings. Request bodies are JSON (`Content-Type: application/json`); endpoints that take files also accept `multipart/form-data`.\n\n### Versioning\n\nThe API is unversioned — there is no version prefix in the path. Changes are additive: new endpoints, optional request fields, response fields and webhook event types can appear at any time, so ignore fields you don't recognise. A few responses also carry older duplicate keys for fields that were renamed; they hold the same value, are deprecated and undocumented, and will only be removed after notice. Breaking changes are announced in the [changelog](https://www.pocketsflow.com/changelog) before they ship.\n\n### Transaction cost\n\nPocketsflow has no monthly or setup fee. Each sale carries one itemized transaction cost — an estimated **4.7% + $0.30** (about **$5.00** on $100) covering the Pocketsflow platform, payment infrastructure, tax handling when required, and dispute prevention. The platform portion is 2% of the sale, 1% on the Volume plan and 0.5% on Volume Max (`feeRate` on `GET /users/entitlements`). Orders return the stored platform fee and the rate it was charged at (`platformFee`, `platformFeeRate`), the processing fee, total transaction cost, effective fee rate, and net amount for reconciliation. See [Pricing & fees](https://www.pocketsflow.com/pricing).\n\n### Example repositories\n\nComplete, runnable examples on GitHub — embed a checkout and verify/handle webhooks:\n\n- [Subscriptions example](https://github.com/pocketsflow/subscriptions-example) — subscription checkout embed + `customer.subscription.*` / `invoice.*` webhooks.\n- [One-time products example](https://github.com/pocketsflow/one-time-products-example) — product checkout embed + `order.completed` / `order.refunded` webhooks.",
    "contact": {
      "name": "Pocketsflow Developer Support",
      "email": "support@pocketsflow.com"
    },
    "termsOfService": "https://pocketsflow.com/terms",
    "license": {
      "name": "Proprietary",
      "url": "https://pocketsflow.com/terms"
    }
  },
  "servers": [
    {
      "url": "https://api.pocketsflow.com",
      "description": "Production API (live and test mode — the key decides)"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Your Pocketsflow API key (`pk_live_…` for live data, `pk_test_…` for test data)"
      }
    },
    "headers": {
      "RateLimit-Limit": {
        "description": "Requests allowed in the current window",
        "schema": {
          "type": "integer",
          "example": 600
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in the current window",
        "schema": {
          "type": "integer",
          "example": 0
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the window resets",
        "schema": {
          "type": "integer",
          "example": 42
        }
      },
      "RateLimit-Policy": {
        "description": "The policy as `<limit>;w=<window seconds>`",
        "schema": {
          "type": "string",
          "example": "600;w=60"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying",
        "schema": {
          "type": "integer",
          "example": 42
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is malformed (invalid JSON or id) or failed validation. A few endpoints answer with a plain-text body instead of JSON.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "message": "Invalid product ID"
            }
          },
          "text/plain": {
            "schema": {
              "type": "string",
              "example": "Invalid product ID"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, malformed, inactive, archived or expired API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Invalid API key",
              "message": "The provided API key is invalid, inactive, or has been archived"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Authenticated, but not allowed — e.g. the account's country is not served (`COUNTRY_RESTRICTED`), the account is suspended, or a plan limit applies",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Country not supported",
              "message": "The API is not available for accounts in your country. Contact support.",
              "code": "COUNTRY_RESTRICTED"
            }
          }
        }
      },
      "NotFound": {
        "description": "The resource does not exist, belongs to another account, or belongs to the other test/live mode. A few endpoints answer with a plain-text body instead of JSON.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "message": "Product not found"
            }
          },
          "text/plain": {
            "schema": {
              "type": "string",
              "example": "Creator page not found"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded: 600 requests/minute per account with an API key, 200 requests/minute per IP without one",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string"
                },
                "retryAfter": {
                  "type": "integer",
                  "description": "Seconds to wait (the window length)",
                  "example": 60
                }
              }
            },
            "example": {
              "message": "Too many requests for this account, please try again later.",
              "retryAfter": 60
            }
          }
        }
      },
      "ServerError": {
        "description": "Unexpected server error. Safe to retry reads with exponential backoff.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "message": "Internal Server Error"
            }
          }
        }
      }
    },
    "schemas": {
      "LicenseValidation": {
        "type": "object",
        "description": "The answer of the public license endpoints. The buyer's email is masked.",
        "properties": {
          "valid": {
            "type": "boolean",
            "description": "True when the key is active (and, on validate with `instanceId`, activated on that instance)"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled",
              "not_found"
            ]
          },
          "code": {
            "type": "string",
            "nullable": true,
            "enum": [
              "invalid_request",
              "license_not_found",
              "license_disabled",
              "activation_limit_reached",
              "instance_not_found"
            ],
            "description": "Why `valid` is false; null when it is true"
          },
          "message": {
            "type": "string"
          },
          "productId": {
            "type": "string",
            "nullable": true
          },
          "activations": {
            "type": "integer",
            "description": "Instances the key is activated on"
          },
          "activationLimit": {
            "type": "integer",
            "nullable": true,
            "description": "Maximum activations; null = unlimited"
          },
          "activationsRemaining": {
            "type": "integer",
            "nullable": true
          },
          "buyerEmail": {
            "type": "string",
            "nullable": true,
            "example": "a***@example.com"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "testMode": {
            "type": "boolean"
          },
          "instance": {
            "type": "object",
            "nullable": true,
            "description": "The instance of this request, when it is activated",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "LicenseKey": {
        "type": "object",
        "description": "A license key as its creator sees it",
        "properties": {
          "id": {
            "type": "string"
          },
          "key": {
            "type": "string",
            "example": "7KQ2M-X9D4R-T3VWB-8HNPC"
          },
          "productId": {
            "type": "string"
          },
          "orderId": {
            "type": "string",
            "description": "The order (sale) it was issued for"
          },
          "buyerEmail": {
            "type": "string",
            "format": "email"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ]
          },
          "disabledReason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "creator",
              "refunded",
              "disputed",
              "subscription_ended"
            ]
          },
          "activationLimit": {
            "type": "integer",
            "nullable": true
          },
          "activationsCount": {
            "type": "integer"
          },
          "activations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "instanceId": {
                  "type": "string"
                },
                "instanceName": {
                  "type": "string"
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "testMode": {
            "type": "boolean"
          },
          "lastValidatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AnalyticsFunnelRow": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "The source (`google.com`, a `utm_source`, `direct`, or `unattributed` for orders recorded before attribution existed) or the product / subscription id"
          },
          "visits": {
            "type": "integer",
            "description": "Visits (distinct visitor sessions)"
          },
          "productViews": {
            "type": "integer",
            "description": "Visits that viewed a product page or opened its checkout"
          },
          "checkouts": {
            "type": "integer",
            "description": "Visits that opened a checkout (checkout started)"
          },
          "paid": {
            "type": "integer",
            "description": "Paid orders (subscription renewals excluded)"
          },
          "revenue": {
            "type": "number",
            "description": "Net revenue of those orders (refunds excluded), in `currency` (major units, e.g. 12.5)"
          },
          "viewRate": {
            "type": "number",
            "description": "productViews ÷ visits, percent with one decimal"
          },
          "checkoutRate": {
            "type": "number",
            "description": "checkouts ÷ productViews, percent with one decimal"
          },
          "paidRate": {
            "type": "number",
            "description": "paid ÷ checkouts, percent with one decimal"
          },
          "conversionRate": {
            "type": "number",
            "description": "paid ÷ visits, percent with one decimal"
          }
        }
      },
      "MrrSummary": {
        "type": "object",
        "properties": {
          "mrr": {
            "type": "number",
            "description": "Monthly recurring revenue at the end of the range (excluding tax), in `currency` (major units, e.g. 12.5)"
          },
          "mrrAtStart": {
            "type": "number",
            "description": "MRR on the day before the range, in `currency` (major units, e.g. 12.5)"
          },
          "arr": {
            "type": "number",
            "description": "MRR × 12, in `currency` (major units, e.g. 12.5)"
          },
          "subscribers": {
            "type": "integer",
            "description": "Paying subscribers at the end"
          },
          "subscribersAtStart": {
            "type": "integer"
          },
          "newMrr": {
            "type": "number",
            "description": "MRR from subscribers who started paying (reactivations included), in `currency` (major units, e.g. 12.5)"
          },
          "expansionMrr": {
            "type": "number",
            "description": "MRR added by subscribers who now pay more, in `currency` (major units, e.g. 12.5)"
          },
          "contractionMrr": {
            "type": "number",
            "description": "MRR lost by subscribers who now pay less, in `currency` (major units, e.g. 12.5)"
          },
          "churnedMrr": {
            "type": "number",
            "description": "MRR of subscribers whose paid period ended without a renewal, in `currency` (major units, e.g. 12.5)"
          },
          "netMrrMovement": {
            "type": "number",
            "description": "new + expansion − contraction − churned, in `currency` (major units, e.g. 12.5)"
          },
          "newSubscribers": {
            "type": "integer"
          },
          "churnedSubscribers": {
            "type": "integer"
          },
          "logoChurnRate": {
            "type": "number",
            "description": "Churned subscribers ÷ subscribers at the start, percent with one decimal",
            "nullable": true
          },
          "revenueChurnRate": {
            "type": "number",
            "description": "(churned + contraction MRR) ÷ MRR at the start, percent with one decimal",
            "nullable": true
          },
          "monthlyChurnRate": {
            "type": "number",
            "description": "Logo churn rescaled to 30 days, percent with one decimal",
            "nullable": true
          },
          "arpu": {
            "type": "number",
            "description": "Average revenue per subscriber: MRR ÷ subscribers, in `currency` (major units, e.g. 12.5)",
            "nullable": true
          },
          "ltv": {
            "type": "number",
            "description": "Lifetime value: ARPU ÷ monthly churn (null while nobody churned), in `currency` (major units, e.g. 12.5)",
            "nullable": true
          },
          "trials": {
            "type": "object",
            "nullable": true,
            "description": "Only when an offer has a free trial",
            "properties": {
              "started": {
                "type": "integer"
              },
              "ended": {
                "type": "integer",
                "description": "Trials whose period ended in the range"
              },
              "converted": {
                "type": "integer",
                "description": "Of those, how many paid"
              },
              "conversionRate": {
                "type": "number",
                "description": "converted ÷ ended, percent with one decimal",
                "nullable": true
              },
              "trialing": {
                "type": "integer",
                "description": "Trialing at the end of the range"
              }
            }
          }
        }
      },
      "Segment": {
        "type": "object",
        "description": "A saved audience segment",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6710b2c49e3f1a0012cd56ef"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "definition": {
            "type": "object",
            "required": [
              "groups"
            ],
            "description": "Rules in groups. Rules inside a group combine with the group's `match`; groups combine with the top-level `match` (`all` = AND, `any` = OR). Up to 10 groups of 20 rules.",
            "properties": {
              "match": {
                "type": "string",
                "enum": [
                  "all",
                  "any"
                ],
                "default": "all"
              },
              "groups": {
                "type": "array",
                "minItems": 1,
                "maxItems": 10,
                "items": {
                  "type": "object",
                  "required": [
                    "rules"
                  ],
                  "properties": {
                    "match": {
                      "type": "string",
                      "enum": [
                        "all",
                        "any"
                      ],
                      "default": "all"
                    },
                    "rules": {
                      "type": "array",
                      "minItems": 1,
                      "maxItems": 20,
                      "items": {
                        "type": "object",
                        "required": [
                          "type"
                        ],
                        "description": "One condition. `type` picks which other fields apply: `bought` / `notBought` (`productId`), `spent` (`op` gt|lt, `amountCents` in your account currency), `boughtWithin` / `notBoughtWithin` (`days`), `engagedWithin` (`days`, `event` opened|clicked|any), `tag` / `notTag` (`tag`), `country` (`countries`, ISO 3166-1 alpha-2), `source` (`source`).",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "bought",
                              "notBought",
                              "spent",
                              "boughtWithin",
                              "notBoughtWithin",
                              "engagedWithin",
                              "tag",
                              "notTag",
                              "country",
                              "source"
                            ]
                          },
                          "productId": {
                            "type": "string",
                            "description": "One of your products or subscription offers"
                          },
                          "op": {
                            "type": "string",
                            "enum": [
                              "gt",
                              "lt"
                            ],
                            "default": "gt"
                          },
                          "amountCents": {
                            "type": "integer",
                            "minimum": 0,
                            "example": 20000
                          },
                          "days": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 3650,
                            "example": 90
                          },
                          "event": {
                            "type": "string",
                            "enum": [
                              "opened",
                              "clicked",
                              "any"
                            ],
                            "default": "any"
                          },
                          "tag": {
                            "type": "string",
                            "example": "vip"
                          },
                          "countries": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "example": "FR"
                            }
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "form",
                              "checkout",
                              "import",
                              "dashboard",
                              "api",
                              "recommendation"
                            ],
                            "description": "How the contact joined: your store's form, a purchase, a CSV import, added by hand, or the API"
                          }
                        }
                      }
                    }
                  }
                }
              }
            },
            "example": {
              "match": "any",
              "groups": [
                {
                  "match": "all",
                  "rules": [
                    {
                      "type": "bought",
                      "productId": "665f1c2a9b3e4a0012ab34cd"
                    },
                    {
                      "type": "spent",
                      "op": "gt",
                      "amountCents": 20000
                    }
                  ]
                },
                {
                  "rules": [
                    {
                      "type": "tag",
                      "tag": "vip"
                    }
                  ]
                }
              ]
            }
          },
          "smartTag": {
            "type": "string",
            "nullable": true
          },
          "lastCount": {
            "type": "integer",
            "nullable": true,
            "description": "Active contacts at the last count (`GET /newsletters/segments/{id}/count` refreshes it)"
          },
          "lastCountAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CustomerProfileSummary": {
        "type": "object",
        "description": "A buyer's lifetime numbers in your store",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Customer profile id"
          },
          "customerId": {
            "type": "string",
            "nullable": true,
            "description": "The matching customer id, when there is one"
          },
          "email": {
            "type": "string",
            "example": "ada@example.com"
          },
          "firstName": {
            "type": "string",
            "nullable": true
          },
          "lastName": {
            "type": "string",
            "nullable": true
          },
          "country": {
            "type": "string",
            "nullable": true,
            "description": "ISO 3166-1 alpha-2 country of the latest purchase"
          },
          "currency": {
            "type": "string",
            "example": "USD",
            "description": "Your account currency (uppercase ISO)"
          },
          "ltvCents": {
            "type": "integer",
            "description": "Lifetime value: everything paid minus everything refunded, in cents of `currency` (your account currency)"
          },
          "grossCents": {
            "type": "integer",
            "description": "Everything paid, refunds included, in cents of `currency` (your account currency)"
          },
          "refundedCents": {
            "type": "integer",
            "description": "Everything refunded, in cents of `currency` (your account currency)"
          },
          "ordersCount": {
            "type": "integer",
            "description": "Paid orders and subscription payments, refunded ones included"
          },
          "refundsCount": {
            "type": "integer"
          },
          "averageOrderCents": {
            "type": "integer",
            "description": "Average paid order, in cents of `currency` (your account currency)"
          },
          "firstPurchaseAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastPurchaseAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Your CRM tags on this buyer"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the numbers were last rebuilt"
          }
        }
      },
      "CustomerNote": {
        "type": "object",
        "description": "A private note on a buyer. Only your store sees it.",
        "properties": {
          "_id": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "author": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "owner",
                  "member",
                  "api"
                ],
                "description": "The store owner, a team member or an API key"
              },
              "email": {
                "type": "string",
                "nullable": true,
                "description": "The author's email (owner or team member)"
              },
              "role": {
                "type": "string",
                "nullable": true,
                "description": "The team role of the author"
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CustomerProfile": {
        "type": "object",
        "description": "Everything about one buyer",
        "properties": {
          "customer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CustomerProfileSummary"
              }
            ],
            "description": "The buyer, with `customerSince`"
          },
          "ltv": {
            "type": "object",
            "properties": {
              "currency": {
                "type": "string"
              },
              "ltvCents": {
                "type": "integer"
              },
              "grossCents": {
                "type": "integer"
              },
              "refundedCents": {
                "type": "integer"
              },
              "ordersCount": {
                "type": "integer"
              },
              "refundsCount": {
                "type": "integer"
              },
              "averageOrderCents": {
                "type": "integer"
              },
              "oneTimeOrders": {
                "type": "integer"
              },
              "subscriptionPayments": {
                "type": "integer"
              },
              "firstPurchaseAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "lastPurchaseAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "products": {
            "type": "array",
            "description": "Products and subscription offers the buyer owns (refunded ones left out)",
            "items": {
              "type": "object",
              "properties": {
                "_id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "product",
                    "subscription"
                  ]
                },
                "thumbnail": {
                  "type": "string",
                  "nullable": true
                },
                "firstPurchasedAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "subscriptions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "_id": {
                  "type": "string",
                  "description": "Subscriber id"
                },
                "subscriptionId": {
                  "type": "string",
                  "nullable": true
                },
                "name": {
                  "type": "string",
                  "nullable": true
                },
                "status": {
                  "type": "string",
                  "nullable": true,
                  "example": "active"
                },
                "cancelAtPeriodEnd": {
                  "type": "boolean"
                },
                "currentPeriodEnd": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "startedAt": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                }
              }
            }
          },
          "email": {
            "type": "object",
            "description": "Your emails to this buyer: one count per email (a post opened three times is one open)",
            "properties": {
              "onList": {
                "type": "boolean",
                "description": "The buyer is a contact on your email list"
              },
              "status": {
                "type": "string",
                "nullable": true,
                "example": "active"
              },
              "contactId": {
                "type": "string",
                "nullable": true
              },
              "received": {
                "type": "integer"
              },
              "opened": {
                "type": "integer"
              },
              "clicked": {
                "type": "integer"
              },
              "openRate": {
                "type": "number",
                "nullable": true,
                "description": "Percent with one decimal"
              },
              "clickRate": {
                "type": "number",
                "nullable": true,
                "description": "Percent with one decimal"
              },
              "lastOpenedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "lastClickedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "recent": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "postId": {
                      "type": "string"
                    },
                    "subject": {
                      "type": "string"
                    },
                    "sentAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "opened": {
                      "type": "boolean"
                    },
                    "clicked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "segments": {
            "type": "array",
            "description": "Your audience segments the buyer's contact is in right now",
            "items": {
              "type": "object",
              "properties": {
                "_id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "smartTag": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          },
          "contactTags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tags on the buyer's email contact"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Your CRM tags"
          },
          "notes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerNote"
            }
          },
          "timeline": {
            "type": "array",
            "description": "Newest first, up to 100 events: `order`, `subscription_payment`, `refund` (with `saleId`, `itemId`, `itemName`, `amountCents`, `currency`), `subscription_started` / `subscription_canceled` (with `subscriptionCustomerId`), `note` (with `noteId`, `author`)",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "order",
                    "subscription_payment",
                    "refund",
                    "subscription_started",
                    "subscription_canceled",
                    "note"
                  ]
                },
                "at": {
                  "type": "string",
                  "format": "date-time"
                },
                "saleId": {
                  "type": "string"
                },
                "itemId": {
                  "type": "string",
                  "nullable": true
                },
                "itemName": {
                  "type": "string",
                  "nullable": true
                },
                "amountCents": {
                  "type": "integer"
                },
                "currency": {
                  "type": "string"
                },
                "refunded": {
                  "type": "boolean"
                },
                "subscriptionCustomerId": {
                  "type": "string"
                },
                "noteId": {
                  "type": "string"
                },
                "author": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          }
        }
      },
      "Benchmarks": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ready",
              "not_enough_stores",
              "no_data"
            ],
            "description": "`ready`: compared with a cohort. `not_enough_stores`: fewer than 10 similar stores yet. `no_data`: no live sales in the last 90 days (or not measured yet — the job runs nightly)."
          },
          "windowDays": {
            "type": "integer",
            "example": 90
          },
          "minimumStores": {
            "type": "integer",
            "example": 10,
            "description": "The smallest cohort ever shown"
          },
          "currency": {
            "type": "string",
            "example": "USD",
            "description": "Currency of the average order values (your account currency, converted at today's rate)"
          },
          "computedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "cohort": {
            "type": "object",
            "nullable": true,
            "properties": {
              "band": {
                "type": "string",
                "enum": [
                  "under_1k",
                  "1k_10k",
                  "10k_50k",
                  "50k_250k",
                  "250k_plus"
                ],
                "description": "Revenue band over the last 90 days (USD)"
              },
              "bandLabel": {
                "type": "string",
                "example": "$1k to $10k"
              },
              "category": {
                "type": "string",
                "enum": [
                  "all",
                  "digital",
                  "courses",
                  "subscriptions"
                ]
              },
              "categoryLabel": {
                "type": "string",
                "example": "Digital products"
              },
              "stores": {
                "type": "integer",
                "nullable": true,
                "description": "Stores in the cohort, rounded down to a multiple of 5"
              }
            }
          },
          "metrics": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "enum": [
                    "conversionRate",
                    "averageOrderValue",
                    "refundRate",
                    "emailOpenRate"
                  ]
                },
                "unit": {
                  "type": "string",
                  "enum": [
                    "percent",
                    "money"
                  ],
                  "description": "`money` values are cents of `currency`; `percent` values have one decimal"
                },
                "higherIsBetter": {
                  "type": "boolean"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "ready",
                    "not_enough_stores",
                    "no_data"
                  ]
                },
                "you": {
                  "type": "number",
                  "nullable": true,
                  "description": "Your value (null when you do not have enough data for this metric)"
                },
                "p25": {
                  "type": "number",
                  "nullable": true,
                  "description": "25th percentile of the cohort"
                },
                "median": {
                  "type": "number",
                  "nullable": true,
                  "description": "Median of the cohort"
                },
                "p75": {
                  "type": "number",
                  "nullable": true,
                  "description": "75th percentile of the cohort"
                },
                "stores": {
                  "type": "integer",
                  "nullable": true,
                  "description": "Stores behind this metric, rounded down to a multiple of 5"
                },
                "verdict": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "top",
                    "above",
                    "below",
                    "bottom"
                  ],
                  "description": "Read in the good direction: `top` = better than 75% of the cohort, `bottom` = worse than 75%"
                },
                "suggestion": {
                  "type": "object",
                  "nullable": true,
                  "description": "One next step when you are below the median",
                  "properties": {
                    "id": {
                      "type": "string",
                      "example": "upsells"
                    },
                    "feature": {
                      "type": "string",
                      "nullable": true,
                      "example": "upsells"
                    },
                    "href": {
                      "type": "string",
                      "example": "/upsells",
                      "description": "Dashboard path"
                    },
                    "text": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "SearchHit": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "6711a3d59e3f1a0012cd7788"
          },
          "type": {
            "type": "string",
            "enum": [
              "orders",
              "customers",
              "products",
              "subscriptions",
              "discounts"
            ]
          },
          "title": {
            "type": "string",
            "example": "Ana Lopez",
            "description": "Buyer name or email, product name, or discount code"
          },
          "subtitle": {
            "type": "string",
            "example": "Notion course · ana@example.com · #12CD7788"
          },
          "meta": {
            "type": "object",
            "description": "What the dashboard shows on the right of a result. Money is in major units of `currency` (49.99 = $49.99).",
            "properties": {
              "amount": {
                "type": "number",
                "example": 49
              },
              "currency": {
                "type": "string",
                "example": "USD"
              },
              "status": {
                "type": "string",
                "example": "paid",
                "description": "orders: `paid`, `refunded` or `disputed`; subscriptions: the subscription status (`active`, `trialing`, `past_due`, `canceled`…); products: `published` or `hidden`; discounts: `active`, `inactive` or `expired`"
              },
              "date": {
                "type": "string",
                "format": "date-time",
                "description": "When the order was placed / the record was created"
              },
              "interval": {
                "type": "string",
                "enum": [
                  "weekly",
                  "monthly",
                  "yearly"
                ],
                "description": "Subscriptions and subscription offers"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "product",
                  "subscription"
                ],
                "description": "products: a one-time product or a subscription offer"
              },
              "value": {
                "type": "number",
                "example": 20,
                "description": "discounts: the discount value"
              },
              "valueType": {
                "type": "string",
                "enum": [
                  "percentage",
                  "fixed"
                ],
                "description": "discounts"
              },
              "country": {
                "type": "string",
                "example": "ES",
                "description": "customers: two-letter country code"
              }
            }
          },
          "path": {
            "type": "string",
            "example": "/orders-one-time/6711a3d59e3f1a0012cd7788",
            "description": "Dashboard path that opens the record: https://app.pocketsflow.com + path"
          },
          "image": {
            "type": "string",
            "nullable": true,
            "description": "products: the cover image URL"
          },
          "match": {
            "type": "string",
            "enum": [
              "id",
              "email",
              "name",
              "amount",
              "code",
              "slug",
              "product"
            ],
            "description": "What the query matched (`product`: a subscriber of an offer whose name matched)"
          }
        },
        "required": [
          "id",
          "type",
          "title",
          "subtitle",
          "meta",
          "path",
          "match"
        ]
      },
      "SearchResults": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "example": "ana",
            "description": "The query as searched (trimmed, at most 100 characters)"
          },
          "testMode": {
            "type": "boolean",
            "description": "Whether test data was searched"
          },
          "limit": {
            "type": "integer",
            "example": 5
          },
          "groups": {
            "type": "array",
            "description": "One group per requested type, in this order: orders, customers, products, subscriptions, discounts. A team member only gets the types of the areas their teams have.",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "orders",
                    "customers",
                    "products",
                    "subscriptions",
                    "discounts"
                  ]
                },
                "hits": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SearchHit"
                  }
                },
                "hasMore": {
                  "type": "boolean",
                  "description": "More records match than `limit`"
                }
              }
            }
          }
        }
      },
      "UsageRecord": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "meter": {
            "type": "string",
            "example": "api_calls"
          },
          "quantity": {
            "type": "integer",
            "example": 250
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "idempotencyKey": {
            "type": "string",
            "nullable": true
          },
          "periodId": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "enum": [
              "api",
              "dashboard"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "UsagePeriod": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "closing",
              "closed"
            ]
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "overageCents": {
            "type": "integer",
            "description": "What the period charged on top of the subscription price, in cents"
          },
          "totals": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "One line per meter (and `seats` for extra seats)"
          },
          "charge": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "not_due",
                  "processing",
                  "paid",
                  "failed",
                  "due",
                  "uncollectible"
                ],
                "description": "`due` = recorded but not charged yet (no saved card, collection paused, no payouts account); `failed` = the card was declined. Both are retried 1, 3, 5 and 7 days after the first failure; then `uncollectible` (the creator is emailed)."
              },
              "reason": {
                "type": "string",
                "nullable": true
              },
              "paymentId": {
                "type": "string",
                "nullable": true
              },
              "message": {
                "type": "string",
                "nullable": true
              },
              "settledAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "retries": {
                "type": "integer",
                "description": "Scheduled retries started so far (at most 4)"
              },
              "nextRetryAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "saleId": {
                "type": "string",
                "nullable": true,
                "description": "The order (sale) the paid overage was recorded as — `billingReason: \"usage\"`"
              }
            }
          },
          "closedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "UsageSummary": {
        "type": "object",
        "properties": {
          "subscriber": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "subscriptionId": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "quantity": {
                "type": "integer",
                "description": "Seats now"
              },
              "purchasedQuantity": {
                "type": "integer",
                "description": "Seats the subscription price covers each period; seats above it are billed in arrears, prorated"
              }
            }
          },
          "pricing": {
            "type": "object",
            "description": "Seats and meters of a subscription offer. Send `{ \"seats\": { \"enabled\": false }, \"meters\": [] }` to switch back to flat pricing (always allowed).",
            "properties": {
              "seats": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Price per seat: the buyer picks seats at checkout and pays price × seats each period"
                  },
                  "minSeats": {
                    "type": "integer",
                    "minimum": 1,
                    "default": 1
                  },
                  "maxSeats": {
                    "type": "integer",
                    "nullable": true,
                    "minimum": 1,
                    "maximum": 10000,
                    "description": "`null` = no maximum"
                  },
                  "licenseKeys": {
                    "type": "boolean",
                    "description": "Issue each subscriber a license key whose activation limit follows their seats"
                  }
                }
              },
              "meters": {
                "type": "array",
                "maxItems": 5,
                "items": {
                  "type": "object",
                  "required": [
                    "key",
                    "name",
                    "pricing"
                  ],
                  "properties": {
                    "key": {
                      "type": "string",
                      "pattern": "^[a-z0-9][a-z0-9_.-]{0,39}$",
                      "description": "The meter's id — what you send as `meter` when reporting usage",
                      "example": "api_calls"
                    },
                    "name": {
                      "type": "string",
                      "maxLength": 60,
                      "example": "API calls"
                    },
                    "unit": {
                      "type": "string",
                      "maxLength": 30,
                      "description": "Singular unit name shown to buyers",
                      "example": "call"
                    },
                    "pricing": {
                      "type": "string",
                      "enum": [
                        "per_unit",
                        "graduated",
                        "volume"
                      ],
                      "description": "`per_unit`: every billable unit at `unitAmountCents`. `graduated`: each tier prices the units inside it. `volume`: every billable unit at the rate of the tier the total reaches."
                    },
                    "unitAmountCents": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "`per_unit` only: cents per `packageSize` units (`100` with `packageSize: 1000` = $1 per 1,000)",
                      "example": 100
                    },
                    "packageSize": {
                      "type": "integer",
                      "minimum": 1,
                      "default": 1,
                      "description": "Units one price applies to, so sub-cent unit prices stay exact. Each line is rounded half up to the cent.",
                      "example": 1000
                    },
                    "includedUnits": {
                      "type": "integer",
                      "minimum": 0,
                      "default": 0,
                      "description": "Free units per billing period before anything is charged",
                      "example": 10000
                    },
                    "tiers": {
                      "type": "array",
                      "maxItems": 10,
                      "items": {
                        "type": "object",
                        "required": [
                          "upTo",
                          "unitAmountCents"
                        ],
                        "properties": {
                          "upTo": {
                            "type": "integer",
                            "nullable": true,
                            "minimum": 1,
                            "description": "Inclusive upper bound in billable units (after included units). `null` on the last tier."
                          },
                          "unitAmountCents": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Cents per `packageSize` units in this tier"
                          },
                          "flatAmountCents": {
                            "type": "integer",
                            "minimum": 0,
                            "default": 0,
                            "description": "Flat cents added when any unit lands in this tier"
                          }
                        }
                      },
                      "description": "`graduated` / `volume` only; ascending `upTo`, last one `null`"
                    }
                  }
                }
              }
            }
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "currentPeriod": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "nullable": true
              },
              "start": {
                "type": "string",
                "format": "date-time"
              },
              "end": {
                "type": "string",
                "format": "date-time"
              },
              "status": {
                "type": "string"
              },
              "lines": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "kind": {
                      "type": "string",
                      "enum": [
                        "meter",
                        "seats"
                      ]
                    },
                    "meter": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "totalUnits": {
                      "type": "number"
                    },
                    "includedUnits": {
                      "type": "number"
                    },
                    "billableUnits": {
                      "type": "number"
                    },
                    "amountCents": {
                      "type": "integer"
                    }
                  }
                }
              },
              "upcomingOverageCents": {
                "type": "integer",
                "description": "What closing the period now would charge on top of the subscription price"
              }
            }
          },
          "daily": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "example": "2026-10-16"
                },
                "meter": {
                  "type": "string"
                },
                "units": {
                  "type": "integer"
                }
              }
            }
          },
          "records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageRecord"
            }
          },
          "periods": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsagePeriod"
            },
            "description": "The last 12 closed periods, newest first"
          },
          "outstandingCents": {
            "type": "integer",
            "description": "Overage from closed periods that is due, failed or uncollectible"
          },
          "entitled": {
            "type": "boolean",
            "description": "Whether your plan currently includes usage-based pricing"
          }
        }
      },
      "TrackedLinkStats": {
        "type": "object",
        "properties": {
          "clicks": {
            "type": "integer",
            "description": "Counted visits",
            "example": 412
          },
          "visitors": {
            "type": "integer",
            "description": "Unique visitors",
            "example": 361
          },
          "sales": {
            "type": "integer",
            "description": "Paid orders (and free claims) credited to the link",
            "example": 18
          },
          "revenue": {
            "type": "number",
            "description": "Revenue of those orders, in your account currency",
            "example": 846
          },
          "conversionRate": {
            "type": "number",
            "description": "Sales per unique visitor, in percent",
            "example": 4.99
          }
        }
      },
      "TrackedLinkDay": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "example": "2026-10-15"
          },
          "clicks": {
            "type": "integer",
            "example": 31
          },
          "visitors": {
            "type": "integer",
            "example": 27
          },
          "sales": {
            "type": "integer",
            "example": 2
          },
          "revenue": {
            "type": "number",
            "example": 94
          }
        }
      },
      "TrackedLink": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6711a3d59e3f1a0012cd7788"
          },
          "slug": {
            "type": "string",
            "example": "bio"
          },
          "label": {
            "type": "string",
            "example": "Instagram bio"
          },
          "channel": {
            "type": "string",
            "enum": [
              "instagram",
              "tiktok",
              "youtube",
              "x",
              "facebook",
              "email",
              "ads",
              "other"
            ],
            "description": "Where the link is posted. Groups links in the per-channel stats and fills blank UTM fields (`instagram` → `utm_source=instagram&utm_medium=social`).",
            "example": "instagram"
          },
          "destination": {
            "type": "string",
            "maxLength": 2048,
            "description": "Page the link opens. Must be on your own store: your Pocketsflow subdomain, a domain you connected, or a hosted checkout / product page of one of your products (`https://app.pocketsflow.com/checkout?productId=…`). A bare path (`/ebook`) is on your subdomain. Any other site is refused (400).",
            "example": "https://redmug.pocketsflow.com/ebook"
          },
          "utm": {
            "type": "object",
            "description": "UTM parameters the redirect adds to the destination (unless it already sets them). Blank fields are filled from the channel and the slug.",
            "properties": {
              "source": {
                "type": "string",
                "maxLength": 100,
                "example": "instagram"
              },
              "medium": {
                "type": "string",
                "maxLength": 100,
                "example": "social"
              },
              "campaign": {
                "type": "string",
                "maxLength": 100,
                "example": "bio"
              }
            }
          },
          "archived": {
            "type": "boolean",
            "description": "Hidden from the list; the short link keeps redirecting"
          },
          "shortUrl": {
            "type": "string",
            "description": "The link to post: on your connected domain when your plan includes custom domains, else your Pocketsflow subdomain",
            "example": "https://redmug.pocketsflow.com/go/bio"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CourseDripRule": {
        "type": "object",
        "description": "When the lesson unlocks for a buyer. `immediate` (default): at purchase. `afterDays`: `days` (1–3650) after the purchase. `onDate`: on `date` (ISO 8601); buyers who purchase after the date get it right away.",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "immediate",
              "afterDays",
              "onDate"
            ],
            "example": "afterDays"
          },
          "days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3650,
            "example": 7
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "example": "2026-11-02T09:00:00.000Z"
          }
        }
      },
      "CourseQuiz": {
        "type": "object",
        "properties": {
          "passMark": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 70,
            "description": "Score in percent a buyer needs to pass"
          },
          "mustPass": {
            "type": "boolean",
            "default": false,
            "description": "Later lessons stay locked until the buyer passes this quiz"
          },
          "questions": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "type": "object",
              "required": [
                "prompt",
                "options"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Kept when you send it back; generated otherwise",
                  "example": "q_9f8e7d6c"
                },
                "prompt": {
                  "type": "string",
                  "maxLength": 500,
                  "example": "How often should you post?"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "single",
                    "multiple"
                  ],
                  "default": "single",
                  "description": "`single`: exactly one correct option. `multiple`: one or more; the buyer must pick exactly the correct set."
                },
                "options": {
                  "type": "array",
                  "minItems": 2,
                  "maxItems": 10,
                  "items": {
                    "type": "object",
                    "required": [
                      "text"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "description": "Kept when you send it back; generated otherwise",
                        "example": "o_1a2b3c4d"
                      },
                      "text": {
                        "type": "string",
                        "maxLength": 300,
                        "example": "Once a week"
                      },
                      "correct": {
                        "type": "boolean",
                        "description": "A right answer",
                        "example": true
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "example": {
          "passMark": 70,
          "mustPass": true,
          "questions": [
            {
              "id": "q_9f8e7d6c",
              "prompt": "How often should you post?",
              "type": "single",
              "options": [
                {
                  "id": "o_1a2b3c4d",
                  "text": "Once a week",
                  "correct": true
                },
                {
                  "id": "o_5e6f7a8b",
                  "text": "Whenever you feel like it",
                  "correct": false
                }
              ]
            }
          ]
        }
      },
      "CourseSettings": {
        "type": "object",
        "properties": {
          "productId": {
            "type": "string",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "name": {
            "type": "string"
          },
          "entitled": {
            "type": "boolean",
            "description": "Your plan includes drip content. When false, buyers see every lesson open and no quizzes or certificates until you upgrade; your settings are kept."
          },
          "lessons": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "lessonId": {
                  "type": "string",
                  "example": "les_3f9a1c0b7d2e"
                },
                "title": {
                  "type": "string"
                },
                "module": {
                  "type": "string",
                  "description": "Module (section) name; empty when none"
                },
                "order": {
                  "type": "integer"
                },
                "drip": {
                  "$ref": "#/components/schemas/CourseDripRule"
                },
                "quiz": {
                  "$ref": "#/components/schemas/CourseQuiz",
                  "nullable": true
                }
              }
            }
          },
          "certificate": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              }
            }
          },
          "notifyOnUnlock": {
            "type": "boolean",
            "description": "Email buyers when a dripped lesson unlocks (they can turn it off)"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "example": {
          "productId": "665f1c2a9b3e4a0012ab34cd",
          "name": "Instagram growth course",
          "entitled": true,
          "lessons": [
            {
              "lessonId": "les_0a1b2c3d4e5f",
              "title": "Welcome",
              "module": "Start here",
              "order": 0,
              "drip": {
                "type": "immediate"
              },
              "quiz": null
            },
            {
              "lessonId": "les_3f9a1c0b7d2e",
              "title": "Your content plan",
              "module": "Week 1",
              "order": 1,
              "drip": {
                "type": "afterDays",
                "days": 7
              },
              "quiz": {
                "passMark": 70,
                "mustPass": true,
                "questions": [
                  {
                    "id": "q_9f8e7d6c",
                    "prompt": "How often should you post?",
                    "type": "single",
                    "options": [
                      {
                        "id": "o_1a2b3c4d",
                        "text": "Once a week",
                        "correct": true
                      },
                      {
                        "id": "o_5e6f7a8b",
                        "text": "Whenever you feel like it",
                        "correct": false
                      }
                    ]
                  }
                ]
              }
            }
          ],
          "certificate": {
            "enabled": true
          },
          "notifyOnUnlock": true,
          "updatedAt": "2026-10-16T09:00:00.000Z"
        }
      },
      "CourseStats": {
        "type": "object",
        "properties": {
          "enrolled": {
            "type": "integer",
            "description": "Purchases (refunds excluded)",
            "example": 240
          },
          "started": {
            "type": "integer",
            "description": "Buyers who completed a lesson or took a quiz",
            "example": 188
          },
          "completed": {
            "type": "integer",
            "description": "Buyers who finished every lesson",
            "example": 61
          },
          "certificates": {
            "type": "integer",
            "example": 54
          },
          "completionRate": {
            "type": "number",
            "description": "completed ÷ enrolled, in percent (one decimal)",
            "example": 25.4
          },
          "lessons": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "lessonId": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "completed": {
                  "type": "integer"
                },
                "quiz": {
                  "type": "object",
                  "nullable": true,
                  "properties": {
                    "takers": {
                      "type": "integer"
                    },
                    "passed": {
                      "type": "integer"
                    },
                    "attempts": {
                      "type": "integer"
                    },
                    "passRate": {
                      "type": "integer",
                      "description": "Percent of takers who passed"
                    },
                    "averageScore": {
                      "type": "integer",
                      "description": "Average best score, percent"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Certificate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "7K2QX9MD4HTR",
            "description": "Verify id (shown as 7K2Q-X9MD-4HTR)"
          },
          "name": {
            "type": "string",
            "example": "Maya Lopez"
          },
          "course": {
            "type": "string",
            "example": "Instagram growth course"
          },
          "creator": {
            "type": "string",
            "example": "Red Mug Studio"
          },
          "lessons": {
            "type": "integer",
            "example": 12
          },
          "issuedAt": {
            "type": "string",
            "format": "date-time"
          },
          "valid": {
            "type": "boolean",
            "example": true
          },
          "verifyUrl": {
            "type": "string",
            "example": "https://app.pocketsflow.com/certificate/7K2QX9MD4HTR"
          },
          "pdfUrl": {
            "type": "string",
            "example": "https://api.pocketsflow.com/certificates/7K2QX9MD4HTR/pdf"
          }
        }
      },
      "TeamMember": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Membership id"
          },
          "userId": {
            "type": "string",
            "description": "The person's Pocketsflow account id"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ]
          },
          "teamIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused"
            ],
            "description": "Paused members keep their seat history but can't open the store"
          },
          "pausedReason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "manual",
              "seat_limit"
            ],
            "description": "`seat_limit`: paused automatically after a plan change; they come back when seats free up"
          },
          "joinedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TeamInvite": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ]
          },
          "teamIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Invites expire after 7 days"
          },
          "expired": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Team": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "areas": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "products",
                "orders",
                "emails",
                "marketing",
                "analytics",
                "developers"
              ]
            },
            "description": "Parts of the store the team can use: `products` (products and store design), `orders` (orders, customers and refunds), `emails` (emails and automations), `marketing` (discounts, upsells and affiliates), `analytics`, `developers` (API keys and webhooks). Unknown values are dropped.",
            "example": [
              "orders",
              "emails"
            ]
          },
          "memberCount": {
            "type": "integer"
          }
        }
      },
      "TeamActivity": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "action": {
            "type": "string",
            "description": "`invite.created`, `invite.resent`, `invite.canceled`, `invite.accepted`, `member.updated`, `member.removed`, `member.left`, `member.paused_by_plan`, `member.resumed_by_plan`, `team.created`, `team.updated`, `team.deleted`, or `request` (a change a team member made in the store)"
          },
          "actor": {
            "type": "object",
            "properties": {
              "userId": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string",
                "nullable": true
              },
              "role": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "owner",
                  "admin",
                  "member"
                ]
              },
              "system": {
                "type": "boolean"
              }
            }
          },
          "targetLabel": {
            "type": "string",
            "nullable": true,
            "example": "PUT /products/665f1c2a9b3e4a0012ab34cd"
          },
          "area": {
            "type": "string",
            "nullable": true,
            "enum": [
              "products",
              "orders",
              "emails",
              "marketing",
              "analytics",
              "developers"
            ]
          }
        }
      },
      "PostPurchaseOffer": {
        "type": "object",
        "description": "“After purchase, offer…” on a product: right after a buyer pays for this product, the checkout shows one “One more thing” step offering `productId` at `discountPercent` off. Accepting charges the card they just used in one click; the result is a normal order (with its own `order.completed` and an `order.post_purchase_accepted` webhook).",
        "properties": {
          "enabled": {
            "type": "boolean",
            "example": true
          },
          "productId": {
            "type": "string",
            "description": "The product offered — another one-time product of yours, in the same mode (live/test), fixed price (not pay-what-you-want, not a credit pack), available to buy now. Empty when none is chosen.",
            "example": "665f1c2a9b3e4a0012ab34ce"
          },
          "discountPercent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 90,
            "description": "Percent off the offered product's price (0 = full price)",
            "example": 20
          }
        }
      },
      "SocialProofSettings": {
        "type": "object",
        "description": "What the checkout may show about real purchases of a product. Every number is real: live, non-refunded orders and live reviews only.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "example": true
          },
          "purchaseCount": {
            "type": "boolean",
            "description": "Show “N people bought this today / this week” once N reaches `threshold`",
            "example": true
          },
          "countWindow": {
            "type": "string",
            "enum": [
              "24h",
              "7d"
            ],
            "description": "The window the purchase count covers",
            "example": "24h"
          },
          "threshold": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000,
            "description": "Below this many purchases in the window, the count and the recent-purchase notice are hidden",
            "example": 3
          },
          "recentPurchases": {
            "type": "boolean",
            "description": "Show “Someone in Lyon bought this 2h ago” (city when the payment recorded one, otherwise country — never a name or email)",
            "example": true
          },
          "rating": {
            "type": "boolean",
            "description": "Show the product's average review rating",
            "example": true
          }
        }
      },
      "SocialProof": {
        "type": "object",
        "description": "The aggregate social proof a checkout shows for a product. Aggregates only — no name, email, order id or amount.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "False when the creator has it off or their plan no longer includes it; every other field is then null"
          },
          "purchases": {
            "type": "object",
            "nullable": true,
            "description": "Null below the creator's threshold",
            "properties": {
              "count": {
                "type": "integer",
                "example": 14
              },
              "window": {
                "type": "string",
                "enum": [
                  "24h",
                  "7d"
                ],
                "example": "24h"
              }
            }
          },
          "recent": {
            "type": "object",
            "nullable": true,
            "properties": {
              "place": {
                "type": "string",
                "description": "A city, or a two-letter country code",
                "example": "Lyon"
              },
              "placeKind": {
                "type": "string",
                "enum": [
                  "city",
                  "country"
                ]
              },
              "minutesAgo": {
                "type": "integer",
                "example": 120
              }
            }
          },
          "rating": {
            "type": "object",
            "nullable": true,
            "properties": {
              "average": {
                "type": "number",
                "example": 4.8
              },
              "count": {
                "type": "integer",
                "example": 15
              }
            }
          }
        }
      },
      "LoyaltySettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Buyers earn and can spend points. Turning it off stops both (balances are kept).",
            "example": true
          },
          "earnPointsPerUnit": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 1000,
            "description": "Points earned per 1.00 paid, in your currency (2 decimals)",
            "example": 1
          },
          "pointsPerCreditUnit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "description": "Points that make 1.00 of store credit",
            "example": 100
          },
          "minRedeemPoints": {
            "type": "integer",
            "minimum": 0,
            "description": "Smallest balance a buyer can spend (0 = any)",
            "example": 0
          },
          "expiryDays": {
            "type": "integer",
            "nullable": true,
            "minimum": 1,
            "maximum": 3650,
            "description": "Points expire this many days after the buyer's last purchase. `null` = never.",
            "example": 365
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "walletLinksConfigured": {
            "type": "boolean",
            "description": "Buyers can spend credit at checkout (always true in production). Earning works either way."
          },
          "planAllows": {
            "type": "boolean",
            "description": "Your current plan includes loyalty"
          }
        }
      },
      "LoyaltySummary": {
        "type": "object",
        "description": "What you owe your buyers in store credit (current mode), and what moved so far",
        "properties": {
          "members": {
            "type": "integer",
            "description": "Buyers with a balance record",
            "example": 412
          },
          "membersWithBalance": {
            "type": "integer",
            "example": 268
          },
          "outstandingPoints": {
            "type": "integer",
            "example": 51230
          },
          "outstandingCredit": {
            "type": "number",
            "description": "The outstanding points as store credit — your liability",
            "example": 512.3
          },
          "pointsEarned": {
            "type": "integer",
            "example": 98400
          },
          "pointsRedeemed": {
            "type": "integer",
            "example": 41200
          },
          "creditRedeemed": {
            "type": "number",
            "example": 412
          },
          "pointsExpired": {
            "type": "integer",
            "example": 5970
          },
          "orders": {
            "type": "integer",
            "description": "Orders paid partly with store credit (refunds excluded)",
            "example": 57
          },
          "currency": {
            "type": "string",
            "example": "usd"
          }
        }
      },
      "LoyaltyMember": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "6711b4e69e3f1a0012cd8800"
          },
          "email": {
            "type": "string",
            "example": "ana@example.com"
          },
          "points": {
            "type": "integer",
            "description": "Spendable points",
            "example": 1250
          },
          "credit": {
            "type": "number",
            "description": "The points as store credit",
            "example": 12.5
          },
          "lifetimeEarned": {
            "type": "integer",
            "example": 2400
          },
          "lifetimeRedeemed": {
            "type": "integer",
            "example": 1150
          },
          "lastEarnedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LoyaltyLedgerEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "example": "ana@example.com"
          },
          "type": {
            "type": "string",
            "enum": [
              "earn",
              "redeem",
              "reverse_earn",
              "restore",
              "expire",
              "adjust"
            ],
            "description": "earn (paid order), redeem (credit spent), reverse_earn / restore (the order was refunded), expire, adjust (you)"
          },
          "points": {
            "type": "integer",
            "description": "Signed",
            "example": 49
          },
          "credit": {
            "type": "number",
            "description": "Store credit the points paid for (redeem / restore)",
            "example": 0
          },
          "orderId": {
            "type": "string",
            "nullable": true,
            "example": "6711b4e69e3f1a0012cd88aa"
          },
          "note": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AffiliateProgram": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "example": true
          },
          "period": {
            "type": "string",
            "enum": [
              "all_time",
              "month",
              "quarter",
              "year"
            ],
            "description": "What the tier counts: sales this calendar month / quarter / year (UTC), or all time",
            "example": "month"
          },
          "tiers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "minSales": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "The tier applies once the affiliate has this many paid, non-refunded sales in the period before the sale (0 = from the first sale; 10 = from the 11th)",
                  "example": 10
                },
                "rate": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "Commission, percent of the sale",
                  "example": 25
                },
                "label": {
                  "type": "string",
                  "description": "The sale numbers the tier covers",
                  "example": "11–50"
                }
              }
            }
          },
          "bonuses": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "sales": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "All-time paid sales that earn the bonus (25 = the 25th sale)",
                  "example": 25
                },
                "amount": {
                  "type": "number",
                  "description": "Bonus, in your currency",
                  "example": 50
                }
              }
            }
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "periodStartsAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "periodEndsAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the period counter resets (null for all time)"
          },
          "planAllows": {
            "type": "boolean",
            "description": "Your current plan includes tiered affiliates"
          }
        }
      },
      "AffiliateTierProgress": {
        "type": "object",
        "description": "Where an affiliate stands on the ladder",
        "properties": {
          "ladder": {
            "type": "string",
            "enum": [
              "program",
              "affiliate",
              "flat"
            ],
            "description": "program (your tiers), affiliate (the affiliate's own tiers, all-time), flat (one rate)"
          },
          "period": {
            "type": "string",
            "enum": [
              "all_time",
              "month",
              "quarter",
              "year"
            ],
            "description": "What the tier counts: sales this calendar month / quarter / year (UTC), or all time",
            "example": "month"
          },
          "periodSales": {
            "type": "integer",
            "example": 13
          },
          "allTimeSales": {
            "type": "integer",
            "example": 41
          },
          "baseRate": {
            "type": "number",
            "example": 20
          },
          "rate": {
            "type": "number",
            "description": "The rate the next sale earns",
            "example": 25
          },
          "tier": {
            "type": "object",
            "nullable": true,
            "description": "Null while below the first tier",
            "properties": {
              "index": {
                "type": "integer",
                "example": 1
              },
              "minSales": {
                "type": "integer",
                "example": 10
              },
              "rate": {
                "type": "number",
                "example": 25
              },
              "label": {
                "type": "string",
                "example": "11–50"
              }
            }
          },
          "nextTier": {
            "type": "object",
            "nullable": true,
            "properties": {
              "index": {
                "type": "integer",
                "example": 2
              },
              "minSales": {
                "type": "integer",
                "example": 50
              },
              "rate": {
                "type": "number",
                "example": 30
              },
              "label": {
                "type": "string",
                "example": "51+"
              },
              "salesToGo": {
                "type": "integer",
                "example": 37
              }
            }
          },
          "nextBonus": {
            "type": "object",
            "nullable": true,
            "properties": {
              "sales": {
                "type": "integer",
                "example": 50
              },
              "amount": {
                "type": "number",
                "example": 100
              },
              "salesToGo": {
                "type": "integer",
                "example": 9
              }
            }
          },
          "periodEndsAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "tiers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "minSales": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "The tier applies once the affiliate has this many paid, non-refunded sales in the period before the sale (0 = from the first sale; 10 = from the 11th)",
                  "example": 10
                },
                "rate": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "Commission, percent of the sale",
                  "example": 25
                },
                "label": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "AffiliateBonus": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "affiliateId": {
            "type": "string",
            "example": "6711b4e69e3f1a0012cd8899"
          },
          "milestone": {
            "type": "integer",
            "example": 25
          },
          "amount": {
            "type": "number",
            "example": 50
          },
          "collected": {
            "type": "number",
            "description": "Collected from sales so far (paid out with them)",
            "example": 50
          },
          "paid": {
            "type": "number",
            "description": "Already paid out to the affiliate",
            "example": 50
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "earned",
              "funded"
            ],
            "description": "earned (reached; part still to be collected) or funded (fully collected)"
          },
          "earnedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "earnedOrderId": {
            "type": "string",
            "nullable": true,
            "example": "6711b4e69e3f1a0012cd88aa"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LimitedDrop": {
        "type": "object",
        "description": "“Only 100 copies” on a product. A buyer who opens the checkout holds one copy for 15 minutes; sales close on their own when every copy is sold. Counts are live.",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "quantity": {
            "type": "integer",
            "nullable": true,
            "minimum": 1,
            "maximum": 1000000,
            "description": "Copies for sale"
          },
          "restockOnRefund": {
            "type": "boolean",
            "description": "A refunded order's copy goes back on sale"
          },
          "sold": {
            "type": "integer",
            "description": "Copies paid for (or claimed) while the drop was on"
          },
          "reserved": {
            "type": "integer",
            "description": "Copies held by buyers on the checkout right now"
          },
          "remaining": {
            "type": "integer",
            "nullable": true,
            "description": "Copies a new buyer can still get"
          },
          "soldOut": {
            "type": "boolean"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the drop was last turned on"
          }
        },
        "example": {
          "enabled": true,
          "quantity": 100,
          "restockOnRefund": false,
          "sold": 63,
          "reserved": 2,
          "remaining": 35,
          "soldOut": false,
          "startedAt": "2026-10-16T09:00:00.000Z"
        }
      },
      "LimitedDropAvailability": {
        "type": "object",
        "description": "What a buyer's page shows. Everything is null (and `enabled` false) when the drop is off or the creator's plan no longer includes it.",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "quantity": {
            "type": "integer",
            "nullable": true
          },
          "remaining": {
            "type": "integer",
            "nullable": true
          },
          "sold": {
            "type": "integer",
            "nullable": true
          },
          "reserved": {
            "type": "integer",
            "nullable": true
          },
          "soldOut": {
            "type": "boolean"
          }
        },
        "example": {
          "enabled": true,
          "quantity": 100,
          "remaining": 35,
          "sold": 63,
          "reserved": 2,
          "soldOut": false
        }
      },
      "PdfStamping": {
        "type": "object",
        "description": "Every PDF a buyer downloads carries their email, order number and date in a small footer on each page. Other files are delivered untouched.",
        "properties": {
          "enabled": {
            "type": "boolean"
          }
        },
        "example": {
          "enabled": true
        }
      },
      "DownloadLimits": {
        "type": "object",
        "description": "Per product: how many times each file can be downloaded per order, and for how many days after the purchase. Only orders placed after the limits were turned on are limited, unless `applyToExisting` is true.",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "maxDownloads": {
            "type": "integer",
            "nullable": true,
            "minimum": 1,
            "maximum": 1000,
            "description": "Downloads per file per order; null = no count limit"
          },
          "expiryDays": {
            "type": "integer",
            "nullable": true,
            "minimum": 1,
            "maximum": 3650,
            "description": "Days the download links work after the purchase (or after you last reset the buyer's downloads); null = never expire"
          },
          "applyToExisting": {
            "type": "boolean",
            "description": "Also limit orders placed before the limits were turned on"
          },
          "appliesFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the limits were last turned on"
          }
        },
        "example": {
          "enabled": true,
          "maxDownloads": 5,
          "expiryDays": 7,
          "applyToExisting": false,
          "appliesFrom": "2026-10-16T09:00:00.000Z"
        }
      },
      "OrderDownloads": {
        "type": "object",
        "description": "What a buyer has downloaded and has left on one order.",
        "properties": {
          "limited": {
            "type": "boolean",
            "description": "Download limits apply to this order"
          },
          "maxDownloads": {
            "type": "integer",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "expired": {
            "type": "boolean"
          },
          "remaining": {
            "type": "integer",
            "nullable": true,
            "description": "Fewest downloads left across the order's files"
          },
          "exhausted": {
            "type": "boolean",
            "description": "Every file has used up its downloads"
          },
          "files": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "example": "main"
                },
                "name": {
                  "type": "string"
                },
                "used": {
                  "type": "integer"
                },
                "remaining": {
                  "type": "integer",
                  "nullable": true
                }
              }
            }
          },
          "stamped": {
            "type": "boolean",
            "description": "The order's PDFs carry the buyer's email"
          },
          "totalDownloads": {
            "type": "integer"
          },
          "lastDownloadAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "resetAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "resetCount": {
            "type": "integer"
          }
        },
        "example": {
          "limited": true,
          "maxDownloads": 5,
          "expiresAt": "2026-10-23T09:12:00.000Z",
          "expired": false,
          "remaining": 3,
          "exhausted": false,
          "files": [
            {
              "key": "main",
              "name": "Field Guide",
              "used": 2,
              "remaining": 3
            }
          ],
          "stamped": true,
          "totalDownloads": 2,
          "lastDownloadAt": "2026-10-16T10:02:00.000Z",
          "resetAt": null,
          "resetCount": 0
        }
      },
      "Testimonial": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "6712b0c49e3f1a0012cd9911"
          },
          "productId": {
            "type": "string",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "productName": {
            "type": "string",
            "example": "Notion Creator Kit"
          },
          "saleId": {
            "type": "string",
            "example": "6711a3d59e3f1a0012cd7799"
          },
          "buyerId": {
            "type": "string",
            "description": "Customer id, when the buyer has one"
          },
          "buyerEmail": {
            "type": "string",
            "example": "jordan@example.com"
          },
          "displayName": {
            "type": "string",
            "description": "The name buyers see: the one the buyer chose, else their masked email",
            "example": "Jordan S."
          },
          "rating": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "example": 5
          },
          "text": {
            "type": "string",
            "example": "Saved me hours every week."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "hidden"
            ],
            "description": "`approved` shows on the wall. A review left on the thank-you page with words starts as `pending`."
          },
          "featured": {
            "type": "boolean",
            "description": "Shown first on the wall and on checkout"
          },
          "source": {
            "type": "string",
            "enum": [
              "request",
              "thank_you"
            ],
            "description": "`request`: sent from the emailed request page. `thank_you`: a review left right after buying."
          },
          "consent": {
            "type": "boolean",
            "description": "The buyer ticked “you may show this on your pages”"
          },
          "video": {
            "type": "object",
            "nullable": true,
            "description": "The buyer's video, when they sent one",
            "properties": {
              "url": {
                "type": "string",
                "example": "https://d372ebr33sbljd.cloudfront.net/testimonials/auth0%7C123/9f2c1e.webm"
              },
              "contentType": {
                "type": "string",
                "enum": [
                  "video/webm",
                  "video/mp4",
                  "video/quicktime"
                ]
              },
              "durationSeconds": {
                "type": "number",
                "nullable": true,
                "example": 41.2
              }
            }
          },
          "testMode": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "moderatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "TestimonialSettings": {
        "type": "object",
        "properties": {
          "requestEnabled": {
            "type": "boolean",
            "description": "Email buyers after their purchase asking for a testimonial (once per order, at most once a week per buyer, never for orders made before you turned it on)"
          },
          "delayDays": {
            "type": "integer",
            "minimum": 1,
            "maximum": 60,
            "example": 7,
            "description": "Days after the purchase before the email goes out"
          },
          "askVideo": {
            "type": "boolean",
            "description": "Offer the video recorder on the request page"
          },
          "wallEnabled": {
            "type": "boolean",
            "description": "Show approved testimonials on the product page"
          },
          "checkoutStrip": {
            "type": "boolean",
            "description": "Show up to two featured testimonials on checkout"
          }
        }
      },
      "AttributionSurvey": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "question": {
            "type": "string",
            "maxLength": 120,
            "example": "How did you hear about us?"
          },
          "options": {
            "type": "array",
            "minItems": 2,
            "maxItems": 12,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "example": "podcast"
                },
                "label": {
                  "type": "string",
                  "maxLength": 60,
                  "example": "A podcast"
                },
                "channel": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "instagram",
                    "tiktok",
                    "youtube",
                    "x",
                    "facebook",
                    "email",
                    "ads",
                    "other"
                  ],
                  "description": "The tracked-link channel this answer is compared with in the report"
                }
              }
            }
          },
          "allowOther": {
            "type": "boolean",
            "description": "Add an \"Other\" answer with a free-text field"
          }
        }
      },
      "FunnelStats": {
        "type": "object",
        "description": "The last computed numbers (refreshed by `GET /funnels/{id}/stats`).",
        "properties": {
          "leads": {
            "type": "integer"
          },
          "emailsSent": {
            "type": "integer"
          },
          "opens": {
            "type": "integer",
            "description": "People who opened, per email, summed"
          },
          "clicks": {
            "type": "integer",
            "description": "People who clicked, per email, summed"
          },
          "purchases": {
            "type": "integer",
            "description": "Attributed offer orders"
          },
          "revenue": {
            "type": "number",
            "description": "Attributed offer revenue, in `currency` (your account currency)"
          },
          "conversionRate": {
            "type": "number",
            "description": "Leads who bought the offer ÷ leads, in percent"
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "Funnel": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6710c3d59e3f1a0012cd7a01"
          },
          "name": {
            "type": "string"
          },
          "templateId": {
            "type": "string",
            "enum": [
              "welcome",
              "minicourse",
              "launch"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "paused"
            ],
            "description": "`paused` also when the funnel's automation was switched off directly"
          },
          "leadMagnetProductId": {
            "type": "string"
          },
          "offerProductId": {
            "type": "string"
          },
          "offerUrl": {
            "type": "string",
            "description": "Where the offer emails link (without the discount code)"
          },
          "offerDiscount": {
            "type": "object",
            "nullable": true,
            "properties": {
              "discountId": {
                "type": "string"
              },
              "code": {
                "type": "string"
              },
              "valueType": {
                "type": "string",
                "enum": [
                  "percentage",
                  "fixed"
                ]
              },
              "value": {
                "type": "number"
              },
              "expiresAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "automationId": {
            "type": "string",
            "description": "The automation the template created (the email sequence)"
          },
          "emailPostIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The email posts the template created, in send order"
          },
          "leadTag": {
            "type": "string",
            "description": "Audience tag every lead gets"
          },
          "activatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "stats": {
            "$ref": "#/components/schemas/FunnelStats"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FunnelDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Funnel"
          },
          {
            "type": "object",
            "properties": {
              "leadMagnet": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "_id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "price": {
                    "type": "number"
                  },
                  "payWant": {
                    "type": "boolean"
                  },
                  "minPrice": {
                    "type": "number"
                  },
                  "published": {
                    "type": "boolean"
                  },
                  "thumbnail": {
                    "type": "string",
                    "nullable": true
                  },
                  "free": {
                    "type": "boolean",
                    "description": "$0 to get (a $0 price, or pay what you want from $0)"
                  }
                }
              },
              "offer": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "_id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "price": {
                    "type": "number"
                  },
                  "payWant": {
                    "type": "boolean"
                  },
                  "minPrice": {
                    "type": "number"
                  },
                  "published": {
                    "type": "boolean"
                  },
                  "thumbnail": {
                    "type": "string",
                    "nullable": true
                  },
                  "free": {
                    "type": "boolean",
                    "description": "$0 to get (a $0 price, or pay what you want from $0)"
                  }
                }
              },
              "automation": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "_id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "inactive",
                      "draft"
                    ]
                  }
                }
              },
              "emails": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "nodeId": {
                      "type": "string"
                    },
                    "postId": {
                      "type": "string",
                      "nullable": true
                    },
                    "sendAfterHours": {
                      "type": "number",
                      "description": "Hours after the lead magnet was claimed"
                    },
                    "subject": {
                      "type": "string",
                      "nullable": true
                    },
                    "preview": {
                      "type": "string",
                      "nullable": true
                    },
                    "missing": {
                      "type": "boolean",
                      "description": "The node points at no existing email post"
                    }
                  }
                }
              },
              "checklist": {
                "type": "object",
                "description": "What activation needs",
                "properties": {
                  "senderVerified": {
                    "type": "boolean"
                  },
                  "leadMagnetPublished": {
                    "type": "boolean"
                  },
                  "leadMagnetFree": {
                    "type": "boolean"
                  },
                  "offerPublished": {
                    "type": "boolean"
                  },
                  "emailsReady": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        ]
      },
      "FunnelAnalytics": {
        "type": "object",
        "properties": {
          "funnelId": {
            "type": "string"
          },
          "attributionWindowDays": {
            "type": "integer",
            "example": 30
          },
          "totals": {
            "type": "object",
            "properties": {
              "leads": {
                "type": "integer"
              },
              "emailsSent": {
                "type": "integer"
              },
              "opens": {
                "type": "integer"
              },
              "clicks": {
                "type": "integer"
              },
              "purchases": {
                "type": "integer"
              },
              "buyers": {
                "type": "integer",
                "description": "Leads who bought the offer"
              },
              "revenue": {
                "type": "number"
              },
              "conversionRate": {
                "type": "number"
              },
              "currency": {
                "type": "string"
              }
            }
          },
          "stages": {
            "type": "object",
            "description": "Unique people at each step",
            "properties": {
              "leads": {
                "type": "integer"
              },
              "opened": {
                "type": "integer"
              },
              "clicked": {
                "type": "integer"
              },
              "purchased": {
                "type": "integer"
              }
            }
          },
          "emails": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "nodeId": {
                  "type": "string"
                },
                "postId": {
                  "type": "string",
                  "nullable": true
                },
                "subject": {
                  "type": "string",
                  "nullable": true
                },
                "sendAfterHours": {
                  "type": "number"
                },
                "sent": {
                  "type": "integer"
                },
                "opened": {
                  "type": "integer",
                  "description": "Unique people"
                },
                "clicked": {
                  "type": "integer",
                  "description": "Unique people"
                }
              }
            }
          }
        }
      },
      "FunnelTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "welcome",
              "minicourse",
              "launch"
            ]
          },
          "emails": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "subject": {
                  "type": "string"
                },
                "sendAfterHours": {
                  "type": "number"
                },
                "offer": {
                  "type": "string",
                  "enum": [
                    "full",
                    "ps",
                    "none"
                  ],
                  "description": "`full`: pitches the offer with a button (and the discount); `ps`: a one-line P.S. link; `none`: value only"
                }
              }
            }
          }
        }
      },
      "RevenueSplitTotals": {
        "type": "object",
        "description": "Totals in one currency",
        "properties": {
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "pendingCents": {
            "type": "integer",
            "description": "Recorded, not paid yet, in cents of `currency`"
          },
          "heldCents": {
            "type": "integer",
            "description": "Held (account under review), not paid, in cents of `currency`"
          },
          "paidCents": {
            "type": "integer",
            "description": "Paid out, in cents of `currency`"
          },
          "reversedCents": {
            "type": "integer",
            "description": "Reversed by refunds and disputes, in cents of `currency`"
          },
          "clawbackOwedCents": {
            "type": "integer",
            "description": "Paid shares refunded later, netted from the next payout, in cents of `currency`"
          },
          "shareCount": {
            "type": "integer",
            "description": "Ledger rows counted"
          }
        }
      },
      "RevenueSplit": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "6710c3d59e3f1a0012cd7c01"
          },
          "productId": {
            "type": "string"
          },
          "product": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "thumbnail": {
                "type": "string",
                "nullable": true,
                "description": "Image id or URL of the product thumbnail"
              }
            }
          },
          "owner": {
            "type": "object",
            "nullable": true,
            "description": "The product owner's store (in a co-creator's view)",
            "properties": {
              "name": {
                "type": "string"
              },
              "subdomain": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "collaboratorEmail": {
            "type": "string",
            "description": "The email the invite was sent to"
          },
          "collaborator": {
            "type": "object",
            "nullable": true,
            "description": "The co-creator's name once they accepted",
            "properties": {
              "name": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "sharePercent": {
            "type": "number",
            "description": "The co-creator's share of the split base, in percent",
            "example": 30
          },
          "shareBps": {
            "type": "integer",
            "description": "The same share in basis points (3000 = 30%)"
          },
          "status": {
            "type": "string",
            "enum": [
              "invited",
              "active",
              "declined",
              "removed",
              "expired"
            ],
            "description": "`invited` (waiting for the co-creator), `active` (sales are split), `declined`, `removed` (by the owner, or the co-creator left), `expired` (invite not accepted within 14 days)"
          },
          "invitedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "inviteExpiresAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "acceptedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Sales from this moment on are split"
          },
          "declinedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "removedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Sales from this moment on are no longer split"
          },
          "totals": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RevenueSplitTotals"
            }
          }
        }
      },
      "RevenueSplitSale": {
        "type": "object",
        "description": "How one sale was split. The owner sees every share; a co-creator only their own (and `ownerCents` is null).",
        "properties": {
          "saleId": {
            "type": "string",
            "description": "The order id"
          },
          "productId": {
            "type": "string"
          },
          "productName": {
            "type": "string",
            "nullable": true
          },
          "saleDate": {
            "type": "string",
            "format": "date-time"
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "saleAmountCents": {
            "type": "integer",
            "description": "Pre-tax product amount, in cents of `currency`"
          },
          "platformFeeCents": {
            "type": "integer",
            "description": "Platform fee, taken once on the full sale, in cents of `currency`"
          },
          "affiliateCents": {
            "type": "integer",
            "description": "Affiliate commission, in cents of `currency`"
          },
          "baseCents": {
            "type": "integer",
            "description": "What is split: sale − platform fee − affiliate, in cents of `currency`"
          },
          "ownerCents": {
            "type": "integer",
            "nullable": true,
            "description": "The owner's part (base − every share, rounding remainders included), in cents"
          },
          "shares": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "splitId": {
                  "type": "string"
                },
                "collaboratorEmail": {
                  "type": "string"
                },
                "collaboratorName": {
                  "type": "string",
                  "nullable": true
                },
                "sharePercent": {
                  "type": "number"
                },
                "amountCents": {
                  "type": "integer",
                  "description": "This co-creator's share, in cents of `currency`"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "pending",
                    "held",
                    "paid",
                    "reversed"
                  ],
                  "description": "`pending` (recorded, not paid yet), `held` (an account is under review), `paid`, `reversed` (refunded or disputed before it was paid)"
                },
                "reversalReason": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "refunded",
                    "disputed",
                    null
                  ]
                },
                "clawbackCents": {
                  "type": "integer",
                  "description": "Paid, then refunded: owed back and netted from the next payout"
                },
                "paidAt": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                }
              }
            }
          }
        }
      },
      "RevenueSplitEarningEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "splitId": {
            "type": "string"
          },
          "saleId": {
            "type": "string"
          },
          "productId": {
            "type": "string"
          },
          "productName": {
            "type": "string",
            "nullable": true
          },
          "saleDate": {
            "type": "string",
            "format": "date-time"
          },
          "currency": {
            "type": "string"
          },
          "saleAmountCents": {
            "type": "integer",
            "description": "Pre-tax product amount, in cents of `currency`"
          },
          "platformFeeCents": {
            "type": "integer",
            "description": "Platform fee, in cents of `currency`"
          },
          "affiliateCents": {
            "type": "integer",
            "description": "Affiliate commission, in cents of `currency`"
          },
          "baseCents": {
            "type": "integer",
            "description": "Split base, in cents of `currency`"
          },
          "sharePercent": {
            "type": "number"
          },
          "amountCents": {
            "type": "integer",
            "description": "Your share, in cents of `currency`"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "held",
              "paid",
              "reversed"
            ]
          },
          "reversalReason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "refunded",
              "disputed",
              null
            ]
          },
          "clawbackCents": {
            "type": "integer"
          },
          "clawbackStatus": {
            "type": "string",
            "nullable": true,
            "enum": [
              "owed",
              "recovered",
              null
            ]
          },
          "paidAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "Membership": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6711a0c4e1b2c3d4e5f60a01"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "description": "The members area lives at `https://<subdomain>.pocketsflow.com/members/<slug>`"
          },
          "description": {
            "type": "string"
          },
          "offerIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Subscription offers whose live subscribers are members"
          },
          "productIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Your products members can open while subscribed"
          },
          "communityEnabled": {
            "type": "boolean"
          },
          "notifyByEmail": {
            "type": "boolean",
            "description": "Email members when you publish a post (they can opt out)"
          },
          "archived": {
            "type": "boolean",
            "description": "Archived: the members area is closed and everyone gets `member.left`"
          },
          "membersUrl": {
            "type": "string"
          },
          "offers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "price": {
                  "type": "number"
                },
                "frequency": {
                  "type": "string",
                  "enum": [
                    "weekly",
                    "monthly",
                    "yearly"
                  ]
                },
                "currency": {
                  "type": "string"
                },
                "priceLabel": {
                  "type": "string",
                  "nullable": true
                },
                "published": {
                  "type": "boolean"
                }
              }
            }
          },
          "products": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "thumbnail": {
                  "type": "string",
                  "nullable": true
                },
                "productType": {
                  "type": "string",
                  "enum": [
                    "file",
                    "url",
                    "course",
                    "credits"
                  ]
                }
              }
            }
          },
          "stats": {
            "type": "object",
            "properties": {
              "activeMembers": {
                "type": "integer",
                "description": "People with a live subscription right now"
              },
              "posts": {
                "type": "integer",
                "description": "Published posts"
              },
              "threads": {
                "type": "integer",
                "description": "Visible community threads"
              },
              "joinedLast30Days": {
                "type": "integer"
              },
              "leftLast30Days": {
                "type": "integer"
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MemberPost": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6711a0c4e1b2c3d4e5f60b01"
          },
          "membershipId": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "html": {
            "type": "string",
            "description": "The post, as sanitised HTML (paragraphs, headings, lists, links, quotes, code and https images). Not included in lists."
          },
          "excerpt": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ]
          },
          "pinned": {
            "type": "boolean",
            "description": "Pinned posts lead the feed"
          },
          "publishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "notify": {
            "type": "boolean",
            "description": "Email members when published"
          },
          "notifyState": {
            "type": "string",
            "enum": [
              "off",
              "pending",
              "sending",
              "sent"
            ],
            "description": "Where the new-post emails are"
          },
          "notifiedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "notifiedCount": {
            "type": "integer",
            "description": "Emails sent for this post"
          },
          "reactions": {
            "type": "object",
            "description": "Reaction counts by kind (like, love, insightful)",
            "properties": {
              "like": {
                "type": "integer",
                "example": 0
              },
              "love": {
                "type": "integer",
                "example": 0
              },
              "insightful": {
                "type": "integer",
                "example": 0
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CommunityThread": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6711a0c4e1b2c3d4e5f60c01"
          },
          "membershipId": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": "string",
            "description": "Plain text"
          },
          "author": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "member",
                  "creator"
                ]
              },
              "name": {
                "type": "string",
                "description": "The name other members see"
              },
              "email": {
                "type": "string",
                "nullable": true,
                "description": "The member's email (null for your own posts). Never shown to other members."
              },
              "banned": {
                "type": "boolean",
                "description": "The author is banned from your communities"
              }
            }
          },
          "pinned": {
            "type": "boolean"
          },
          "locked": {
            "type": "boolean",
            "description": "No new replies"
          },
          "hidden": {
            "type": "boolean",
            "description": "Hidden from members (only you see it)"
          },
          "replyCount": {
            "type": "integer",
            "description": "Visible replies (hidden ones not counted)"
          },
          "reactions": {
            "type": "object",
            "description": "Reaction counts by kind (like, love, insightful)",
            "properties": {
              "like": {
                "type": "integer",
                "example": 0
              },
              "love": {
                "type": "integer",
                "example": 0
              },
              "insightful": {
                "type": "integer",
                "example": 0
              }
            }
          },
          "lastActivityAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CommunityReply": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6711a0c4e1b2c3d4e5f60d01"
          },
          "threadId": {
            "type": "string"
          },
          "body": {
            "type": "string",
            "description": "Plain text"
          },
          "author": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "member",
                  "creator"
                ]
              },
              "name": {
                "type": "string",
                "description": "The name other members see"
              },
              "email": {
                "type": "string",
                "nullable": true,
                "description": "The member's email (null for your own posts). Never shown to other members."
              },
              "banned": {
                "type": "boolean",
                "description": "The author is banned from your communities"
              }
            }
          },
          "hidden": {
            "type": "boolean"
          },
          "reactions": {
            "type": "object",
            "description": "Reaction counts by kind (like, love, insightful)",
            "properties": {
              "like": {
                "type": "integer",
                "example": 0
              },
              "love": {
                "type": "integer",
                "example": 0
              },
              "insightful": {
                "type": "integer",
                "example": 0
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MembershipMember": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Name on their subscription"
          },
          "displayName": {
            "type": "string",
            "description": "The name other members see"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "ended"
            ],
            "description": "`active` while a subscription to one of the offers is live"
          },
          "subscriptionStatus": {
            "type": "string",
            "nullable": true,
            "description": "Their live subscription's status, else their latest"
          },
          "banned": {
            "type": "boolean"
          },
          "bannedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "banReason": {
            "type": "string",
            "nullable": true
          },
          "emailOptOut": {
            "type": "boolean",
            "description": "Turned off new-post emails"
          },
          "offerIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "joinedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "leftAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "PaymentPlanSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Offer “Pay in N” on this product's checkout. Needs a fixed price (not pay what you want)."
          },
          "installments": {
            "type": "integer",
            "minimum": 2,
            "maximum": 6,
            "description": "Number of payments, 30 days apart (default 3)"
          },
          "surchargePercent": {
            "type": "number",
            "minimum": 0,
            "maximum": 25,
            "description": "Optional payment plan fee on the whole price, in percent (up to two decimals, default 0)"
          }
        },
        "example": {
          "enabled": true,
          "installments": 3,
          "surchargePercent": 0
        }
      },
      "ExitOfferSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Show a one-time discount when a buyer is about to leave the checkout"
          },
          "valueType": {
            "type": "string",
            "enum": [
              "percentage",
              "fixed"
            ]
          },
          "value": {
            "type": "number",
            "description": "Whole percent (1–90) for `percentage`, or an amount in your currency for `fixed`"
          },
          "discountId": {
            "type": "string",
            "description": "The discount that carries the offer's code (it appears in your discounts)"
          }
        },
        "example": {
          "enabled": true,
          "valueType": "percentage",
          "value": 10,
          "discountId": "6710b2c49e3f1a0012cd56ef"
        }
      },
      "DownsellSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Offer a cheaper product when a buyer removes an order bump at checkout"
          },
          "productId": {
            "type": "string",
            "description": "Another of your products, in the same mode"
          },
          "price": {
            "type": "number",
            "description": "Its price on this offer; 0 sells it at its own price"
          }
        },
        "example": {
          "enabled": true,
          "productId": "665f1c2a9b3e4a0012ab34ce",
          "price": 19
        }
      },
      "CheckoutOfferSettings": {
        "type": "object",
        "properties": {
          "paymentPlan": {
            "$ref": "#/components/schemas/PaymentPlanSettings"
          },
          "exitOffer": {
            "$ref": "#/components/schemas/ExitOfferSettings"
          },
          "downsell": {
            "$ref": "#/components/schemas/DownsellSettings"
          }
        }
      },
      "CheckoutOffers": {
        "type": "object",
        "description": "What a product's checkout offers right now. An offer is `null` when it is off, or when the seller's plan no longer includes it.",
        "properties": {
          "paymentPlan": {
            "type": "object",
            "nullable": true,
            "properties": {
              "installments": {
                "type": "integer"
              },
              "surchargePercent": {
                "type": "number"
              },
              "intervalDays": {
                "type": "integer",
                "example": 30
              }
            }
          },
          "exitOffer": {
            "type": "object",
            "nullable": true,
            "properties": {
              "valueType": {
                "type": "string",
                "enum": [
                  "percentage",
                  "fixed"
                ]
              },
              "value": {
                "type": "number"
              }
            }
          },
          "downsell": {
            "type": "object",
            "nullable": true,
            "properties": {
              "productId": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "thumbnail": {
                "type": "string"
              },
              "price": {
                "type": "number"
              }
            }
          }
        },
        "example": {
          "paymentPlan": {
            "installments": 3,
            "surchargePercent": 0,
            "intervalDays": 30
          },
          "exitOffer": {
            "valueType": "percentage",
            "value": 10
          },
          "downsell": {
            "productId": "665f1c2a9b3e4a0012ab34ce",
            "name": "Notion OS Lite",
            "thumbnail": "",
            "price": 19
          }
        }
      },
      "FlashSale": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "6711d4e59e3f1a0012cd8b01"
          },
          "name": {
            "type": "string",
            "example": "Black Friday"
          },
          "startsAt": {
            "type": "string",
            "format": "date-time"
          },
          "endsAt": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "live",
              "ended"
            ]
          },
          "valueType": {
            "type": "string",
            "enum": [
              "percentage",
              "fixed"
            ]
          },
          "value": {
            "type": "number",
            "example": 30
          },
          "allProducts": {
            "type": "boolean"
          },
          "productIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "announcementPostId": {
            "type": "string",
            "nullable": true,
            "description": "The announcement email draft, once created"
          },
          "stats": {
            "type": "object",
            "properties": {
              "sales": {
                "type": "integer"
              },
              "revenue": {
                "type": "number",
                "description": "Product revenue at the sale price, your currency"
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "example": {
          "id": "6711d4e59e3f1a0012cd8b01",
          "name": "Black Friday",
          "startsAt": "2026-11-27T08:00:00.000Z",
          "endsAt": "2026-11-30T23:59:00.000Z",
          "status": "scheduled",
          "valueType": "percentage",
          "value": 30,
          "allProducts": true,
          "productIds": [],
          "announcementPostId": null,
          "stats": {
            "sales": 0,
            "revenue": 0
          },
          "createdAt": "2026-10-16T10:00:00.000Z"
        }
      },
      "FairPricingSettings": {
        "type": "object",
        "properties": {
          "settings": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "tiers": {
                "type": "object",
                "description": "Whole percent off per tier (0–80). `t1` = price level 0.58–0.72 of the US (e.g. Spain, Japan), `t2` = 0.45–0.58 (Poland, Brazil, Mexico), `t3` = 0.33–0.45 (Romania, Colombia, Kenya), `t4` = under 0.33 (India, Indonesia, Nigeria).",
                "properties": {
                  "t1": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 80,
                    "example": 20
                  },
                  "t2": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 80,
                    "example": 35
                  },
                  "t3": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 80,
                    "example": 50
                  },
                  "t4": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 80,
                    "example": 60
                  }
                }
              },
              "countryOverrides": {
                "type": "object",
                "description": "Country exceptions: ISO country code → whole percent off (0–80). They win over the country's tier; 0 means full price. Sending this replaces the whole list.",
                "additionalProperties": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 80
                },
                "example": {
                  "BR": 40,
                  "PT": 0
                }
              }
            }
          },
          "tiers": {
            "type": "array",
            "description": "The tier table: which countries each tier holds",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "enum": [
                    "t1",
                    "t2",
                    "t3",
                    "t4"
                  ]
                },
                "minLevel": {
                  "type": "number"
                },
                "maxLevel": {
                  "type": "number"
                },
                "defaultPercentOff": {
                  "type": "integer"
                },
                "countries": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "code": {
                        "type": "string",
                        "example": "IN"
                      },
                      "name": {
                        "type": "string",
                        "example": "India"
                      }
                    }
                  }
                }
              }
            }
          },
          "maxPercentOff": {
            "type": "integer",
            "example": 80
          }
        }
      },
      "PriceSteps": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "example": "step_3f9a1c2b7d4e"
                },
                "price": {
                  "type": "number",
                  "example": 49
                },
                "maxSales": {
                  "type": "integer",
                  "nullable": true,
                  "example": 50
                },
                "endsAt": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "sold": {
                  "type": "integer",
                  "description": "Paid sales at this price"
                },
                "taken": {
                  "type": "integer",
                  "description": "Seats sold plus seats held by buyers paying right now (a held seat goes back after 20 minutes unpaid)"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "open",
                    "upcoming",
                    "sold_out",
                    "ended"
                  ]
                }
              }
            }
          },
          "current": {
            "type": "object",
            "nullable": true,
            "description": "The step in effect now, or null (list price)",
            "properties": {
              "index": {
                "type": "integer"
              },
              "price": {
                "type": "number"
              },
              "left": {
                "type": "integer",
                "nullable": true
              },
              "maxSales": {
                "type": "integer",
                "nullable": true
              },
              "endsAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "nextPrice": {
                "type": "number",
                "description": "The price after this step"
              }
            }
          }
        }
      },
      "PricingRecord": {
        "type": "object",
        "nullable": true,
        "description": "How the order's price was reached, in your currency (main product, before tax). `source` is the ONE discount applied (`flash_sale`, `fair_pricing`, `code`) or null.",
        "properties": {
          "listPrice": {
            "type": "number",
            "example": 79
          },
          "basePrice": {
            "type": "number",
            "description": "The price in effect before discounts: the lowest of the list price, the early-bird step and the buyer's personal-deadline offer",
            "example": 49
          },
          "price": {
            "type": "number",
            "example": 29.4
          },
          "amountOff": {
            "type": "number",
            "description": "listPrice − price"
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "source": {
            "type": "string",
            "nullable": true,
            "enum": [
              "flash_sale",
              "fair_pricing",
              "code",
              null
            ]
          },
          "earlyBird": {
            "type": "object",
            "nullable": true,
            "properties": {
              "stepId": {
                "type": "string"
              },
              "index": {
                "type": "integer"
              },
              "price": {
                "type": "number"
              },
              "nextPrice": {
                "type": "number"
              }
            }
          },
          "personalOffer": {
            "type": "object",
            "nullable": true,
            "description": "The buyer's personal-deadline offer, when it set the base price",
            "properties": {
              "id": {
                "type": "string",
                "description": "Personal deadline id"
              },
              "price": {
                "type": "number"
              }
            }
          },
          "flashSale": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "valueType": {
                "type": "string"
              },
              "value": {
                "type": "number"
              }
            }
          },
          "fairPricing": {
            "type": "object",
            "nullable": true,
            "properties": {
              "country": {
                "type": "string",
                "example": "IN"
              },
              "tier": {
                "type": "string",
                "example": "t4"
              },
              "percentOff": {
                "type": "number",
                "example": 40
              },
              "billingCountry": {
                "type": "string",
                "description": "Orders only: the billing country collected at payment"
              },
              "verified": {
                "type": "boolean",
                "description": "Orders only: false when the billing country differs from the country the discount was for"
              }
            }
          },
          "code": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string"
              },
              "code": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "discount_code",
                  "unique_coupon"
                ]
              }
            }
          },
          "codeIgnored": {
            "type": "boolean",
            "description": "The buyer's code was not used because a better price applied"
          }
        }
      },
      "PriceQuote": {
        "type": "object",
        "properties": {
          "productId": {
            "type": "string"
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "payWant": {
            "type": "boolean",
            "description": "Pay what you want: the buyer picks the price, nothing below applies"
          },
          "listPrice": {
            "type": "number",
            "nullable": true,
            "example": 79
          },
          "basePrice": {
            "type": "number",
            "nullable": true,
            "example": 49
          },
          "price": {
            "type": "number",
            "nullable": true,
            "example": 44.1
          },
          "applied": {
            "type": "object",
            "nullable": true,
            "properties": {
              "source": {
                "type": "string",
                "enum": [
                  "flash_sale",
                  "fair_pricing",
                  "code"
                ]
              },
              "label": {
                "type": "string"
              },
              "valueType": {
                "type": "string",
                "enum": [
                  "percentage",
                  "fixed"
                ]
              },
              "value": {
                "type": "number"
              },
              "amountOff": {
                "type": "number"
              }
            }
          },
          "codeIgnored": {
            "type": "boolean"
          },
          "earlyBird": {
            "type": "object",
            "nullable": true,
            "properties": {
              "index": {
                "type": "integer"
              },
              "count": {
                "type": "integer"
              },
              "price": {
                "type": "number"
              },
              "nextPrice": {
                "type": "number"
              },
              "left": {
                "type": "integer",
                "nullable": true
              },
              "maxSales": {
                "type": "integer",
                "nullable": true
              },
              "endsAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "flashSale": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "endsAt": {
                "type": "string",
                "format": "date-time"
              },
              "valueType": {
                "type": "string"
              },
              "value": {
                "type": "number"
              },
              "applied": {
                "type": "boolean"
              }
            }
          },
          "fairPricing": {
            "type": "object",
            "nullable": true,
            "properties": {
              "country": {
                "type": "string"
              },
              "countryName": {
                "type": "string"
              },
              "percentOff": {
                "type": "number"
              },
              "applied": {
                "type": "boolean"
              },
              "reason": {
                "type": "string",
                "enum": [
                  "ok",
                  "billing_mismatch"
                ],
                "description": "`billing_mismatch`: the billing country sent is not the buyer's country, so the discount does not apply"
              }
            }
          },
          "cart": {
            "type": "object",
            "nullable": true,
            "description": "With `orderBumpIds`: the cart as the checkout charges it — the product at `price`, each bump at its offer price, then the \"Buy more, save more\" tier off the subtotal. Seller currency, before tax. Null without bumps.",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "productId": {
                      "type": "string"
                    },
                    "orderBumpId": {
                      "type": "string"
                    },
                    "price": {
                      "type": "number",
                      "description": "The item's share after the tier"
                    }
                  }
                }
              },
              "subtotal": {
                "type": "number",
                "example": 73.1
              },
              "tieredDiscount": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "percentOff": {
                    "type": "integer",
                    "example": 10
                  },
                  "itemCount": {
                    "type": "integer",
                    "example": 2
                  },
                  "amount": {
                    "type": "number",
                    "example": 7.31
                  },
                  "settlementAmount": {
                    "type": "number",
                    "example": 7.31
                  }
                }
              },
              "total": {
                "type": "number",
                "example": 65.79
              }
            }
          }
        },
        "example": {
          "productId": "665f1c2a9b3e4a0012ab34cd",
          "currency": "USD",
          "payWant": false,
          "listPrice": 79,
          "basePrice": 49,
          "price": 44.1,
          "applied": {
            "source": "flash_sale",
            "label": "Launch week",
            "valueType": "percentage",
            "value": 10,
            "amountOff": 4.9
          },
          "codeIgnored": false,
          "earlyBird": {
            "index": 0,
            "count": 2,
            "price": 49,
            "nextPrice": 59,
            "left": 23,
            "maxSales": 50,
            "endsAt": null
          },
          "flashSale": {
            "id": "6711d4e59e3f1a0012cd8b01",
            "name": "Launch week",
            "endsAt": "2026-10-20T09:00:00.000Z",
            "valueType": "percentage",
            "value": 10,
            "applied": true
          },
          "fairPricing": null,
          "cart": null
        }
      },
      "BundleItem": {
        "type": "object",
        "description": "One product included in a bundle (no deliverable).",
        "properties": {
          "_id": {
            "type": "string",
            "example": "665f1c2a9b3e4a0012ab34ce"
          },
          "name": {
            "type": "string",
            "example": "Notion Content OS"
          },
          "thumbnail": {
            "type": "string",
            "example": "https://cdn.example.com/os.png"
          },
          "price": {
            "type": "number",
            "description": "The product's own list price, in your account currency",
            "example": 49
          },
          "productType": {
            "type": "string",
            "enum": [
              "file",
              "url",
              "course",
              "credits"
            ],
            "example": "file"
          }
        }
      },
      "Bundle": {
        "type": "object",
        "description": "A bundle product and what it includes. Money is in your account currency, major units.",
        "properties": {
          "productId": {
            "type": "string",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "name": {
            "type": "string",
            "example": "Creator Starter Kit"
          },
          "price": {
            "type": "number",
            "description": "The bundle's price (the product's own price)",
            "example": 76
          },
          "published": {
            "type": "boolean",
            "example": true
          },
          "productIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The included products in the order you set. Empty when the product is not a bundle.",
            "example": [
              "665f1c2a9b3e4a0012ab34ce",
              "665f1c2a9b3e4a0012ab34cf",
              "665f1c2a9b3e4a0012ab34d0"
            ]
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BundleItem"
            },
            "description": "The included products that still exist (a deleted product drops out)"
          },
          "value": {
            "type": "number",
            "description": "Sum of the included products' list prices",
            "example": 114
          },
          "savings": {
            "type": "number",
            "description": "value − price, never below 0 (“Save $38”)",
            "example": 38
          },
          "savingsPercent": {
            "type": "integer",
            "example": 33
          }
        }
      },
      "TieredDiscounts": {
        "type": "object",
        "description": "The store's “Buy more, save more” tiers. Tiers are sorted by `minItems`; a tier with more items always gives a bigger discount.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "example": true
          },
          "tiers": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "type": "object",
              "properties": {
                "minItems": {
                  "type": "integer",
                  "minimum": 2,
                  "maximum": 20,
                  "description": "Items in the checkout to reach this tier",
                  "example": 2
                },
                "percentOff": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 90,
                  "description": "Percent off the cart subtotal",
                  "example": 10
                }
              }
            }
          },
          "combineWithCodes": {
            "type": "boolean",
            "description": "Also apply the tier when the buyer used a discount code (off by default: the code wins)",
            "example": false
          }
        }
      },
      "SignupForm": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6711b0c39e3f1a0012cd9911"
          },
          "name": {
            "type": "string",
            "maxLength": 80,
            "description": "Internal name, shown only in your dashboard",
            "example": "Blog footer"
          },
          "type": {
            "type": "string",
            "enum": [
              "inline",
              "slide_in",
              "popup"
            ],
            "description": "`inline` renders where the script tag is; `slide_in` slides into a bottom corner; `popup` opens a centred dialog (on exit intent on desktop, after the delay on touch screens).",
            "example": "inline"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused"
            ],
            "description": "A paused form renders nothing and refuses signups."
          },
          "archived": {
            "type": "boolean",
            "description": "Hide from the list (implies it stops showing)"
          },
          "headline": {
            "type": "string",
            "maxLength": 120,
            "description": "Heading of the form",
            "example": "Get the newsletter"
          },
          "body": {
            "type": "string",
            "maxLength": 500,
            "description": "Text under the heading",
            "example": "One useful email a week."
          },
          "buttonLabel": {
            "type": "string",
            "maxLength": 40,
            "description": "Submit button text",
            "example": "Subscribe"
          },
          "placeholder": {
            "type": "string",
            "maxLength": 60,
            "description": "Email field placeholder",
            "example": "you@example.com"
          },
          "collectName": {
            "type": "boolean",
            "description": "Also ask for a first name"
          },
          "successHeadline": {
            "type": "string",
            "maxLength": 80,
            "description": "Heading after a signup",
            "example": "You're in"
          },
          "successMessage": {
            "type": "string",
            "maxLength": 300,
            "description": "Text after a signup"
          },
          "leadMagnetProductId": {
            "type": "string",
            "nullable": true,
            "description": "One of your products to hand over after the signup: the success state links to its page (after confirmation, with double opt-in). `null` removes it."
          },
          "tags": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "maxLength": 40
            },
            "description": "Tags every signup gets in your audience (send to them by tag)",
            "example": [
              "blog",
              "lead-magnet"
            ]
          },
          "doubleOptIn": {
            "type": "boolean",
            "description": "Email a confirmation link first; the person joins your audience only after clicking it (links expire after 7 days)."
          },
          "accentColor": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Button colour on your site. The button text turns dark on light colours.",
            "example": "#2563eb"
          },
          "position": {
            "type": "string",
            "enum": [
              "bottom_right",
              "bottom_left"
            ],
            "description": "Slide-in corner"
          },
          "trigger": {
            "type": "object",
            "description": "When a slide-in or popup shows. A popup with `exitIntent` waits for the pointer to leave the page (desktop) and uses `delaySeconds` on touch screens; otherwise `scrollPercent` (when above 0) or `delaySeconds` decides.",
            "properties": {
              "delaySeconds": {
                "type": "integer",
                "minimum": 0,
                "maximum": 120,
                "example": 5
              },
              "scrollPercent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "example": 0
              },
              "exitIntent": {
                "type": "boolean",
                "example": true
              }
            }
          },
          "frequencyDays": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365,
            "description": "After a visitor closes a slide-in or popup, hide it from them for this many days (0 = show on every page view). People who signed up never see it again.",
            "example": 7
          },
          "stats": {
            "type": "object",
            "properties": {
              "views": {
                "type": "integer",
                "description": "Times the form was shown",
                "example": 1840
              },
              "signups": {
                "type": "integer",
                "description": "Accepted signups",
                "example": 92
              },
              "conversionRate": {
                "type": "number",
                "description": "Signups per view, in percent",
                "example": 5
              }
            }
          },
          "embed": {
            "type": "object",
            "description": "Copy-paste embed code",
            "properties": {
              "script": {
                "type": "string",
                "example": "<script src=\"https://app.pocketsflow.com/forms.js\" data-pocketsflow-form=\"6711b0c39e3f1a0012cd9911\" async></script>"
              },
              "iframe": {
                "type": "string",
                "example": "<iframe src=\"https://api.pocketsflow.com/forms/public/6711b0c39e3f1a0012cd9911/frame\" title=\"Newsletter signup\" loading=\"lazy\" style=\"width:100%;max-width:560px;height:320px;border:0;\"></iframe>"
              }
            }
          },
          "lastSignupAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PostResend": {
        "type": "object",
        "properties": {
          "recipients": {
            "type": "integer",
            "description": "Unique people who received the post (subject-test arms included)",
            "example": 4210
          },
          "opened": {
            "type": "integer",
            "description": "Unique people who opened it",
            "example": 1893
          },
          "openRate": {
            "type": "number",
            "description": "Percent",
            "example": 45
          },
          "nonOpeners": {
            "type": "integer",
            "description": "Received it and have not opened it — who a resend would go to now",
            "example": 2317
          },
          "eligible": {
            "type": "boolean",
            "description": "Can a resend be scheduled now"
          },
          "reason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "NOT_SENT",
              "IS_RESEND",
              "IS_TEST_ARM",
              "ALREADY_SCHEDULED",
              "ALREADY_RESENT",
              "NO_NON_OPENERS",
              null
            ],
            "description": "Why `eligible` is false"
          },
          "resend": {
            "type": "object",
            "nullable": true,
            "description": "The resend, once scheduled. It is its own post (`postId`).",
            "properties": {
              "postId": {
                "type": "string"
              },
              "subject": {
                "type": "string",
                "example": "In case you missed it: the spring drop"
              },
              "delayHours": {
                "type": "integer",
                "example": 48
              },
              "scheduledAt": {
                "type": "string",
                "format": "date-time"
              },
              "status": {
                "type": "string",
                "enum": [
                  "draft",
                  "scheduled",
                  "sending",
                  "sent",
                  "failed",
                  "deleted"
                ]
              },
              "sentAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "failureReason": {
                "type": "string",
                "nullable": true
              },
              "sent": {
                "type": "integer",
                "example": 2290
              },
              "opened": {
                "type": "integer",
                "example": 412
              },
              "openRate": {
                "type": "number",
                "example": 18
              }
            }
          }
        }
      },
      "SubjectTest": {
        "type": "object",
        "properties": {
          "subjectTest": {
            "type": "object",
            "nullable": true,
            "properties": {
              "subjectA": {
                "type": "string"
              },
              "subjectB": {
                "type": "string"
              },
              "testPercent": {
                "type": "integer",
                "example": 20
              },
              "waitHours": {
                "type": "integer",
                "example": 4
              },
              "status": {
                "type": "string",
                "enum": [
                  "testing",
                  "decided"
                ]
              },
              "variantAPostId": {
                "type": "string"
              },
              "variantBPostId": {
                "type": "string"
              },
              "testRecipients": {
                "type": "integer",
                "example": 842
              },
              "audienceSize": {
                "type": "integer",
                "example": 4210
              },
              "startedAt": {
                "type": "string",
                "format": "date-time"
              },
              "decideAt": {
                "type": "string",
                "format": "date-time"
              },
              "decidedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "winner": {
                "type": "string",
                "enum": [
                  "A",
                  "B"
                ],
                "nullable": true
              },
              "results": {
                "type": "object",
                "nullable": true,
                "description": "Unique opens per arm when the winner was picked"
              }
            }
          },
          "arms": {
            "type": "object",
            "nullable": true,
            "description": "Live results of each arm",
            "properties": {
              "A": {
                "$ref": "#/components/schemas/SubjectTestArm"
              },
              "B": {
                "$ref": "#/components/schemas/SubjectTestArm"
              }
            }
          },
          "rest": {
            "type": "object",
            "nullable": true,
            "description": "The winner's send to everyone not in the test (the post itself)",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "draft",
                  "scheduled",
                  "sending",
                  "sent",
                  "failed",
                  "deleted"
                ]
              },
              "recipients": {
                "type": "integer",
                "example": 3368
              },
              "sent": {
                "type": "integer",
                "example": 3351
              },
              "opened": {
                "type": "integer",
                "example": 1530
              },
              "openRate": {
                "type": "number",
                "example": 45.7
              }
            }
          }
        }
      },
      "SubjectTestArm": {
        "type": "object",
        "properties": {
          "postId": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "sending",
              "sent",
              "failed",
              "deleted"
            ]
          },
          "recipients": {
            "type": "integer",
            "example": 421
          },
          "sent": {
            "type": "integer",
            "example": 419
          },
          "opened": {
            "type": "integer",
            "example": 171
          },
          "openRate": {
            "type": "number",
            "example": 40.8
          }
        }
      },
      "UniqueCode": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "example": "VIP-7KQ2MX9D"
          },
          "email": {
            "type": "string",
            "nullable": true,
            "description": "The recipient the code is bound to; null for a bearer batch code"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "redeemed",
              "expired"
            ]
          },
          "source": {
            "type": "string",
            "enum": [
              "email",
              "batch",
              "api"
            ]
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "redeemedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "UniqueCodeStats": {
        "type": "object",
        "properties": {
          "issued": {
            "type": "integer"
          },
          "redeemed": {
            "type": "integer"
          },
          "expired": {
            "type": "integer"
          },
          "active": {
            "type": "integer"
          }
        }
      },
      "PersonalDeadline": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "example": "72-hour launch offer"
          },
          "productId": {
            "type": "string",
            "description": "Your fixed-price one-time product the offer is on"
          },
          "durationHours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 720,
            "default": 72,
            "description": "Length of each person's window in hours. Changing it only affects people who start after the change."
          },
          "offer": {
            "type": "object",
            "required": [
              "type",
              "value"
            ],
            "description": "The personal offer during the window. `percentage` / `fixed` off the product price, or a set `price` (your account currency; not applied to variants)",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "percentage",
                  "fixed",
                  "price"
                ]
              },
              "value": {
                "type": "number",
                "minimum": 0,
                "example": 30
              }
            }
          },
          "fallback": {
            "type": "object",
            "description": "What the person pays after their window ends (or once they used the offer): full price, or a smaller lasting discount",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "full_price",
                  "percentage",
                  "fixed"
                ],
                "default": "full_price"
              },
              "value": {
                "type": "number",
                "minimum": 0,
                "example": 10
              }
            }
          },
          "trigger": {
            "type": "object",
            "required": [
              "type"
            ],
            "description": "What starts a person's clock. `audience_joined`: they join your email audience. `lead_magnet`: they claim the free product `productId`. `automation_entered`: they enter the automation `automationId`. People already on your list start on the first email that carries a deadline merge tag; you can also start a clock with `POST /personal-deadlines/{id}/start`.",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "audience_joined",
                  "lead_magnet",
                  "automation_entered"
                ]
              },
              "productId": {
                "type": "string",
                "description": "lead_magnet only"
              },
              "automationId": {
                "type": "string",
                "description": "automation_entered only"
              }
            }
          },
          "active": {
            "type": "boolean",
            "default": true
          },
          "testMode": {
            "type": "boolean"
          },
          "mergeTags": {
            "type": "object",
            "description": "Paste these into an email (post or automation step)",
            "properties": {
              "countdown": {
                "type": "string",
                "example": "{{deadline:6710b2c49e3f1a0012cd56ef}}",
                "description": "Text such as \"ends in 47 hours\""
              },
              "link": {
                "type": "string",
                "example": "{{deadline_url:6710b2c49e3f1a0012cd56ef}}",
                "description": "The person's own product link (identifies them on your store)"
              },
              "block": {
                "type": "string",
                "example": "{{deadline_countdown:6710b2c49e3f1a0012cd56ef}}",
                "description": "A static countdown block (HTML)"
              }
            }
          },
          "stats": {
            "type": "object",
            "properties": {
              "started": {
                "type": "integer"
              },
              "active": {
                "type": "integer"
              },
              "redeemed": {
                "type": "integer"
              },
              "expired": {
                "type": "integer"
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PersonalDeadlineEntry": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "active",
              "redeemed",
              "expired"
            ]
          },
          "source": {
            "type": "string",
            "enum": [
              "audience_joined",
              "lead_magnet",
              "automation_entered",
              "email",
              "api"
            ]
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "redeemedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "PersonalDeadlineLink": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "example": "https://yourstore.pocketsflow.com/course?pf_sid=eyJl…",
            "description": "Product page link that identifies this person (`pf_sid`)"
          },
          "sid": {
            "type": "string",
            "nullable": true,
            "description": "The signed visitor token alone (null when personal links are not configured)"
          },
          "personalized": {
            "type": "boolean",
            "description": "False when the server has no signing key configured: `url` is then the plain product link and personal offers don't apply"
          },
          "entry": {
            "type": "object",
            "nullable": true,
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "active",
                  "redeemed",
                  "expired"
                ]
              },
              "startedAt": {
                "type": "string",
                "format": "date-time"
              },
              "expiresAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "CreatorServices": {
        "type": "object",
        "properties": {
          "prioritySupport": {
            "type": "object",
            "properties": {
              "allowed": {
                "type": "boolean"
              },
              "responseHours": {
                "type": "integer",
                "description": "Business hours to a first reply on your tickets",
                "example": 4
              },
              "priorityResponseHours": {
                "type": "integer",
                "example": 4
              },
              "standardResponseHours": {
                "type": "integer",
                "example": 24
              }
            }
          },
          "accountManager": {
            "type": "object",
            "properties": {
              "allowed": {
                "type": "boolean"
              },
              "manager": {
                "type": "object",
                "nullable": true,
                "description": "Your account manager, or null while one is being assigned",
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "Maya Chen"
                  },
                  "title": {
                    "type": "string",
                    "nullable": true,
                    "example": "Account manager"
                  },
                  "email": {
                    "type": "string",
                    "nullable": true,
                    "example": "maya@pocketsflow.com"
                  },
                  "photoUrl": {
                    "type": "string",
                    "nullable": true,
                    "example": "https://cdn.pocketsflow.com/team/maya.jpg"
                  },
                  "bookingUrl": {
                    "type": "string",
                    "nullable": true,
                    "example": "https://cal.com/maya-pocketsflow/30min"
                  },
                  "assignedAt": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  }
                }
              },
              "slackChannel": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "none",
                      "requested",
                      "ready"
                    ],
                    "description": "`requested` while our team sets it up; `ready` with the link"
                  },
                  "url": {
                    "type": "string",
                    "nullable": true,
                    "example": "https://pocketsflow.slack.com/archives/C07ABCDEF"
                  },
                  "requestedAt": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  }
                },
                "nullable": true
              }
            }
          },
          "disputeCover": {
            "type": "object",
            "properties": {
              "allowed": {
                "type": "boolean"
              },
              "feeCents": {
                "type": "integer",
                "description": "The dispute fee covered per dispute, in USD cents",
                "example": 1500
              },
              "coveredSince": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Sales made from this date on are covered"
              },
              "payouts": {
                "type": "string",
                "enum": [
                  "off",
                  "on"
                ],
                "description": "`off`: covers are recorded and settled by our team; `on`: the fee is also sent to your payouts account automatically"
              },
              "usage": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "month": {
                    "type": "string",
                    "example": "2026-10"
                  },
                  "used": {
                    "type": "integer",
                    "example": 1
                  },
                  "limit": {
                    "type": "integer",
                    "example": 10
                  },
                  "remaining": {
                    "type": "integer",
                    "example": 9
                  }
                }
              }
            }
          }
        }
      },
      "DisputeHelpRequest": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6711c0e19e3f1a0012cd9911"
          },
          "kind": {
            "type": "string",
            "enum": [
              "dispute_help"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "in_progress",
              "done",
              "cancelled"
            ]
          },
          "priority": {
            "type": "boolean"
          },
          "saleId": {
            "type": "string",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "message": {
            "type": "string"
          },
          "dueAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "ticketId": {
            "type": "string",
            "nullable": true,
            "description": "The support thread where our team replies"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "resolvedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "DisputeCover": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6711c2a79e3f1a0012cd9a01"
          },
          "saleId": {
            "type": "string",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "status": {
            "type": "string",
            "enum": [
              "covered",
              "excluded"
            ],
            "description": "`excluded` explains itself in `excludedReason`"
          },
          "excludedReason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "fraud_label",
              "account_closed",
              "monthly_limit",
              "before_entitlement",
              null
            ],
            "description": "`before_entitlement`: the sale was made before you were on the plan; `monthly_limit`: this month's covered disputes are used up"
          },
          "feeCents": {
            "type": "integer",
            "example": 1500
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "month": {
            "type": "string",
            "example": "2026-10"
          },
          "disputedAt": {
            "type": "string",
            "format": "date-time"
          },
          "credit": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "ledger",
                  "transfer"
                ],
                "description": "`ledger`: settled by our team; `transfer`: sent to your payouts account"
              },
              "status": {
                "type": "string",
                "enum": [
                  "recorded",
                  "credited",
                  "failed"
                ]
              },
              "creditedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "response": {
            "type": "object",
            "description": "The dispute response our team writes",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "todo",
                  "in_progress",
                  "submitted",
                  "won",
                  "lost"
                ]
              },
              "submittedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SheetsSync": {
        "type": "object",
        "properties": {
          "configured": {
            "type": "boolean",
            "description": "false when this server has no Google connection set up yet"
          },
          "allowed": {
            "type": "boolean",
            "description": "Your plan includes Google Sheets sync"
          },
          "maxBackfillDays": {
            "type": "integer",
            "example": 365
          },
          "connection": {
            "type": "object",
            "nullable": true,
            "description": "null until you connect a Google account",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "active",
                  "needs_reconnect"
                ],
                "description": "`needs_reconnect`: Google access was removed; connect again"
              },
              "spreadsheet": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "id": {
                    "type": "string",
                    "example": "1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789abcdEF"
                  },
                  "url": {
                    "type": "string",
                    "example": "https://docs.google.com/spreadsheets/d/1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789abcdEF/edit"
                  },
                  "title": {
                    "type": "string",
                    "example": "Pocketsflow sales"
                  }
                }
              },
              "syncSales": {
                "type": "boolean"
              },
              "syncCustomers": {
                "type": "boolean"
              },
              "connectedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "lastSyncedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "lastError": {
                "type": "string",
                "nullable": true
              },
              "lastErrorAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "counts": {
                "type": "object",
                "properties": {
                  "pending": {
                    "type": "integer",
                    "example": 3
                  },
                  "synced": {
                    "type": "integer",
                    "example": 412
                  },
                  "failed": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      },
      "AbTestVariant": {
        "type": "object",
        "description": "One variant. `A` is always the control: the product as it was when the test started. Only the field of the test's `type` is set.",
        "properties": {
          "_id": {
            "type": "string"
          },
          "key": {
            "type": "string",
            "enum": [
              "A",
              "B",
              "C",
              "D"
            ]
          },
          "weight": {
            "type": "integer",
            "description": "Share of new visitors, percent (all variants sum to 100)"
          },
          "control": {
            "type": "boolean"
          },
          "label": {
            "type": "string",
            "maxLength": 60,
            "description": "Your name for the variant (defaults to `Variant B`, …)",
            "example": "Lower price"
          },
          "price": {
            "type": "number",
            "minimum": 1,
            "maximum": 1000000,
            "description": "`price` tests: the list price in your account currency (major units, e.g. `39`). Discounts and personal offers apply on top of it at checkout, exactly as on the product's own price.",
            "example": 39
          },
          "headline": {
            "type": "string",
            "maxLength": 200,
            "description": "`headline` tests: the product name shown as the headline",
            "example": "Ship your first app this weekend"
          },
          "description": {
            "type": "string",
            "description": "`headline` tests (optional): the product description (HTML, like the product's own). Left out = the product's description."
          },
          "coverImage": {
            "type": "string",
            "format": "uri",
            "description": "`cover` tests: an https image URL (upload one with `POST /images` first)"
          },
          "checkoutLayout": {
            "type": "string",
            "enum": [
              "split",
              "flipped",
              "focus",
              "banner",
              "card",
              "hero",
              "sidebar",
              "gallery",
              "sheet",
              "frame"
            ],
            "description": "`checkout_layout` tests: the checkout layout"
          }
        }
      },
      "AbTest": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6712c4d59e3f1a0012cd9911"
          },
          "name": {
            "type": "string",
            "example": "Price test · Notion OS"
          },
          "productId": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "price",
              "headline",
              "cover",
              "checkout_layout"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "completed",
              "stopped"
            ],
            "description": "`completed` = a winner was declared; `stopped` = ended without one"
          },
          "testMode": {
            "type": "boolean"
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AbTestVariant"
            }
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "endedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "endReason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "winner",
              "stopped",
              "product_edited",
              "product_deleted"
            ],
            "description": "`product_edited`: you changed what the test was testing (e.g. the product price during a price test), so it stopped on its own"
          },
          "endDetail": {
            "type": "string",
            "nullable": true,
            "description": "`product_edited`: the product field that changed",
            "example": "price"
          },
          "winnerVariantId": {
            "type": "string",
            "nullable": true
          },
          "summary": {
            "type": "object",
            "description": "List only: totals so far",
            "properties": {
              "visitors": {
                "type": "integer"
              },
              "conversions": {
                "type": "integer"
              }
            }
          },
          "results": {
            "$ref": "#/components/schemas/AbTestResults"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AbTestVariantResult": {
        "type": "object",
        "properties": {
          "variantId": {
            "type": "string"
          },
          "key": {
            "type": "string",
            "enum": [
              "A",
              "B",
              "C",
              "D"
            ]
          },
          "label": {
            "type": "string"
          },
          "control": {
            "type": "boolean"
          },
          "weight": {
            "type": "integer"
          },
          "visitors": {
            "type": "integer",
            "description": "Unique visitors who saw this variant (bots excluded)"
          },
          "conversions": {
            "type": "integer",
            "description": "Paid sales from those visitors (refunds left out)"
          },
          "conversionRate": {
            "type": "number",
            "description": "Percent",
            "example": 3.42
          },
          "revenue": {
            "type": "number",
            "description": "Paid revenue in your account currency"
          },
          "revenuePerVisitor": {
            "type": "number"
          },
          "lift": {
            "type": "number",
            "nullable": true,
            "description": "Conversion rate change vs A, percent"
          },
          "zScore": {
            "type": "number",
            "nullable": true
          },
          "pValue": {
            "type": "number",
            "nullable": true,
            "description": "Two-proportion z-test vs A (two-sided). Under 0.05 = 95% confident the conversion rates differ."
          },
          "probabilityToBeat": {
            "type": "number",
            "nullable": true,
            "description": "0–1: Bayesian chance this variant's conversion rate is higher than A's"
          },
          "revenueProbabilityToBeat": {
            "type": "number",
            "nullable": true,
            "description": "Price tests: 0–1 chance this variant earns more per visitor than A"
          },
          "probabilityToBeBest": {
            "type": "number",
            "description": "0–1: chance this variant is the best of all"
          },
          "significant": {
            "type": "boolean"
          },
          "verdict": {
            "type": "string",
            "enum": [
              "control",
              "collecting",
              "winning",
              "losing",
              "no_difference"
            ],
            "description": "`collecting` until both arms have 100+ visitors and 10+ conversions between them"
          }
        }
      },
      "AbTestResults": {
        "type": "object",
        "properties": {
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "metric": {
            "type": "string",
            "enum": [
              "conversion_rate",
              "revenue_per_visitor"
            ],
            "description": "What the verdict follows. Price tests are judged on revenue per visitor."
          },
          "totals": {
            "type": "object",
            "properties": {
              "visitors": {
                "type": "integer"
              },
              "conversions": {
                "type": "integer"
              },
              "revenue": {
                "type": "number"
              }
            }
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AbTestVariantResult"
            }
          },
          "daily": {
            "type": "array",
            "description": "One row per day of the test (most recent 120): new visitors and conversions per variant id",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "example": "2026-10-14"
                },
                "variants": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "visitors": {
                        "type": "integer"
                      },
                      "conversions": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          },
          "recommendation": {
            "type": "object",
            "properties": {
              "variantId": {
                "type": "string",
                "nullable": true
              },
              "reason": {
                "type": "string",
                "enum": [
                  "winner",
                  "keep_control",
                  "too_early",
                  "no_difference"
                ]
              }
            }
          },
          "thresholds": {
            "type": "object",
            "properties": {
              "minVisitors": {
                "type": "integer",
                "example": 100
              },
              "minConversions": {
                "type": "integer",
                "example": 10
              }
            }
          }
        }
      },
      "MigrationFile": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "products",
              "customers",
              "orders",
              "subscribers"
            ],
            "description": "`products` (catalog), `customers`, `orders` (purchase history — saved as imported records, never as sales) or `subscribers` (email list).",
            "example": "orders"
          },
          "origin": {
            "type": "string",
            "enum": [
              "csv",
              "api"
            ]
          },
          "filename": {
            "type": "string",
            "example": "gumroad-sales.csv"
          },
          "headers": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "Purchase ID",
              "Item Name",
              "Purchase Email"
            ]
          },
          "mapping": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "nullable": true
            },
            "description": "Canonical field → the CSV column it reads (`null` = not mapped)",
            "example": {
              "sourceOrderId": "Purchase ID",
              "productName": "Item Name",
              "email": "Purchase Email"
            }
          },
          "rowCount": {
            "type": "integer",
            "example": 1240
          },
          "validCount": {
            "type": "integer",
            "example": 1236
          },
          "invalidCount": {
            "type": "integer",
            "example": 4
          },
          "warningCount": {
            "type": "integer",
            "example": 12
          },
          "truncated": {
            "type": "boolean",
            "description": "The file had more than 50,000 rows; only the first 50,000 were read"
          },
          "addedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MigrationJob": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "example": "6711c0d59e3f1a0012cd9911"
          },
          "source": {
            "type": "string",
            "enum": [
              "gumroad",
              "lemonsqueezy",
              "stan",
              "csv"
            ],
            "description": "Where you are moving from: `gumroad`, `lemonsqueezy`, `stan` (Stan Store) or `csv` (any other platform). It decides which export columns are recognised and whether `connect` is available (Gumroad, Lemon Squeezy).",
            "example": "gumroad"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "queued",
              "running",
              "completed",
              "failed",
              "canceled"
            ],
            "description": "`draft` until started; files and mapping can only change while `draft`"
          },
          "testMode": {
            "type": "boolean"
          },
          "createdBy": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "creator",
                  "admin"
                ],
                "description": "`admin` = run for you by the Pocketsflow team"
              }
            }
          },
          "options": {
            "type": "object",
            "properties": {
              "downloadFiles": {
                "type": "boolean"
              }
            }
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MigrationFile"
            }
          },
          "dryRun": {
            "$ref": "#/components/schemas/MigrationDryRun",
            "nullable": true
          },
          "progress": {
            "type": "object",
            "description": "Per kind, this run's progress",
            "properties": {
              "products": {
                "type": "object",
                "properties": {
                  "total": {
                    "type": "integer",
                    "example": 1240
                  },
                  "processed": {
                    "type": "integer",
                    "example": 600
                  },
                  "imported": {
                    "type": "integer",
                    "example": 580
                  },
                  "skipped": {
                    "type": "integer",
                    "example": 18
                  },
                  "failed": {
                    "type": "integer",
                    "example": 2
                  }
                }
              },
              "customers": {
                "type": "object",
                "properties": {
                  "total": {
                    "type": "integer",
                    "example": 1240
                  },
                  "processed": {
                    "type": "integer",
                    "example": 600
                  },
                  "imported": {
                    "type": "integer",
                    "example": 580
                  },
                  "skipped": {
                    "type": "integer",
                    "example": 18
                  },
                  "failed": {
                    "type": "integer",
                    "example": 2
                  }
                }
              },
              "orders": {
                "type": "object",
                "properties": {
                  "total": {
                    "type": "integer",
                    "example": 1240
                  },
                  "processed": {
                    "type": "integer",
                    "example": 600
                  },
                  "imported": {
                    "type": "integer",
                    "example": 580
                  },
                  "skipped": {
                    "type": "integer",
                    "example": 18
                  },
                  "failed": {
                    "type": "integer",
                    "example": 2
                  }
                }
              },
              "subscribers": {
                "type": "object",
                "properties": {
                  "total": {
                    "type": "integer",
                    "example": 1240
                  },
                  "processed": {
                    "type": "integer",
                    "example": 600
                  },
                  "imported": {
                    "type": "integer",
                    "example": 580
                  },
                  "skipped": {
                    "type": "integer",
                    "example": 18
                  },
                  "failed": {
                    "type": "integer",
                    "example": 2
                  }
                }
              }
            }
          },
          "phase": {
            "type": "string",
            "nullable": true,
            "description": "The kind the run is on"
          },
          "cancelRequested": {
            "type": "boolean"
          },
          "lastError": {
            "type": "string",
            "nullable": true
          },
          "queuedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "startedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MigrationDryRun": {
        "type": "object",
        "properties": {
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "canStart": {
            "type": "boolean"
          },
          "subscriberLimit": {
            "type": "integer",
            "nullable": true,
            "description": "Your plan's subscriber cap (`null` = unlimited)"
          },
          "kinds": {
            "type": "object",
            "description": "Counts per kind. products: total, invalid, skipped, create, existing, filesToDownload, fileNeeded. customers: total, invalid, skipped, create, existing. orders: total, invalid, skipped, create, alreadyImported, newCustomers, refunded. subscribers: total, invalid, noConsent, create, active, unsubscribed, existing, suppressed, overLimit, activeNow.",
            "additionalProperties": {
              "type": "object",
              "additionalProperties": {
                "type": "integer"
              }
            },
            "example": {
              "products": {
                "total": 12,
                "invalid": 0,
                "skipped": 0,
                "create": 11,
                "existing": 1,
                "filesToDownload": 8,
                "fileNeeded": 3
              },
              "orders": {
                "total": 1240,
                "invalid": 4,
                "skipped": 0,
                "create": 1236,
                "alreadyImported": 0,
                "newCustomers": 913,
                "refunded": 21
              }
            }
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "MigrationRow": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "description": "1-based data row of the file",
            "example": 7
          },
          "status": {
            "type": "string",
            "enum": [
              "invalid",
              "pending",
              "imported",
              "skipped",
              "failed"
            ]
          },
          "data": {
            "type": "object",
            "nullable": true,
            "description": "The canonical record (null while invalid)"
          },
          "raw": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The source cells by column"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "\"jane@\" is not a valid email address."
            ]
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "note": {
            "type": "string",
            "nullable": true,
            "example": "Already one of your customers."
          },
          "entityId": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Error body. Most endpoints set `message`; authentication errors set both `error` and `message`; some add a machine-readable `code`. Read `message` first and fall back to `error`.",
        "properties": {
          "message": {
            "type": "string",
            "description": "Human-readable explanation",
            "example": "Product not found"
          },
          "error": {
            "type": "string",
            "description": "Short error title (authentication errors) or, on a few endpoints, a machine-readable code such as `PLAN_LIMIT_EXCEEDED`"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable error code when present, e.g. `COUNTRY_RESTRICTED`, `ACCOUNT_BLOCKED`, `ACCOUNT_DELETED`, `SEND_ALLOWANCE_EXCEEDED`, `EMAIL_SENDING_PAUSED`, `DAILY_SEND_LIMIT`, `FEATURE_LOCKED` (see `FeatureLocked`)"
          }
        }
      },
      "FeatureLocked": {
        "type": "object",
        "description": "The `403` body when a write would turn on a feature the account's plan does not include. Nothing was saved and retrying will not help: tell the creator to upgrade at https://app.pocketsflow.com/profile/billing, then retry. `GET /users/entitlements` tells you up front which features are allowed.",
        "required": [
          "error",
          "code",
          "upgradeRequired",
          "message",
          "details"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "FEATURE_LOCKED"
            ]
          },
          "code": {
            "type": "string",
            "enum": [
              "FEATURE_LOCKED"
            ]
          },
          "upgradeRequired": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "message": {
            "type": "string",
            "example": "This feature is part of a higher plan. Please upgrade your plan to use it."
          },
          "details": {
            "type": "object",
            "required": [
              "feature",
              "currentPlan",
              "requiredPlan",
              "requiredPlanName"
            ],
            "properties": {
              "feature": {
                "type": "string",
                "enum": [
                  "removeBadge",
                  "customDomains",
                  "premiumTemplates",
                  "adPixels",
                  "trackedLinks",
                  "pdfStamping",
                  "limitedDrops",
                  "upsells",
                  "abandonedCheckout",
                  "affiliates",
                  "teams",
                  "paymentPlans",
                  "exitOffers",
                  "postPurchaseUpsells",
                  "socialProof",
                  "salesFunnels",
                  "paidNewsletter",
                  "fairPricing",
                  "revenueSplits",
                  "earlyBirdPricing",
                  "flashSales",
                  "bundles",
                  "tieredDiscounts",
                  "audienceSegments",
                  "winbackEmails",
                  "uniqueCoupons",
                  "embedForms",
                  "testimonials",
                  "resendNonOpeners",
                  "dripContent",
                  "downloadLimits",
                  "attributionSurvey",
                  "loyalty",
                  "advancedAnalytics",
                  "failedPaymentRecovery",
                  "cancellationSaveOffers",
                  "abTests",
                  "memberships",
                  "sheetsSync",
                  "usagePricing",
                  "mrrReports",
                  "introPricing",
                  "browseAbandonment",
                  "personalDeadlines",
                  "tieredAffiliates",
                  "customerCrm",
                  "benchmarks",
                  "prioritySupport",
                  "lowerFee",
                  "migration",
                  "accountManager",
                  "disputeCover"
                ],
                "description": "The locked feature key",
                "example": "upsells"
              },
              "currentPlan": {
                "type": "string",
                "enum": [
                  "free",
                  "tier1",
                  "tier2",
                  "tier3",
                  "tier4",
                  "volume",
                  "volume_max",
                  "enterprise"
                ],
                "description": "The account's active plan",
                "example": "free"
              },
              "requiredPlan": {
                "type": "string",
                "enum": [
                  "free",
                  "tier1",
                  "tier2",
                  "tier3",
                  "tier4",
                  "volume",
                  "volume_max",
                  "enterprise"
                ],
                "description": "The cheapest plan that includes the feature",
                "example": "tier2"
              },
              "requiredPlanName": {
                "type": "string",
                "example": "Growth"
              },
              "requiredPlanPrice": {
                "type": "number",
                "nullable": true,
                "description": "Monthly price of `requiredPlan` in USD",
                "example": 49
              },
              "upgradeRequired": {
                "type": "boolean",
                "enum": [
                  true
                ]
              }
            }
          }
        }
      },
      "FeatureEntitlement": {
        "type": "object",
        "description": "Whether one feature is available to the account right now",
        "properties": {
          "allowed": {
            "type": "boolean",
            "example": false
          },
          "requiredPlan": {
            "type": "string",
            "enum": [
              "free",
              "tier1",
              "tier2",
              "tier3",
              "tier4",
              "volume",
              "volume_max",
              "enterprise"
            ],
            "description": "The cheapest plan that includes the feature",
            "example": "tier2"
          },
          "requiredPlanName": {
            "type": "string",
            "example": "Growth"
          },
          "reason": {
            "type": "string",
            "enum": [
              "plan",
              "grandfathered",
              "not_enforced",
              "locked"
            ],
            "description": "`plan` = the account's plan includes it; `grandfathered` = kept from before plan limits applied, until `grandfatheredUntil`; `not_enforced` = open to everyone for now (plan limits not switched on yet for this existing feature); `locked` = needs an upgrade",
            "example": "locked"
          },
          "grandfatheredUntil": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When grandfathered access ends (`reason: grandfathered` only)"
          }
        }
      },
      "Entitlements": {
        "type": "object",
        "description": "What the account's plan unlocks. The plan is the ACTIVE plan: a canceled paid plan reads as `free` once its paid period ends.",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "tier1",
              "tier2",
              "tier3",
              "tier4",
              "volume",
              "volume_max",
              "enterprise"
            ],
            "description": "Active plan id: `free`, `tier1` (Starter), `tier2` (Growth), `tier3` (Pro), `tier4` (Scale), `volume`, `volume_max`, `enterprise`",
            "example": "tier1"
          },
          "planName": {
            "type": "string",
            "example": "Starter"
          },
          "billingInterval": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ],
            "example": "month"
          },
          "feeRate": {
            "type": "number",
            "description": "Platform fee charged on new sales, as a proportion (`0.02` = 2%; `0.01` on Volume, `0.005` on Volume Max)",
            "example": 0.02
          },
          "seats": {
            "type": "integer",
            "nullable": true,
            "description": "Team seats included; `null` = unlimited",
            "example": 1
          },
          "enforced": {
            "type": "boolean",
            "description": "Whether plan limits apply to features that existed before plans did. While `false`, those features are open to everyone (`reason: not_enforced`); features added since are always plan-gated."
          },
          "features": {
            "type": "object",
            "description": "One entry per feature key: `removeBadge`, `customDomains`, `premiumTemplates`, `adPixels`, `trackedLinks`, `pdfStamping`, `limitedDrops`, `upsells`, `abandonedCheckout`, `affiliates`, `teams`, `paymentPlans`, `exitOffers`, `postPurchaseUpsells`, `socialProof`, `salesFunnels`, `paidNewsletter`, `fairPricing`, `revenueSplits`, `earlyBirdPricing`, `flashSales`, `bundles`, `tieredDiscounts`, `audienceSegments`, `winbackEmails`, `uniqueCoupons`, `embedForms`, `testimonials`, `resendNonOpeners`, `dripContent`, `downloadLimits`, `attributionSurvey`, `loyalty`, `advancedAnalytics`, `failedPaymentRecovery`, `cancellationSaveOffers`, `abTests`, `memberships`, `sheetsSync`, `usagePricing`, `mrrReports`, `introPricing`, `browseAbandonment`, `personalDeadlines`, `tieredAffiliates`, `customerCrm`, `benchmarks`, `prioritySupport`, `lowerFee`, `migration`, `accountManager`, `disputeCover`. New keys are added as features ship — ignore keys you don't know.",
            "additionalProperties": {
              "$ref": "#/components/schemas/FeatureEntitlement"
            }
          }
        }
      },
      "Plan": {
        "type": "object",
        "description": "One plan of the public catalogue",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "tier1",
              "tier2",
              "tier3",
              "tier4",
              "volume",
              "volume_max"
            ],
            "example": "tier2"
          },
          "name": {
            "type": "string",
            "example": "Growth"
          },
          "pricePerMonth": {
            "type": "number",
            "description": "USD per month, billed monthly",
            "example": 49
          },
          "pricePerYear": {
            "type": "number",
            "description": "USD per year, billed yearly (two months free)",
            "example": 490
          },
          "feeRate": {
            "type": "number",
            "description": "Platform fee on every sale while the plan is active (`0.02` = 2%)",
            "example": 0.02
          },
          "seats": {
            "type": "integer",
            "nullable": true,
            "description": "Team seats included; `null` = unlimited",
            "example": 5
          },
          "minSubscribers": {
            "type": "integer",
            "example": 0
          },
          "maxSubscribers": {
            "type": "integer",
            "description": "Newsletter subscriber cap",
            "example": 10000
          },
          "maxSendsPerMonth": {
            "type": "integer",
            "description": "Newsletter emails per month",
            "example": 40000
          },
          "maxUploadMb": {
            "type": "integer",
            "description": "Largest single upload, in MB",
            "example": 100
          },
          "isPopular": {
            "type": "boolean"
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "removeBadge",
                "customDomains",
                "premiumTemplates",
                "adPixels",
                "trackedLinks",
                "pdfStamping",
                "limitedDrops",
                "upsells",
                "abandonedCheckout",
                "affiliates",
                "teams",
                "paymentPlans",
                "exitOffers",
                "postPurchaseUpsells",
                "socialProof",
                "salesFunnels",
                "paidNewsletter",
                "fairPricing",
                "revenueSplits",
                "earlyBirdPricing",
                "flashSales",
                "bundles",
                "tieredDiscounts",
                "audienceSegments",
                "winbackEmails",
                "uniqueCoupons",
                "embedForms",
                "testimonials",
                "resendNonOpeners",
                "dripContent",
                "downloadLimits",
                "attributionSurvey",
                "loyalty",
                "advancedAnalytics",
                "failedPaymentRecovery",
                "cancellationSaveOffers",
                "abTests",
                "memberships",
                "sheetsSync",
                "usagePricing",
                "mrrReports",
                "introPricing",
                "browseAbandonment",
                "personalDeadlines",
                "tieredAffiliates",
                "customerCrm",
                "benchmarks",
                "prioritySupport",
                "lowerFee",
                "migration",
                "accountManager",
                "disputeCover"
              ]
            },
            "description": "Feature keys this plan (or a lower one) includes"
          }
        }
      },
      "AbandonedCheckoutSummary": {
        "type": "object",
        "description": "Live checkouts lost over the last `windowDays` days: the buyer typed their name or email (or had a payment declined) more than an hour ago and never paid. Counted once per buyer and product, and not at all when that buyer bought the product.",
        "properties": {
          "count": {
            "type": "integer",
            "example": 7
          },
          "amountUsd": {
            "type": "number",
            "description": "Sum of the USD checkouts among them (other currencies are counted, not summed). Decimal, not cents.",
            "example": 243
          },
          "windowDays": {
            "type": "integer",
            "example": 30
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "totalCount": {
            "type": "integer",
            "example": 137
          },
          "totalPages": {
            "type": "integer",
            "example": 7
          },
          "currentPage": {
            "type": "integer",
            "example": 1
          },
          "pageSize": {
            "type": "integer",
            "example": 20
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether `currentPage + 1` has results",
            "example": true
          }
        }
      },
      "Product": {
        "type": "object",
        "description": "A one-time product in your catalog",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Product id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "userId": {
            "type": "string",
            "description": "Owner account id"
          },
          "name": {
            "type": "string",
            "example": "Starter Kit"
          },
          "subtitle": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "description": "Price in the seller's account currency (`currency` on `GET /users/me`). Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 29
          },
          "compareAtPrice": {
            "type": "number",
            "description": "Optional crossed-out merchandising price; display only. Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 49
          },
          "payWant": {
            "type": "boolean",
            "description": "Pay-what-you-want pricing enabled"
          },
          "minPrice": {
            "type": "number",
            "description": "Pay-what-you-want minimum. Decimal amount in major currency units (e.g. `29.99`), not cents."
          },
          "maxPrice": {
            "type": "number",
            "description": "Pay-what-you-want maximum. Decimal amount in major currency units (e.g. `29.99`), not cents."
          },
          "published": {
            "type": "boolean",
            "example": true
          },
          "hasProductPage": {
            "type": "string",
            "enum": [
              "yes",
              "no",
              "unknown"
            ],
            "description": "Whether the product has its own page (`unknown` on products that never set it)"
          },
          "slug": {
            "type": "string",
            "example": "starter-kit"
          },
          "thumbnail": {
            "type": "string",
            "description": "Cover image URL (list responses fall back to the first gallery image)"
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Gallery image ids"
          },
          "productType": {
            "type": "string",
            "enum": [
              "file",
              "url",
              "course",
              "credits"
            ],
            "description": "Catalog type. Absent on legacy products."
          },
          "availability": {
            "type": "string",
            "enum": [
              "available",
              "preorder",
              "waitlist"
            ],
            "description": "`available` (default), `preorder` (pay now, content unlocks on `releaseAt`) or `waitlist` (no checkout; collects emails)",
            "example": "available"
          },
          "releaseAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Pre-order release date. Only set while `availability` is `preorder`."
          },
          "releasedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the product was last released from pre-order. Read-only."
          },
          "file": {
            "type": "string",
            "description": "Primary file URL"
          },
          "url": {
            "type": "string",
            "description": "Delivered link (url products)"
          },
          "refundPolicy": {
            "type": "string",
            "description": "Refund policy id, or empty"
          },
          "showSales": {
            "type": "boolean"
          },
          "showReviews": {
            "type": "boolean"
          },
          "hasFirstName": {
            "type": "boolean"
          },
          "hasLastName": {
            "type": "boolean"
          },
          "allowGifts": {
            "type": "boolean"
          },
          "ctaText": {
            "type": "string"
          },
          "checkoutText": {
            "type": "string"
          },
          "thankYouText": {
            "type": "string"
          },
          "creditAutoRefill": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "topOffCents": {
                "type": "integer"
              }
            }
          },
          "licenseKeys": {
            "type": "object",
            "description": "License keys: with `enabled`, every sale of this product gets one key. `activationLimit` is the number of activations per key (null = unlimited).",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "activationLimit": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10000,
                "nullable": true
              }
            }
          },
          "abandonedCheckoutEnabled": {
            "type": "boolean",
            "description": "Abandoned checkout recovery: a buyer who leaves this product's checkout gets one recovery email (Growth plan). On by default. Read-only here — change it with `PATCH /products/{id}/abandoned-checkout`.",
            "example": true
          },
          "limitedDrop": {
            "type": "object",
            "description": "Limited drop (Starter). Read-only here — change it with `PATCH /products/{id}/limited-drop`.",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "quantity": {
                "type": "integer",
                "nullable": true
              },
              "sold": {
                "type": "integer"
              },
              "held": {
                "type": "integer"
              },
              "restockOnRefund": {
                "type": "boolean"
              }
            }
          },
          "pdfStamping": {
            "type": "object",
            "description": "PDF stamping (Starter). Read-only here — change it with `PATCH /products/{id}/pdf-stamping`.",
            "properties": {
              "enabled": {
                "type": "boolean"
              }
            }
          },
          "downloadLimits": {
            "type": "object",
            "description": "Download limits (Growth). Read-only here — change them with `PATCH /products/{id}/download-limits`.",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "maxDownloads": {
                "type": "integer",
                "nullable": true
              },
              "expiryDays": {
                "type": "integer",
                "nullable": true
              },
              "applyToExisting": {
                "type": "boolean"
              }
            }
          },
          "testMode": {
            "type": "boolean",
            "description": "Whether the product lives in test mode"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Payment": {
        "type": "object",
        "description": "A single payment (a `Sale`). Covers BOTH one-time product purchases (`isSubscription: false`) and subscription payments — the initial charge and every renewal (`isSubscription: true`). Money fields are decimal amounts in `currency`, not cents.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Payment (sale) id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "isSubscription": {
            "type": "boolean",
            "description": "true for subscription payments (initial + renewals); false for one-time product purchases",
            "example": false
          },
          "billingReason": {
            "type": "string",
            "enum": [
              "initial",
              "renewal",
              "usage"
            ],
            "description": "Only present on subscription payments: `initial` for the first charge that activates the membership, `renewal` for every recurring charge after it, `usage` for a usage-based overage charged at the end of a billing period",
            "example": "renewal"
          },
          "amount": {
            "type": "number",
            "description": "Charged amount (after tax), in the payment currency. Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 12.1
          },
          "grossAmount": {
            "type": "number",
            "description": "Gross amount collected from the buyer. Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 12.1
          },
          "amountBeforeTax": {
            "type": "number",
            "description": "Amount before tax was applied. Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 10
          },
          "taxAmount": {
            "type": "number",
            "description": "Tax collected on this payment. Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 2.1
          },
          "currency": {
            "type": "string",
            "example": "usd"
          },
          "buyerEmail": {
            "type": "string",
            "example": "buyer@example.com"
          },
          "firstName": {
            "type": "string",
            "example": "Jane"
          },
          "lastName": {
            "type": "string",
            "example": "Buyer"
          },
          "buyerDevice": {
            "type": "object",
            "description": "The buyer's device and browser at checkout, read from their browser's user agent. Absent on older orders and on checkouts not started from a browser. Empty strings mean unknown.",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "mobile",
                  "tablet",
                  "desktop",
                  "bot",
                  ""
                ],
                "example": "mobile"
              },
              "browser": {
                "type": "string",
                "example": "Safari"
              },
              "browserVersion": {
                "type": "string",
                "example": "18.1"
              },
              "os": {
                "type": "string",
                "example": "iOS"
              },
              "osVersion": {
                "type": "string",
                "example": "18.1"
              }
            }
          },
          "isRefunded": {
            "type": "boolean",
            "example": false
          },
          "isDisputed": {
            "type": "boolean",
            "example": false
          },
          "gift": {
            "type": "object",
            "description": "Present only on an order bought as a gift. The buyer paid; the product belongs to `recipientEmail`.",
            "properties": {
              "recipientEmail": {
                "type": "string",
                "example": "friend@example.com"
              },
              "recipientName": {
                "type": "string",
                "example": "Sam Lee"
              },
              "message": {
                "type": "string",
                "example": "Happy birthday!"
              },
              "status": {
                "type": "string",
                "enum": [
                  "paid",
                  "sending",
                  "delivered",
                  "failed",
                  "revoked"
                ],
                "example": "delivered"
              },
              "selfGift": {
                "type": "boolean",
                "example": false
              },
              "deliveredAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "revokedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "revokedReason": {
                "type": "string",
                "enum": [
                  "refunded",
                  "disputed"
                ],
                "nullable": true
              }
            }
          },
          "productId": {
            "type": "string",
            "description": "The product purchased. Empty on subscription payments — use `subscriptionId` instead.",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "subscriptionId": {
            "type": "string",
            "description": "The subscription offer this payment belongs to. Empty on one-time product purchases.",
            "example": "665f1c2a9b3e4a0012ab7788"
          },
          "partnerPaymentId": {
            "type": "string",
            "description": "The payments partner payment (receipt) id backing this sale",
            "example": "pay_XXXXXXXX"
          },
          "partnerPlanId": {
            "type": "string",
            "description": "The payments partner plan id this payment was charged against",
            "example": "plan_XXXXXXXX"
          },
          "membershipId": {
            "type": "string",
            "description": "The payments partner membership id this subscription payment belongs to (subscription payments only). Matches the subscriber's `membershipId`.",
            "example": "mem_XXXXXXXX"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PaymentDetail": {
        "type": "object",
        "description": "A single payment with its related product OR subscription offer, the customer, and the stored payments partner payload.",
        "properties": {
          "payment": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Payment"
              },
              {
                "type": "object",
                "properties": {
                  "net": {
                    "type": "number",
                    "description": "Net to the seller (amountBeforeTax − affiliateCut − legacy processor fee). Decimal amount in major currency units (e.g. `29.99`), not cents."
                  },
                  "fee": {
                    "type": "number",
                    "description": "Legacy processor fee (older card / PayPal sales). Decimal amount in major currency units (e.g. `29.99`), not cents."
                  },
                  "metadata": {
                    "type": "object",
                    "description": "The raw stored payment payload from the payments partner captured on the webhook"
                  }
                }
              }
            ]
          },
          "product": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Product"
              }
            ],
            "nullable": true,
            "description": "The purchased product (one-time purchases only; null for subscription payments)"
          },
          "subscription": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SubscriptionOffer"
              }
            ],
            "nullable": true,
            "description": "The subscription offer (subscription payments only; null for one-time purchases)"
          },
          "customer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Customer"
              }
            ],
            "nullable": true,
            "description": "The buyer's customer record"
          },
          "paymentsPartner": {
            "type": "object",
            "description": "The stored payments partner payload for this payment (sourced from `sale.metadata`)"
          },
          "paymentsPartnerLive": {
            "type": "object",
            "nullable": true,
            "description": "Only present when `?live=true`: the authoritative live payment fetched from the payments partner. `null` if the live lookup failed (the stored data still applies)."
          }
        }
      },
      "OrderFees": {
        "type": "object",
        "description": "Seller-facing fee breakdown attached to orders. All decimal amounts in the sale currency.",
        "properties": {
          "net": {
            "type": "number",
            "description": "amountBeforeTax − affiliateCut − legacy processor fee. Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 8.46
          },
          "fee": {
            "type": "number",
            "description": "Legacy processor fee (older card / PayPal sales only). Decimal amount in major currency units (e.g. `29.99`), not cents."
          },
          "platformFee": {
            "type": "number",
            "description": "Pocketsflow platform fee, as locked at checkout (in the order currency). Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 0.2
          },
          "platformFeeSource": {
            "type": "string",
            "enum": [
              "stored",
              "estimated",
              "none"
            ],
            "description": "`stored` = the fee locked at checkout, converted to the order currency; `estimated` = older sale without a stored fee (2% estimate); `none` = legacy card / PayPal sale with no platform line"
          },
          "platformFeeRate": {
            "type": "number",
            "nullable": true,
            "description": "The rate `platformFee` was charged at, as a proportion: `0.02` (2%), `0.01` on the Volume plan, `0.005` on Volume Max — the seller's plan at checkout, so a later plan change never rewrites it. Read back from the stored fee and settled amount; if no single plan rate reproduces the fee to the cent, the plain ratio (4 decimals). `0.02` when `platformFeeSource` is `estimated`, `null` when it is `none`.",
            "example": 0.01
          },
          "processingFee": {
            "type": "number",
            "description": "Payment processing fee (excludes affiliate commission — see `affiliateFee`). Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 0.59
          },
          "affiliateFee": {
            "type": "number",
            "description": "Affiliate commission deducted from this sale. Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 0
          },
          "totalTransactionCost": {
            "type": "number",
            "nullable": true,
            "description": "platformFee + processingFee, decimal. Null until the settled amount is known.",
            "example": 0.79
          },
          "totalDeductions": {
            "type": "number",
            "nullable": true,
            "description": "totalTransactionCost + affiliateFee, decimal. Null until the settled amount is known."
          },
          "effectiveFeeRate": {
            "type": "number",
            "nullable": true,
            "description": "totalTransactionCost as a percentage of the pre-tax amount (e.g. `7.9` = 7.9%). Null until the settled amount is known.",
            "example": 7.9
          },
          "netAmount": {
            "type": "number",
            "nullable": true,
            "description": "Stored settled net amount for the sale, or null when not recorded. Decimal, not cents."
          },
          "affiliateCut": {
            "type": "number",
            "description": "Affiliate commission paid out of this sale. Decimal amount in major currency units (e.g. `29.99`), not cents."
          },
          "affiliateRate": {
            "type": "number",
            "description": "Commission rate used for this sale, percent (affiliate sales). With affiliate tiers it was decided from the affiliate's sales before this one.",
            "example": 25
          },
          "affiliateTier": {
            "type": "integer",
            "description": "Affiliate tiers: the tier (0 = first) the rate came from. Absent when the affiliate's own rate applied.",
            "example": 1
          },
          "affiliateBonus": {
            "type": "number",
            "description": "Affiliate tiers: the part of `affiliateCut` that is a milestone bonus. Decimal amount in major currency units (e.g. `29.99`), not cents."
          },
          "loyaltyCredit": {
            "type": "number",
            "description": "Loyalty: store credit taken off this order, in your currency. Decimal amount in major currency units (e.g. `29.99`), not cents."
          },
          "loyaltyPointsRedeemed": {
            "type": "integer",
            "description": "Loyalty: points spent on this order",
            "example": 1250
          },
          "loyaltyPointsEarned": {
            "type": "integer",
            "description": "Loyalty: points this order earned",
            "example": 49
          }
        }
      },
      "Order": {
        "description": "An order as listed by `GET /orders`: the one-time `Sale` plus its product, customer reference, fee breakdown and purchased upsells.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Payment"
          },
          {
            "$ref": "#/components/schemas/OrderFees"
          },
          {
            "type": "object",
            "properties": {
              "product": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Product"
                  }
                ],
                "nullable": true,
                "description": "The purchased product (null if it was deleted)"
              },
              "customer": {
                "type": "object",
                "properties": {
                  "_id": {
                    "type": "string",
                    "nullable": true,
                    "description": "Customer id (null if no customer record)"
                  },
                  "buyerEmail": {
                    "type": "string"
                  }
                }
              },
              "country": {
                "type": "string",
                "description": "Buyer country (ISO code)",
                "example": "NL"
              },
              "gross": {
                "type": "number",
                "description": "Gross amount collected (= grossAmount). Decimal amount in major currency units (e.g. `29.99`), not cents.",
                "example": 12.1
              },
              "taxes": {
                "type": "number",
                "description": "Tax collected (= taxAmount). Decimal amount in major currency units (e.g. `29.99`), not cents.",
                "example": 2.1
              },
              "variantId": {
                "type": "string",
                "description": "Purchased variant id, if any"
              },
              "discountId": {
                "type": "string",
                "description": "Applied discount id, if any"
              },
              "upsellProduct": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Product"
                  }
                ],
                "nullable": true,
                "description": "Legacy single upsell product, if one was bought"
              },
              "upsellAmount": {
                "type": "number",
                "description": "Amount paid for the legacy upsell. Decimal amount in major currency units (e.g. `29.99`), not cents.",
                "example": 0
              },
              "installment": {
                "type": "object",
                "nullable": true,
                "description": "Payment plans only: which payment of the plan this order is. The buyer got the product on payment 1; later payments record the money only.",
                "properties": {
                  "index": {
                    "type": "integer",
                    "example": 2
                  },
                  "of": {
                    "type": "integer",
                    "example": 3
                  }
                }
              },
              "pricing": {
                "$ref": "#/components/schemas/PricingRecord"
              },
              "bundleItems": {
                "type": "array",
                "description": "Bundle orders only: every included product and its access-grant order id (`saleId`)",
                "items": {
                  "type": "object",
                  "properties": {
                    "productId": {
                      "type": "string",
                      "example": "665f1c2a9b3e4a0012ab34ce"
                    },
                    "saleId": {
                      "type": "string",
                      "example": "665f1c2a9b3e4a0012ab3500"
                    },
                    "name": {
                      "type": "string",
                      "example": "Notion Content OS"
                    }
                  }
                }
              },
              "tieredDiscount": {
                "type": "object",
                "nullable": true,
                "description": "“Buy more, save more”: the tier this order's checkout reached. Already taken off every amount on the order.",
                "properties": {
                  "percentOff": {
                    "type": "integer",
                    "example": 20
                  },
                  "itemCount": {
                    "type": "integer",
                    "example": 3
                  },
                  "amount": {
                    "type": "number",
                    "description": "Saved, in the charge currency",
                    "example": 19.4
                  },
                  "settlementAmount": {
                    "type": "number",
                    "description": "Saved, in your account currency",
                    "example": 19.4
                  }
                }
              },
              "purchasedUpsells": {
                "type": "array",
                "description": "Upsells bought with this order",
                "items": {
                  "type": "object",
                  "properties": {
                    "upsellId": {
                      "type": "string"
                    },
                    "productId": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "number",
                      "description": "Amount paid for the upsell. Decimal amount in major currency units (e.g. `29.99`), not cents."
                    },
                    "currency": {
                      "type": "string"
                    },
                    "purchasedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "product": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Product"
                        }
                      ],
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "OrderDetail": {
        "type": "object",
        "description": "A single order (`GET /orders/{id}`)",
        "properties": {
          "sale": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Payment"
              },
              {
                "$ref": "#/components/schemas/OrderFees"
              }
            ],
            "description": "The sale record with its fee breakdown"
          },
          "product": {
            "nullable": true,
            "description": "The purchased product — or, for a subscription payment, the subscription offer",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Product"
              },
              {
                "$ref": "#/components/schemas/SubscriptionOffer"
              }
            ]
          },
          "subscription": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SubscriptionOffer"
              }
            ],
            "nullable": true,
            "description": "The subscription offer (subscription payments only)"
          },
          "customer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Customer"
              }
            ],
            "nullable": true
          },
          "review": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Review"
              }
            ],
            "nullable": true,
            "description": "The buyer's review for this order, if any"
          },
          "upsellProduct": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Product"
              }
            ],
            "nullable": true
          },
          "upsell": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Upsell"
              }
            ],
            "nullable": true
          },
          "upsellAmount": {
            "type": "number",
            "description": "Amount paid for the legacy upsell. Decimal amount in major currency units (e.g. `29.99`), not cents."
          },
          "upsellMetadata": {
            "type": "object",
            "nullable": true
          },
          "purchasedUpsells": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Upsells bought with this order (same shape as on `Order`)"
          }
        }
      },
      "Customer": {
        "type": "object",
        "description": "A buyer of yours. One record per buyer email and test/live mode.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Customer id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "buyerEmail": {
            "type": "string",
            "example": "buyer@example.com"
          },
          "firstName": {
            "type": "string",
            "example": "Jane"
          },
          "lastName": {
            "type": "string",
            "example": "Buyer"
          },
          "country": {
            "type": "string",
            "example": "NL"
          },
          "productId": {
            "type": "string",
            "description": "The product of the buyer's first purchase"
          },
          "testMode": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CustomerListItem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Customer"
          },
          {
            "type": "object",
            "properties": {
              "numberOfOrders": {
                "type": "integer",
                "description": "Payments by this buyer (for the `productId` filter, when given)",
                "example": 3
              },
              "products": {
                "type": "array",
                "description": "Every product or subscription offer they bought",
                "items": {
                  "type": "object",
                  "properties": {
                    "_id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "CustomerDetail": {
        "type": "object",
        "properties": {
          "customer": {
            "$ref": "#/components/schemas/Customer"
          },
          "stats": {
            "type": "array",
            "description": "`sales` (total paid, decimal USD), `orders` (one-time orders), `subscriptions`, `since` (first seen)",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "enum": [
                    "sales",
                    "orders",
                    "subscriptions",
                    "since"
                  ]
                },
                "value": {
                  "description": "Number, or a date-time string for `since`"
                }
              }
            }
          },
          "orders": {
            "type": "array",
            "description": "One-time purchases, newest first, each with `product`",
            "items": {
              "$ref": "#/components/schemas/Payment"
            }
          },
          "subscriptions": {
            "type": "array",
            "description": "Their subscriptions, each with `subscription`, `paymentsCount`, `totalPaid` (decimal) and `lastPaymentAt`",
            "items": {
              "$ref": "#/components/schemas/Subscriber"
            }
          },
          "reviews": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Review"
            }
          }
        }
      },
      "Discount": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Discount id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "name": {
            "type": "string",
            "example": "Launch week"
          },
          "code": {
            "type": "string",
            "description": "What buyers enter at checkout",
            "example": "LAUNCH20"
          },
          "value": {
            "type": "number",
            "description": "Percent off (`valueType: percentage`) or a fixed decimal amount off in your account currency (`valueType: fixed`)",
            "example": 20
          },
          "valueType": {
            "type": "string",
            "enum": [
              "percentage",
              "fixed"
            ],
            "default": "percentage"
          },
          "mainProductIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Products / subscription offers the code applies to"
          },
          "active": {
            "type": "boolean",
            "example": true
          },
          "expiration": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the code stops working (checkout rejects it after this date). Absent or null = never expires."
          },
          "used": {
            "type": "integer",
            "description": "Sales that used the code (`GET /discounts` only)"
          },
          "uniquePerRecipient": {
            "type": "boolean",
            "default": false,
            "description": "Unique coupons (Growth plan and above; turning it on answers `403 FEATURE_LOCKED` otherwise). When true the discount is only redeemable through one-time codes issued per recipient — the `{{unique_coupon:<discountId>}}` merge tag in an email, or `POST /discounts/{id}/unique-codes` — and its own `code` becomes the code prefix (it no longer works at checkout by itself). Each code works once, only for the email it was issued to, until it expires."
          },
          "uniqueExpiryHours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 720,
            "default": 48,
            "description": "How long each one-time code lasts after it is issued, in hours"
          },
          "testMode": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Upsell": {
        "type": "object",
        "description": "A post-purchase / order-bump offer shown at checkout for the `mainProductIds`",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Upsell id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "name": {
            "type": "string"
          },
          "offer": {
            "type": "string",
            "description": "Offer headline"
          },
          "mainProductIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Products whose checkout shows this upsell"
          },
          "upsellProductId": {
            "type": "string",
            "description": "The product being offered"
          },
          "upsellPrice": {
            "type": "number",
            "description": "Price of the upsell. Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 9
          },
          "compareAtPrice": {
            "type": "number",
            "description": "Crossed-out price shown next to `upsellPrice`. Decimal amount in major currency units (e.g. `29.99`), not cents.",
            "example": 19
          },
          "upsellDescription": {
            "type": "string"
          },
          "primaryButtonText": {
            "type": "string"
          },
          "secondaryButtonText": {
            "type": "string"
          },
          "active": {
            "type": "boolean"
          },
          "showAtCheckout": {
            "type": "boolean"
          },
          "upsellBackground": {
            "type": "string"
          },
          "upsellBorder": {
            "type": "string"
          },
          "upsellHeadline": {
            "type": "string"
          },
          "upsellDescriptionColor": {
            "type": "string"
          },
          "upsellCheckboxColor": {
            "type": "string"
          },
          "upsellCheckboxBackground": {
            "type": "string"
          },
          "upsellHeadingFont": {
            "type": "string"
          },
          "upsellBodyFont": {
            "type": "string"
          },
          "testMode": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Review": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Review id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "productId": {
            "type": "string"
          },
          "saleId": {
            "type": "string",
            "description": "The order the review belongs to"
          },
          "buyerEmail": {
            "type": "string"
          },
          "rating": {
            "type": "number",
            "minimum": 1,
            "maximum": 5,
            "description": "Star rating, 1–5",
            "example": 5
          },
          "review": {
            "type": "string",
            "description": "Review text"
          },
          "testMode": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RefundPolicy": {
        "type": "object",
        "description": "A refund policy — the refund terms shown to buyers at checkout. Products reference it by id (`refundPolicy`). Not a refund of a payment.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Refund policy id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab5566"
          },
          "name": {
            "type": "string",
            "description": "Internal name",
            "example": "30-day guarantee"
          },
          "policy": {
            "type": "string",
            "description": "Policy text shown to buyers"
          },
          "period": {
            "type": "number",
            "description": "Length of the refund window",
            "example": 30
          },
          "periodType": {
            "type": "string",
            "enum": [
              "days",
              "weeks"
            ],
            "example": "days"
          },
          "appliedTo": {
            "type": "integer",
            "description": "Products using this policy (`GET /refunds` only)"
          },
          "testMode": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Webhook id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "example": "https://acme.com/webhooks/pocketsflow"
          },
          "description": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "order.completed",
                "order.refunded",
                "product.created",
                "product.updated",
                "product.deleted",
                "review.created",
                "customer.created",
                "customer.subscription.created",
                "customer.subscription.updated",
                "customer.subscription.deleted",
                "customer.subscription.pause",
                "customer.subscription.resumed",
                "customer.subscription.trial_will_end",
                "invoice.created",
                "invoice.upcoming",
                "invoice.payment_succeeded",
                "invoice.payment_failed",
                "payment_intent.succeeded",
                "payment_intent.payment_failed",
                "subscription.payment_failed",
                "subscription.recovered",
                "subscription.saved",
                "credit.granted",
                "credit.balance_changed",
                "credit.auto_refill.succeeded",
                "credit.auto_refill.failed",
                "credit.consent.enabled",
                "credit.consent.disabled",
                "usage.recorded",
                "usage.period_closed",
                "order.post_purchase_accepted",
                "funnel.lead",
                "installment.paid",
                "installment.failed",
                "ab_test.completed",
                "affiliate.tier_changed",
                "loyalty.credit_redeemed",
                "split.created",
                "split.paid",
                "course.completed",
                "certificate.issued",
                "testimonial.submitted",
                "member.joined",
                "member.left"
              ]
            }
          },
          "active": {
            "type": "boolean",
            "example": true
          },
          "secret": {
            "type": "string",
            "description": "Signing secret. Returned by `POST /webhooks`, `GET /webhooks/{id}` and `POST /webhooks/{id}/rotate-secret` only — never by the list or update responses."
          },
          "testMode": {
            "type": "boolean",
            "description": "Mode of the endpoint — it only receives events from that mode"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "description": "One delivery attempt from the webhook delivery log (kept for 30 days)",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Delivery id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "webhookId": {
            "type": "string",
            "description": "The endpoint it was sent to"
          },
          "eventId": {
            "type": "string",
            "description": "The event id (`evt_…`) — shared by retries and redeliveries"
          },
          "event": {
            "type": "string",
            "example": "order.completed"
          },
          "url": {
            "type": "string",
            "description": "Endpoint URL at the time of sending"
          },
          "webhookData": {
            "type": "string",
            "description": "The exact JSON body that was sent (a string)"
          },
          "isSuccessful": {
            "type": "boolean",
            "description": "Whether the endpoint answered 2xx"
          },
          "attempt": {
            "type": "integer",
            "description": "Attempt number (1 = first try)"
          },
          "status": {
            "type": "integer",
            "description": "HTTP status the endpoint returned, if any"
          },
          "durationMs": {
            "type": "integer"
          },
          "error": {
            "type": "string",
            "description": "Failure reason, if any"
          },
          "isTest": {
            "type": "boolean",
            "description": "`true` for `POST /webhooks/{id}/test` deliveries"
          },
          "redeliveryOf": {
            "type": "string",
            "description": "The delivery this one replayed (`POST /delivered-webhooks/{id}/redeliver`)"
          },
          "sentDate": {
            "type": "string",
            "format": "date-time"
          },
          "deliveredAt": {
            "type": "string",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the log entry is deleted"
          },
          "testMode": {
            "type": "boolean"
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "description": "The JSON body of an outgoing webhook delivery (not an API response). The envelope fields sit at the top level next to the event's own fields — see the Webhook events section for headers and signature verification.",
        "required": [
          "id",
          "type",
          "created",
          "webhookId"
        ],
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique event id — the same value as the `X-Pocketsflow-Event-Id` header. Retries of one delivery share it.",
            "example": "evt_6a43ce05fae6c4f0cb5a2193"
          },
          "type": {
            "type": "string",
            "enum": [
              "order.completed",
              "order.refunded",
              "product.created",
              "product.updated",
              "product.deleted",
              "review.created",
              "customer.created",
              "customer.subscription.created",
              "customer.subscription.updated",
              "customer.subscription.deleted",
              "customer.subscription.pause",
              "customer.subscription.resumed",
              "customer.subscription.trial_will_end",
              "invoice.created",
              "invoice.upcoming",
              "invoice.payment_succeeded",
              "invoice.payment_failed",
              "payment_intent.succeeded",
              "payment_intent.payment_failed",
              "subscription.payment_failed",
              "subscription.recovered",
              "subscription.saved",
              "credit.granted",
              "credit.balance_changed",
              "credit.auto_refill.succeeded",
              "credit.auto_refill.failed",
              "credit.consent.enabled",
              "credit.consent.disabled",
              "usage.recorded",
              "usage.period_closed",
              "order.post_purchase_accepted",
              "funnel.lead",
              "installment.paid",
              "installment.failed",
              "ab_test.completed",
              "affiliate.tier_changed",
              "loyalty.credit_redeemed",
              "split.created",
              "split.paid",
              "course.completed",
              "certificate.issued",
              "testimonial.submitted",
              "member.joined",
              "member.left"
            ],
            "description": "Event type — the same value as `X-Pocketsflow-Event`",
            "example": "order.completed"
          },
          "created": {
            "type": "integer",
            "description": "When the event was created, Unix seconds",
            "example": 1767225600
          },
          "test": {
            "type": "boolean",
            "description": "`true` on deliveries sent by `POST /webhooks/{id}/test`"
          },
          "webhookId": {
            "type": "string",
            "description": "The endpoint this delivery was sent to (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "clientReferenceId": {
            "type": "string",
            "nullable": true,
            "description": "Echo of the `clientReferenceId` passed at checkout (order and subscription events)"
          },
          "metadata": {
            "type": "object",
            "description": "Echo of the checkout `metadata` (order and subscription events)"
          },
          "licenseKey": {
            "type": "object",
            "description": "`order.completed` of a product with license keys on: the key issued for this order. Absent otherwise.",
            "properties": {
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string",
                "example": "7KQ2M-X9D4R-T3VWB-8HNPC"
              },
              "status": {
                "type": "string",
                "enum": [
                  "active",
                  "disabled"
                ]
              },
              "activationLimit": {
                "type": "integer",
                "nullable": true
              }
            }
          },
          "bundle": {
            "type": "object",
            "description": "`order.completed` of a bundle: every product it delivered, each with its own access-grant order id. Absent otherwise.",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "productId": {
                      "type": "string"
                    },
                    "orderId": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "tieredDiscount": {
            "type": "object",
            "nullable": true,
            "description": "`order.completed` when the checkout reached a “Buy more, save more” tier: the tier, already taken off `amount`. Absent otherwise.",
            "properties": {
              "percentOff": {
                "type": "integer",
                "example": 20
              },
              "itemCount": {
                "type": "integer",
                "example": 3
              },
              "amount": {
                "type": "number",
                "description": "Saved, in the charge currency",
                "example": 19.4
              },
              "settlementAmount": {
                "type": "number",
                "description": "Saved, in your account currency",
                "example": 19.4
              }
            }
          },
          "funnel": {
            "type": "object",
            "description": "`funnel.lead` only: the funnel whose lead magnet was claimed. Sent once per new lead (a repeat claim never fires it).",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "leadMagnetProductId": {
                "type": "string"
              },
              "offerProductId": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "lead": {
            "type": "object",
            "description": "`funnel.lead` only: who got the lead magnet",
            "properties": {
              "email": {
                "type": "string"
              },
              "firstName": {
                "type": "string",
                "nullable": true
              },
              "lastName": {
                "type": "string",
                "nullable": true
              },
              "subscriberId": {
                "type": "string",
                "nullable": true
              },
              "subscribed": {
                "type": "boolean",
                "description": "`false` when the contact had unsubscribed: they are counted as a lead but get no emails"
              },
              "tag": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "capturedAt": {
            "type": "string",
            "format": "date-time",
            "description": "`funnel.lead` only: when the lead was captured"
          },
          "affiliate": {
            "type": "object",
            "description": "`affiliate.tier_changed` only: the affiliate who moved up",
            "properties": {
              "id": {
                "type": "string",
                "example": "6711b4e69e3f1a0012cd8899"
              },
              "email": {
                "type": "string",
                "example": "leo@example.com"
              }
            }
          },
          "previousTier": {
            "type": "object",
            "nullable": true,
            "description": "`affiliate.tier_changed` only: the tier before this sale (null = the base rate)",
            "properties": {
              "index": {
                "type": "integer"
              },
              "minSales": {
                "type": "integer"
              },
              "rate": {
                "type": "number"
              }
            }
          },
          "tier": {
            "type": "object",
            "description": "`affiliate.tier_changed` only: the tier the affiliate is in now",
            "properties": {
              "index": {
                "type": "integer",
                "example": 1
              },
              "minSales": {
                "type": "integer",
                "example": 10
              },
              "rate": {
                "type": "number",
                "example": 25
              }
            }
          },
          "periodSales": {
            "type": "integer",
            "description": "`affiliate.tier_changed` only: sales counted in the period, this one included",
            "example": 11
          },
          "points": {
            "type": "integer",
            "description": "`loyalty.credit_redeemed` only: points spent",
            "example": 1500
          },
          "credit": {
            "type": "object",
            "description": "`loyalty.credit_redeemed` only: the credit taken off the order",
            "properties": {
              "amount": {
                "type": "number",
                "example": 15
              },
              "currency": {
                "type": "string",
                "example": "usd"
              }
            }
          },
          "balance": {
            "type": "object",
            "description": "`loyalty.credit_redeemed` only: the buyer's balance after it",
            "properties": {
              "points": {
                "type": "integer",
                "example": 120
              },
              "credit": {
                "type": "number",
                "example": 1.2
              }
            }
          },
          "pricing": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PricingRecord"
              }
            ],
            "description": "`order.completed`: how the price was reached — early-bird step, flash sale, fair pricing or code (launch pricing). Absent on orders created before it."
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "collaborator"
            ],
            "description": "`split.created` / `split.paid` only: whether the receiving account is the product owner or the co-creator"
          },
          "split": {
            "type": "object",
            "description": "`split.created` only: how the sale was split (amounts in cents of `currency`). The owner's copy carries every share and `ownerCents`; a co-creator's carries only their share.",
            "properties": {
              "saleId": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "saleAmountCents": {
                "type": "integer"
              },
              "platformFeeCents": {
                "type": "integer"
              },
              "affiliateCents": {
                "type": "integer"
              },
              "baseCents": {
                "type": "integer"
              },
              "ownerCents": {
                "type": "integer",
                "nullable": true
              },
              "shares": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "splitId": {
                      "type": "string"
                    },
                    "collaboratorEmail": {
                      "type": "string"
                    },
                    "sharePercent": {
                      "type": "number"
                    },
                    "amountCents": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "held",
                        "paid",
                        "reversed"
                      ]
                    }
                  }
                }
              }
            }
          },
          "payout": {
            "type": "object",
            "description": "`split.paid` only: the transfer of a co-creator's shares (in cents of `currency`), net of any clawback",
            "properties": {
              "id": {
                "type": "string"
              },
              "amountCents": {
                "type": "integer"
              },
              "grossCents": {
                "type": "integer"
              },
              "clawbackCents": {
                "type": "integer"
              },
              "currency": {
                "type": "string"
              },
              "shareCount": {
                "type": "integer"
              },
              "paidAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "lessons": {
            "type": "integer",
            "description": "`course.completed`: number of lessons in the course"
          },
          "completedAt": {
            "type": "string",
            "format": "date-time",
            "description": "`course.completed`: when the buyer finished"
          },
          "certificate": {
            "type": "object",
            "description": "`certificate.issued`: the certificate (also `order`, `product`, `customer`)",
            "properties": {
              "id": {
                "type": "string",
                "example": "7K2Q-X9MD-4HTR"
              },
              "name": {
                "type": "string"
              },
              "course": {
                "type": "string"
              },
              "issuedAt": {
                "type": "string",
                "format": "date-time"
              },
              "verifyUrl": {
                "type": "string"
              },
              "pdfUrl": {
                "type": "string"
              }
            }
          },
          "testimonial": {
            "type": "object",
            "description": "`testimonial.submitted` only: a buyer sent a testimonial from the request page. It waits for your moderation (`status: pending`).",
            "properties": {
              "id": {
                "type": "string"
              },
              "productId": {
                "type": "string"
              },
              "productName": {
                "type": "string"
              },
              "saleId": {
                "type": "string"
              },
              "buyerEmail": {
                "type": "string"
              },
              "displayName": {
                "type": "string"
              },
              "rating": {
                "type": "integer"
              },
              "text": {
                "type": "string"
              },
              "video": {
                "type": "object",
                "nullable": true,
                "description": "The buyer's video, when they sent one",
                "properties": {
                  "url": {
                    "type": "string",
                    "example": "https://d372ebr33sbljd.cloudfront.net/testimonials/auth0%7C123/9f2c1e.webm"
                  },
                  "contentType": {
                    "type": "string",
                    "enum": [
                      "video/webm",
                      "video/mp4",
                      "video/quicktime"
                    ]
                  },
                  "durationSeconds": {
                    "type": "number",
                    "nullable": true,
                    "example": 41.2
                  }
                }
              },
              "status": {
                "type": "string",
                "enum": [
                  "pending"
                ]
              },
              "submittedAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "membership": {
            "type": "object",
            "description": "`member.joined` / `member.left` only: the membership",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              }
            }
          },
          "member": {
            "type": "object",
            "description": "`member.joined` / `member.left` only: who joined or left",
            "properties": {
              "email": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "joinedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "`member.joined` / `member.left` only: when they (last) joined"
          },
          "leftAt": {
            "type": "string",
            "format": "date-time",
            "description": "`member.left` only: when they left"
          },
          "reason": {
            "type": "string",
            "enum": [
              "subscription_ended",
              "membership_changed",
              "membership_closed"
            ],
            "description": "`member.left` only: their subscription ended (canceled, expired, refunded), the membership's offers changed, or the membership was archived / deleted"
          }
        },
        "example": {
          "id": "evt_6a43ce05fae6c4f0cb5a2193",
          "type": "order.completed",
          "created": 1767225600,
          "product": {
            "id": "6949918ee8d99b0e628103e0",
            "variantId": null
          },
          "order": {
            "id": "6a43ce05fae6c4f0cb5a2193"
          },
          "currency": "usd",
          "customer": {
            "email": "buyer@example.com"
          },
          "amount": 1000,
          "amountBeforeTax": 1000,
          "metadata": {},
          "clientReferenceId": null,
          "webhookId": "6a43ca9f7e229e3568f9c9bb"
        }
      },
      "Post": {
        "type": "object",
        "description": "A newsletter post (email campaign)",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Post id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "name": {
            "type": "string",
            "example": "June product update"
          },
          "subject": {
            "type": "string"
          },
          "preview": {
            "type": "string"
          },
          "fromName": {
            "type": "string"
          },
          "fromEmail": {
            "type": "string",
            "description": "Your verified sender address"
          },
          "replyToEmail": {
            "type": "string"
          },
          "contentHtml": {
            "type": "string"
          },
          "contentJson": {
            "type": "object",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "scheduled",
              "sending",
              "sent",
              "failed",
              "automation"
            ],
            "description": "`draft` → `scheduled` → `sending` → `sent` (or `failed`). `automation` posts are templates sent only by automations."
          },
          "scheduledAt": {
            "type": "string",
            "format": "date-time"
          },
          "scheduledTimezone": {
            "type": "string",
            "example": "UTC"
          },
          "sentAt": {
            "type": "string",
            "format": "date-time"
          },
          "sentCount": {
            "type": "integer",
            "description": "Recipients the post was delivered to"
          },
          "failedCount": {
            "type": "integer",
            "description": "Recipients rejected on the last send attempt"
          },
          "failureReason": {
            "type": "string",
            "enum": [
              "SEND_ALLOWANCE_EXCEEDED",
              "SELLER_BLOCKED",
              "EMAIL_SENDING_PAUSED",
              "DAILY_SEND_LIMIT",
              "FEATURE_LOCKED",
              "PAID_NEWSLETTER_DISABLED"
            ],
            "description": "Why a scheduled send was refused at delivery time (or stopped mid-send: `EMAIL_SENDING_PAUSED`). `FEATURE_LOCKED` / `PAID_NEWSLETTER_DISABLED`: a `free` / `paid` post whose plan no longer includes the paid newsletter, or whose paid newsletter was turned off. `FEATURE_LOCKED` is also a resend whose account lost the Growth plan."
          },
          "audience": {
            "type": "string",
            "enum": [
              "everyone",
              "free",
              "paid"
            ],
            "default": "everyone",
            "description": "Who may read the post (paid newsletter, Growth plan). `everyone`: every recipient gets the whole post. `free`: only subscribers without a paid membership. `paid`: paid members get the whole post; free subscribers get the part above the paywall marker plus a subscribe button (see `sendPreviewToFree`). Mark the paywall in `contentHtml` with `{{paywall}}` or an element carrying `data-pf-paywall`. Setting `free` or `paid` returns `403` with `code: FEATURE_LOCKED` when your plan does not include the paid newsletter."
          },
          "sendPreviewToFree": {
            "type": "boolean",
            "default": true,
            "description": "`paid` posts only: email free subscribers the preview above the paywall plus a subscribe button. `false` sends the post to paid members only."
          },
          "publishToWeb": {
            "type": "boolean",
            "default": false,
            "description": "Show the post on your public newsletter archive (`https://<your-subdomain>.pocketsflow.com/newsletter`) once it is sent. Paid posts show their preview and a paywall to anyone who is not a verified paid member."
          },
          "sendToAll": {
            "type": "boolean"
          },
          "subscriberIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "productIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "segmentId": {
            "type": "string",
            "nullable": true,
            "description": "Saved segment this post is sent to"
          },
          "source": {
            "type": "string",
            "enum": [
              "dashboard",
              "api"
            ],
            "description": "`api` for emails sent with `POST /newsletters/send`, `dashboard` otherwise"
          },
          "resendOf": {
            "type": "string",
            "description": "Set on a resend to non-openers: the post it resends (see `POST /newsletters/posts/{id}/resend`)"
          },
          "resend": {
            "type": "object",
            "nullable": true,
            "description": "Set on a sent post with a scheduled resend",
            "properties": {
              "postId": {
                "type": "string"
              },
              "subject": {
                "type": "string"
              },
              "delayHours": {
                "type": "integer"
              },
              "scheduledAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "subjectTest": {
            "type": "object",
            "nullable": true,
            "description": "Set on a post sent with a subject test; full results at `GET /newsletters/posts/{id}/subject-test`"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PaidNewsletterStats": {
        "type": "object",
        "description": "Paid newsletter numbers",
        "properties": {
          "paidSubscribers": {
            "type": "integer",
            "description": "People with a live subscription (active, trialing, past due or paused) to a membership offer"
          },
          "freeSubscribers": {
            "type": "integer",
            "description": "Active subscribers without a paid membership"
          },
          "mrrCents": {
            "type": "integer",
            "description": "Monthly recurring revenue from the membership offers, in cents of `currency` (yearly plans count 1/12)",
            "example": 25500
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "conversionRate": {
            "type": "number",
            "description": "Free → paid conversion: paid / (paid + free), 0 to 1",
            "example": 0.042
          },
          "monthlyMembers": {
            "type": "integer"
          },
          "yearlyMembers": {
            "type": "integer"
          },
          "newPaidLast30Days": {
            "type": "integer"
          },
          "canceledLast30Days": {
            "type": "integer"
          }
        }
      },
      "PaidNewsletter": {
        "type": "object",
        "description": "Your paid newsletter settings, membership offers and numbers",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Paid posts can be sent and the public archive is live"
          },
          "monthlyOfferId": {
            "type": "string",
            "nullable": true,
            "description": "Subscription offer billed monthly that makes someone a paid member"
          },
          "yearlyOfferId": {
            "type": "string",
            "nullable": true,
            "description": "Subscription offer billed yearly that makes someone a paid member"
          },
          "title": {
            "type": "string",
            "description": "Newsletter name shown on the public archive",
            "example": "The Studio Letter"
          },
          "description": {
            "type": "string"
          },
          "archiveEnabled": {
            "type": "boolean",
            "description": "Show the public archive at `/newsletter` on your storefront"
          },
          "lastSyncedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "archiveUrl": {
            "type": "string",
            "example": "https://acme.pocketsflow.com/newsletter"
          },
          "entitled": {
            "type": "boolean",
            "description": "Your plan includes the paid newsletter (Growth and above)"
          },
          "offers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "frequency": {
                  "type": "string",
                  "enum": [
                    "monthly",
                    "yearly"
                  ]
                },
                "price": {
                  "type": "number",
                  "example": 9
                },
                "currency": {
                  "type": "string",
                  "example": "USD"
                },
                "priceLabel": {
                  "type": "string",
                  "nullable": true,
                  "example": "$9/month"
                }
              }
            }
          },
          "stats": {
            "$ref": "#/components/schemas/PaidNewsletterStats"
          }
        }
      },
      "EmailSubscriber": {
        "type": "object",
        "description": "A newsletter contact",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Subscriber id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "email": {
            "type": "string",
            "example": "reader@example.com"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "unsubscribed",
              "bounced",
              "complained"
            ],
            "description": "`bounced` / `complained`: the address hard-bounced or reported your email as spam. Only `active` contacts are emailed."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "productIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Products / subscription offers this contact bought"
          },
          "source": {
            "type": "string",
            "enum": [
              "form",
              "checkout",
              "import",
              "dashboard",
              "api",
              "recommendation"
            ],
            "description": "How the contact joined (absent on older contacts)"
          },
          "country": {
            "type": "string",
            "description": "Billing country of their latest purchase (ISO 3166-1 alpha-2)"
          },
          "totalSpentCents": {
            "type": "integer",
            "description": "Spent in your live store, in cents of your account currency"
          },
          "purchaseCount": {
            "type": "integer"
          },
          "lastPurchaseAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreatorPage": {
        "type": "object",
        "description": "Your Link in Bio page",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Creator page id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "userId": {
            "type": "string"
          },
          "creatorName": {
            "type": "string",
            "example": "Acme Studio"
          },
          "creatorBio": {
            "type": "string"
          },
          "username": {
            "type": "string",
            "description": "Page username — also your store subdomain",
            "example": "acme"
          },
          "profilePicture": {
            "type": "string"
          },
          "coverImage": {
            "type": "string"
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "template": {
            "type": "string",
            "example": "template1"
          },
          "theme": {
            "type": "object",
            "additionalProperties": true
          },
          "themeV2": {
            "type": "object",
            "additionalProperties": true
          },
          "gaMeasurementId": {
            "type": "string"
          },
          "socialLinks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "_id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "customName": {
                  "type": "string"
                }
              }
            }
          },
          "regularLinks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "_id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "customName": {
                  "type": "string"
                }
              }
            }
          },
          "selectedProductIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "selectedSubscriptionIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "catalogVisibilityConfigured": {
            "type": "boolean",
            "description": "`true` once a selection was saved — then only the selected items are shown"
          },
          "subscriberForm": {
            "type": "object",
            "additionalProperties": true
          },
          "bookingEnabled": {
            "type": "boolean"
          },
          "bookingButtonLabel": {
            "type": "string"
          },
          "showFirstName": {
            "type": "boolean"
          },
          "showLastName": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CheckoutSession": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque checkout session id",
            "example": "cs_3f2a9c0d1e2b4a5f6789abcdef0123456789abcdef012345"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Hosted checkout URL on your store subdomain — redirect the buyer here",
            "example": "https://acme.pocketsflow.com/checkout?productId=665f1c2a9b3e4a0012ab34cd&checkout_session_id=cs_3f2a9c0d1e2b4a5f6789abcdef0123456789abcdef012345"
          }
        }
      },
      "User": {
        "type": "object",
        "description": "The authenticated account. Only integrator-relevant fields are listed; other dashboard-internal fields may appear and can change without notice — don't rely on them.",
        "additionalProperties": true,
        "properties": {
          "_id": {
            "type": "string",
            "description": "Account record id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab34cd"
          },
          "userId": {
            "type": "string",
            "description": "Stable account id (owner of every resource)",
            "example": "auth0|68fd3a499605389e7305cdc1"
          },
          "email": {
            "type": "string",
            "format": "email",
            "example": "creator@example.com"
          },
          "name": {
            "type": "string"
          },
          "firstName": {
            "type": "string",
            "example": "Jane"
          },
          "lastName": {
            "type": "string",
            "example": "Creator"
          },
          "creatorName": {
            "type": "string",
            "description": "Store name",
            "example": "Acme Studio"
          },
          "subdomain": {
            "type": "string",
            "description": "Store subdomain — build storefront and product URLs as `https://{subdomain}.pocketsflow.com/…`. Required before `POST /checkout/sessions` works.",
            "example": "acme"
          },
          "currency": {
            "type": "string",
            "description": "Account currency — product prices are stored in it",
            "example": "USD"
          },
          "country": {
            "type": "string",
            "example": "US"
          },
          "testMode": {
            "type": "boolean",
            "description": "With an API key: the key's mode (`true` for `pk_test_…`). With a dashboard session: the dashboard toggle."
          },
          "canSell": {
            "type": "boolean",
            "description": "Whether the account may currently take payments. `false` while selling is paused."
          },
          "hasSales": {
            "type": "boolean",
            "description": "Whether the account has any sale"
          },
          "createdFirstProduct": {
            "type": "boolean"
          },
          "statementDescriptor": {
            "type": "string",
            "description": "The store name buyers see on their card statement"
          },
          "picture": {
            "type": "string",
            "description": "Avatar URL"
          },
          "currentPlan": {
            "type": "string",
            "enum": [
              "free",
              "tier1",
              "tier2",
              "tier3",
              "tier4",
              "volume",
              "volume_max",
              "enterprise"
            ],
            "description": "The plan the account subscribed to. A canceled plan keeps this value until its paid period ends, so read the plan that applies right now from `GET /users/entitlements`."
          },
          "billingInterval": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ],
            "description": "How the plan is billed"
          },
          "facebookPixel": {
            "type": "string",
            "nullable": true,
            "description": "Meta Pixel ID: must be 15 or 16 digits. Empty string or null removes it."
          },
          "googlePixel": {
            "type": "string",
            "nullable": true,
            "description": "Google tag ID: must look like G-XXXXXXX, AW-XXXXXXXXX or GT-XXXXXXX. Empty string or null removes it."
          },
          "googleTagManager": {
            "type": "string",
            "nullable": true,
            "description": "Google Tag Manager container ID: must look like GTM-XXXXXXX. Empty string or null removes it."
          },
          "tiktokPixel": {
            "type": "string",
            "nullable": true,
            "description": "TikTok Pixel ID: must be 16 to 24 letters and numbers (usually 20). Empty string or null removes it."
          },
          "pinterestTag": {
            "type": "string",
            "nullable": true,
            "description": "Pinterest Tag ID: must be 10 to 16 digits (usually 13). Empty string or null removes it."
          },
          "snapchatPixel": {
            "type": "string",
            "nullable": true,
            "description": "Snap Pixel ID: must be a UUID like 1a2b3c4d-1a2b-1a2b-1a2b-1a2b3c4d5e6f. Empty string or null removes it."
          },
          "twitterPixel": {
            "type": "string",
            "nullable": true,
            "description": "X Pixel ID: must be 4 to 8 letters and numbers, like o1abc. Empty string or null removes it."
          },
          "twitterPurchaseEventId": {
            "type": "string",
            "nullable": true,
            "description": "X Purchase event ID: must look like tw-o1abc-o2def. Empty string or null removes it."
          },
          "linkedinPartnerId": {
            "type": "string",
            "nullable": true,
            "description": "LinkedIn Partner ID: must be 4 to 10 digits. Empty string or null removes it."
          },
          "linkedinConversionId": {
            "type": "string",
            "nullable": true,
            "description": "LinkedIn conversion ID: must be 4 to 12 digits. Empty string or null removes it."
          },
          "redditPixel": {
            "type": "string",
            "nullable": true,
            "description": "Reddit Pixel ID: must look like a2_abc123def456. Empty string or null removes it."
          },
          "microsoftUetTag": {
            "type": "string",
            "nullable": true,
            "description": "Microsoft Advertising UET tag ID: must be 5 to 12 digits. Empty string or null removes it."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Subscriber": {
        "type": "object",
        "description": "A subscriber — a buyer's membership (`SubscriptionCustomer`) in one of your subscription offers.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Subscriber id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012abccdd"
          },
          "buyerEmail": {
            "type": "string",
            "example": "buyer@example.com"
          },
          "portalUrl": {
            "type": "string",
            "description": "The subscriber's hosted self-service portal link, including its `?token=…` capability. Send it to the buyer exactly as returned — it is the only link that can cancel or resume; a hand-built `/portal/{subscriptionId}/{subscriberId}` URL without the token opens read-only. Share it only with that buyer.",
            "example": "https://app.pocketsflow.com/portal/665f1c2a9b3e4a0012ab7788/665f1c2a9b3e4a0012abccdd?token=9f2c…"
          },
          "status": {
            "type": "string",
            "description": "Payments-partner-sourced live membership status, maintained by the membership/payment webhooks",
            "enum": [
              "incomplete",
              "incomplete_expired",
              "trialing",
              "active",
              "past_due",
              "canceled",
              "unpaid",
              "succeeded",
              "refunded",
              "paused"
            ],
            "example": "active"
          },
          "active": {
            "type": "boolean",
            "description": "Whether the membership is currently active",
            "example": true
          },
          "partnerPlanId": {
            "type": "string",
            "description": "The payments partner PLAN id for this subscriber",
            "example": "plan_XXXXXXXX"
          },
          "membershipId": {
            "type": "string",
            "description": "The payments partner MEMBERSHIP id. Accepted as `{id}` by the cancel / pause / resume endpoints.",
            "example": "mem_XXXXXXXX"
          },
          "cancelAtPeriodEnd": {
            "type": "boolean",
            "description": "Whether the membership is scheduled to cancel at the end of the current billing period. Stays `active` until the payments partner deactivates it.",
            "example": false
          },
          "subscriptionId": {
            "type": "string",
            "description": "The subscription offer this subscriber belongs to",
            "example": "665f1c2a9b3e4a0012ab7788"
          },
          "firstName": {
            "type": "string",
            "example": "Jane"
          },
          "lastName": {
            "type": "string",
            "example": "Buyer"
          },
          "country": {
            "type": "string",
            "example": "NL"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "subscription": {
            "type": "object",
            "nullable": true,
            "description": "The joined subscription offer",
            "properties": {
              "_id": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "example": "Pro Membership"
              },
              "price": {
                "type": "number",
                "description": "Recurring price of the offer. Decimal amount in major currency units (e.g. `29.99`), not cents.",
                "example": 29
              },
              "frequency": {
                "type": "string",
                "enum": [
                  "weekly",
                  "monthly",
                  "yearly"
                ],
                "example": "monthly"
              }
            }
          }
        }
      },
      "SubscriptionOffer": {
        "type": "object",
        "description": "A recurring subscription offer owned by the authenticated seller",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Subscription offer id (24-character hex MongoDB ObjectId)",
            "example": "665f1c2a9b3e4a0012ab7788"
          },
          "name": {
            "type": "string",
            "example": "Pro Membership"
          },
          "subtitle": {
            "type": "string",
            "example": "Everything in Pro"
          },
          "description": {
            "type": "string",
            "example": "Access to all premium content."
          },
          "price": {
            "type": "number",
            "minimum": 0,
            "description": "Recurring price. Decimal amount in major units, not cents.",
            "example": 29
          },
          "frequency": {
            "type": "string",
            "enum": [
              "weekly",
              "monthly",
              "yearly"
            ],
            "example": "monthly"
          },
          "trialPeriod": {
            "type": "integer",
            "minimum": 0,
            "example": 7
          },
          "published": {
            "type": "boolean",
            "example": true
          },
          "redirectBackUrl": {
            "type": "string",
            "format": "uri",
            "example": "https://creator.example.com/account"
          },
          "thumbnail": {
            "type": "string",
            "format": "uri"
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "testMode": {
            "type": "boolean",
            "example": true
          },
          "productType": {
            "type": "string",
            "enum": [
              "file",
              "url",
              "course",
              "credits"
            ]
          },
          "creditAutoRefill": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "topOffCents": {
                "type": "integer"
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateSubscriptionOfferRequest": {
        "type": "object",
        "required": [
          "name",
          "price",
          "frequency"
        ],
        "properties": {
          "usagePricing": {
            "type": "object",
            "description": "Optional per-seat and usage-based pricing (Pro plan; `403 FEATURE_LOCKED` below it). See `PUT /subscriptions/{id}/usage-pricing`.",
            "properties": {
              "seats": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Price per seat: the buyer picks seats at checkout and pays price × seats each period"
                  },
                  "minSeats": {
                    "type": "integer",
                    "minimum": 1,
                    "default": 1
                  },
                  "maxSeats": {
                    "type": "integer",
                    "nullable": true,
                    "minimum": 1,
                    "maximum": 10000,
                    "description": "`null` = no maximum"
                  },
                  "licenseKeys": {
                    "type": "boolean",
                    "description": "Issue each subscriber a license key whose activation limit follows their seats"
                  }
                }
              },
              "meters": {
                "type": "array",
                "maxItems": 5,
                "items": {
                  "type": "object",
                  "required": [
                    "key",
                    "name",
                    "pricing"
                  ],
                  "properties": {
                    "key": {
                      "type": "string",
                      "pattern": "^[a-z0-9][a-z0-9_.-]{0,39}$",
                      "description": "The meter's id — what you send as `meter` when reporting usage",
                      "example": "api_calls"
                    },
                    "name": {
                      "type": "string",
                      "maxLength": 60,
                      "example": "API calls"
                    },
                    "unit": {
                      "type": "string",
                      "maxLength": 30,
                      "description": "Singular unit name shown to buyers",
                      "example": "call"
                    },
                    "pricing": {
                      "type": "string",
                      "enum": [
                        "per_unit",
                        "graduated",
                        "volume"
                      ],
                      "description": "`per_unit`: every billable unit at `unitAmountCents`. `graduated`: each tier prices the units inside it. `volume`: every billable unit at the rate of the tier the total reaches."
                    },
                    "unitAmountCents": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "`per_unit` only: cents per `packageSize` units (`100` with `packageSize: 1000` = $1 per 1,000)",
                      "example": 100
                    },
                    "packageSize": {
                      "type": "integer",
                      "minimum": 1,
                      "default": 1,
                      "description": "Units one price applies to, so sub-cent unit prices stay exact. Each line is rounded half up to the cent.",
                      "example": 1000
                    },
                    "includedUnits": {
                      "type": "integer",
                      "minimum": 0,
                      "default": 0,
                      "description": "Free units per billing period before anything is charged",
                      "example": 10000
                    },
                    "tiers": {
                      "type": "array",
                      "maxItems": 10,
                      "items": {
                        "type": "object",
                        "required": [
                          "upTo",
                          "unitAmountCents"
                        ],
                        "properties": {
                          "upTo": {
                            "type": "integer",
                            "nullable": true,
                            "minimum": 1,
                            "description": "Inclusive upper bound in billable units (after included units). `null` on the last tier."
                          },
                          "unitAmountCents": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Cents per `packageSize` units in this tier"
                          },
                          "flatAmountCents": {
                            "type": "integer",
                            "minimum": 0,
                            "default": 0,
                            "description": "Flat cents added when any unit lands in this tier"
                          }
                        }
                      },
                      "description": "`graduated` / `volume` only; ascending `upTo`, last one `null`"
                    }
                  }
                }
              }
            }
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Subscription offer name",
            "example": "Pro Membership"
          },
          "subtitle": {
            "type": "string",
            "description": "Short offer subtitle"
          },
          "description": {
            "type": "string",
            "description": "Offer description"
          },
          "price": {
            "type": "number",
            "minimum": 0,
            "description": "Recurring price in USD. Decimal amount in major units, not cents.",
            "example": 29
          },
          "frequency": {
            "type": "string",
            "enum": [
              "weekly",
              "monthly",
              "yearly"
            ],
            "description": "Billing frequency",
            "example": "monthly"
          },
          "trialPeriod": {
            "type": "integer",
            "minimum": 0,
            "default": 0,
            "description": "Trial duration in days"
          },
          "published": {
            "type": "boolean",
            "default": true,
            "description": "Whether the offer is published"
          },
          "redirectBackUrl": {
            "type": "string",
            "format": "uri",
            "description": "Return URL used by the customer billing portal; defaults to https://pocketsflow.com"
          },
          "refundPolicy": {
            "type": "string",
            "description": "Refund policy id (from `POST /refunds`)"
          },
          "checkoutText": {
            "type": "string"
          },
          "portalText": {
            "type": "string"
          },
          "privacyPolicy": {
            "type": "string",
            "format": "uri"
          },
          "termsOfService": {
            "type": "string",
            "format": "uri"
          },
          "thumbnail": {
            "type": "string",
            "format": "uri"
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Existing image references or uploaded images in multipart requests"
          },
          "file": {
            "description": "File URL or uploaded file reference",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "object",
                "additionalProperties": true
              }
            ]
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "isFile": {
            "type": "boolean"
          },
          "hasFirstName": {
            "type": "boolean",
            "default": false
          },
          "hasLastName": {
            "type": "boolean",
            "default": false
          },
          "callbackUrl": {
            "type": "string",
            "format": "uri"
          },
          "hasCallbackUrl": {
            "type": "boolean"
          },
          "productType": {
            "type": "string",
            "enum": [
              "file",
              "url",
              "course",
              "credits"
            ],
            "description": "Catalog type. Additive `credits` is prepaid balance with optional auto-refill."
          },
          "creditAutoRefill": {
            "type": "object",
            "description": "Seller auto-refill setting. `topOffCents` is integer cents.",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "topOffCents": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          "ctaText": {
            "type": "string"
          },
          "thankYouText": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "hasProductPage": {
            "type": "boolean"
          },
          "showSales": {
            "type": "boolean"
          },
          "showReviews": {
            "type": "boolean"
          },
          "maxPrice": {
            "type": "number",
            "minimum": 0
          },
          "minPrice": {
            "type": "number",
            "minimum": 0
          },
          "design": {
            "type": "object",
            "additionalProperties": true
          },
          "colors": {
            "type": "object",
            "additionalProperties": true
          },
          "pageStyle": {
            "type": "object",
            "additionalProperties": true
          },
          "checkoutStyle": {
            "type": "object",
            "additionalProperties": true
          },
          "checkoutColors": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "PartnerPagination": {
        "type": "object",
        "description": "Pagination block of the partner referral lists",
        "properties": {
          "currentPage": {
            "type": "integer",
            "example": 1
          },
          "totalPages": {
            "type": "integer",
            "example": 3
          },
          "totalItems": {
            "type": "integer",
            "example": 47
          },
          "hasNextPage": {
            "type": "boolean"
          },
          "hasPrevPage": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer",
            "example": 20
          }
        }
      },
      "Partner": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string"
          },
          "userId": {
            "type": "string"
          },
          "referralCode": {
            "type": "string",
            "example": "PF-a1b2c3d4"
          },
          "commissionRate": {
            "type": "number",
            "description": "Commission percentage",
            "example": 20
          },
          "active": {
            "type": "boolean"
          },
          "totalSignups": {
            "type": "number"
          },
          "totalSales": {
            "type": "number"
          },
          "totalRevenue": {
            "type": "number",
            "description": "Revenue from referred users. Decimal amount in major currency units (e.g. `29.99`), not cents."
          },
          "totalCommission": {
            "type": "number",
            "description": "Commission earned. Decimal amount in major currency units (e.g. `29.99`), not cents."
          }
        }
      }
    }
  },
  "security": [
    {
      "apiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Products",
      "description": "Create and manage your one-time digital products. Subscription (recurring) offers are a separate resource — see Subscriptions."
    },
    {
      "name": "Checkout",
      "description": "Create hosted checkout links for your products and offers"
    },
    {
      "name": "Orders",
      "description": "View your one-time product orders"
    },
    {
      "name": "Payments",
      "description": "Query the unified payment ledger — one-time purchases and subscription renewals — sourced from the payment processor"
    },
    {
      "name": "Licenses",
      "description": "License keys for software you sell. Turn on **License keys** on a one-time product and every sale gets one key (shown to the buyer in their purchases and receipt, and sent as `licenseKey` on the `order.completed` webhook). Your app calls the public validate / activate / deactivate endpoints with the product id and the key — no secret in the app. A refunded or disputed order's key stops validating. Manage keys (disable, re-enable, reset activations) with your API key."
    },
    {
      "name": "Analytics",
      "description": "Reports on your store. **Advanced analytics** (Growth plan and above): the funnel visits → product views → checkout started → paid per product and traffic source, revenue by source and UTM tag, repeat-buyer cohorts, net revenue after refunds, each compared with the previous period. **MRR reports** (Pro and above): MRR and its movements, subscribers, logo and revenue churn, ARPU, LTV and trial conversions, from a daily snapshot that never changes once a day is over. Money is in your account currency (`currency`). A lower plan gets `403 FEATURE_LOCKED`; `GET /analytics/preview` works on every plan."
    },
    {
      "name": "Segments",
      "description": "Saved audience segments (Growth plan and above): rule-based groups of your contacts — what they bought, how much they spent, when they last bought, opened or clicked, tags, country and how they subscribed. Use a segment as the audience of a post (`segmentId`). Counts are live: rules are evaluated when you count or send."
    },
    {
      "name": "Customer CRM",
      "description": "Customer profiles (Pro plan and above): each buyer's lifetime value net of refunds, orders, products, subscriptions, email engagement, segments, private notes and tags, plus a best-customers list sorted by lifetime value, orders or last purchase. Lifetime value is kept up to date on every sale and refund."
    },
    {
      "name": "Benchmarks",
      "description": "Your conversion rate, average order, refund rate and email open rate next to anonymised stores of a similar size (Pro plan and above). Cohorts are stores with similar revenue over the last 90 days (and the same kind of store when there are enough of them), recomputed every night. Only the 25th percentile, median and 75th percentile of cohorts of 10 or more stores are ever shown; test, banned and fraud-labelled stores are left out."
    },
    {
      "name": "Search",
      "description": "One query across your orders, customers, products (and subscription offers), subscribers and discounts — the same search as ⌘K in the dashboard. Matches the start of an email, a first or last name, a discount code or a product slug, any word of a product, offer or discount name, a full or partial id (`#6711A3D5…`) and an amount (`49`, `$49.99`). Results come from the mode of your key: a `pk_test_` key searches test data, a `pk_live_` key live data. Limited to 120 searches per minute per account."
    },
    {
      "name": "Usage billing",
      "description": "Charge subscribers per seat or per unit of usage (API calls, credits, team members). Define seats and meters on a subscription offer, report usage for each subscriber, and Pocketsflow bills the overage at the end of every billing period — once, to the card the subscription pays with. Requires the Pro plan (`403 FEATURE_LOCKED` below it). See the Usage-based billing guide."
    },
    {
      "name": "Tracked links",
      "description": "Short links for every post, bio and ad, each showing the visits and sales it brought in (Starter plan and above). A link lives at `https://<your-store>/go/<slug>`: it counts the visit (bots, link previews and repeat visits within 30 minutes are ignored), then redirects to the destination with UTM parameters and `pf_link=<id>`. The storefront keeps `pf_link` for 30 days and a purchase in that window is credited to the link the buyer clicked last. Below Starter, existing links keep redirecting and keep recording; creating or editing needs the plan and stats come back as `null` (`statsLocked: true`) until you upgrade."
    },
    {
      "name": "Courses",
      "description": "Drip content, quizzes and certificates for course products (Growth plan and above). Unlock lessons some days after purchase or on a date, add a quiz to any lesson (optionally one the buyer must pass to continue), and give a certificate with a public verify page when a buyer finishes. Locked lessons are enforced by the server: their video and files are never sent to the buyer until they open. Lessons are identified by `lessonId`, which stays the same across product edits — send it back in `episodes` when you update a course's lessons. Reading settings and stats and deleting a quiz work on any plan; changing drip rules, quizzes or the certificate needs Growth. Webhooks: `course.completed`, `certificate.issued`."
    },
    {
      "name": "Team",
      "description": "Invite people to run your store with you (Growth plan and above). Owners can do everything, including payouts and billing; admins everything except money and ownership; members only the areas of their teams. Seats: Growth 5, Pro 15, Scale 50, Volume unlimited (the owner counts as one). With an API key you act as the store owner. Payouts, billing and ownership are never reachable by members."
    },
    {
      "name": "Referrals",
      "description": "Refer other creators to Pocketsflow with your referral link. When a store you referred pays for a plan and the payment clears the 14-day refund window, you earn one free month of your own plan, applied to your next monthly plan invoice. At most 12 free months a year, shared with the Powered by Pocketsflow badge. Your own stores and accounts that look like the same person never count, and neither do suspended or fraudulent stores. Free on every plan."
    },
    {
      "name": "Recommendations",
      "description": "Recommend up to 3 other creators. When someone subscribes to you, the success message suggests them; one click sends a confirmation email, and the subscriber joins their list only after confirming (double opt-in). The creators you recommend must allow recommendations, and only active stores in good standing are shown. Free on every plan."
    },
    {
      "name": "Agencies",
      "description": "The Agency partner program: build and run stores for your clients from one login and earn a share (20% by default) of each client's Pocketsflow plan payments for 12 months from the day they invite you. A client invites your agency with your agency code; you become an admin of their store (everything except payouts, billing and ownership) and it appears in your store switcher. Earnings clear after a 14-day refund window and are listed on monthly statements. Free on every plan. With an API key, the agency routes act on the key owner's agency and the agency-access routes on the key owner's store."
    },
    {
      "name": "Loyalty",
      "description": "Loyalty points and store credit (Growth plan and above). Buyers earn points on every paid order — `earnPointsPerUnit` points per 1.00 paid, in your currency, before tax, after discounts and after any credit — and spend them as store credit on their next order (`pointsPerCreditUnit` points = 1.00). At checkout the credit is applied by the server after every discount, never below the 1.00 minimum charge; the platform fee is charged on what the buyer actually pays. A refund takes back the points an order earned and gives back the credit it spent. Points can expire `expiryDays` after the buyer's last purchase. Every movement is in the ledger, recorded once."
    },
    {
      "name": "Affiliate tiers",
      "description": "Tiered affiliate commissions and milestone bonuses (Pro plan and above), on top of your affiliate program. The rate of a sale is decided when its checkout is created, from the affiliate's paid, non-refunded sales in the current period BEFORE this sale: the tier with the highest `minSales` that is at most that count wins (below the first tier, the affiliate's own rate applies). The rate used is stored on the order. Affiliates with their own commission tiers keep them. A milestone bonus is earned once, when the affiliate reaches `sales` all-time paid sales, and is paid like commission: it is collected from the order that reaches the milestone (up to what is left after the platform fee and commission; any rest from the next orders) and paid out with those orders by the regular affiliate payout run. A refund before payout drops the bonus part that order carried; if it was the order that reached the milestone, the bonus is earned again by the next order that reaches it."
    },
    {
      "name": "Sales funnels",
      "description": "A free lead magnet → an email sequence → a paid offer, set up from one template (Growth plan and above). Creating a funnel creates a real automation and real email posts you can edit; activating it turns the automation on (paid plan, verified sender). Someone who gets the lead magnet joins your audience, starts the sequence once, and fires the `funnel.lead` webhook. Offer purchases count for the funnel when the buyer got the lead magnet first and bought within 30 days."
    },
    {
      "name": "Migrations",
      "description": "Move a store from Gumroad, Lemon Squeezy, Stan Store or any platform's CSV exports (Volume plan and above): products, customers, purchase history and email subscribers. The flow is: create an import → add a CSV per kind (`POST /migrations/{id}/files`) or pull with your platform token (`POST /migrations/{id}/connect`, Gumroad and Lemon Squeezy) → check rows and fix the column mapping → `dry-run` → `start` (runs in the background; poll `GET /migrations/{id}`) → `report` and `errors.csv`. Runs are idempotent and resumable: nothing is created twice. Products are created unpublished (with their file when the export has a download link, else marked \"file needed\"). Purchase history is saved as imported records and never counts as sales, revenue, fees or payouts. Subscribers are never emailed by the import; their consent source is recorded, opt-outs keep their status and addresses on the bounce / complaint list are left out. API tokens are used for one pull and never stored. Canonical fields — products: `name` (required), `description`, `price`, `priceCents`, `currency`, `thumbnail`, `fileUrl`, `sourceUrl`, `sourceId`; customers: `email` (required), `name`, `firstName`, `lastName`, `country`, `sourceId`; orders: `email` (required), `productName` (required), `name`, `amount`, `amountCents`, `currency`, `purchasedAt`, `sourceOrderId`, `sourceProductId`, `country`, `refunded`, `status`; subscribers: `email` (required), `name`, `subscribedAt`, `status`, `doNotContact`, `tags`."
    },
    {
      "name": "Launch pricing",
      "description": "Flash sales, fair pricing by country and early-bird price steps (Growth plan and above). The checkout computes every price on the server with ONE rule: an open early-bird step is the price in effect; then at most one discount applies — the best for the buyer of the live flash sales, fair pricing for their country and the code they entered (never stacked). Automatic discounts never go under 1.00 or make an order free. Each order carries `pricing` (how its price was reached), and so does the `order.completed` webhook. Turning a feature on needs the plan; turning it off, deleting and reading never do. When a store moves below Growth, its products go back to list prices."
    },
    {
      "name": "Bundles",
      "description": "Sell several of your products as one bundle at one price (Growth plan and above). A bundle is a normal product — its own page, price, checkout, discount codes — whose `productIds` lists 2–10 of your other one-time products. Buying it is one order, one charge and one platform fee; the buyer then gets every included product exactly as if each had been bought (files, links, courses, license keys), each with its own access-grant order (`bundle.items[].orderId` on `order.completed`). Refunding the bundle order revokes every included product. Below Growth, an existing bundle stops selling as a bundle until the plan is back; nothing is deleted."
    },
    {
      "name": "Buy more, save more",
      "description": "Store-wide tiered discounts such as “2 items 10% off, 3+ items 20% off” (Growth plan and above). When one checkout holds several products — the main product plus order bumps or a downsell — the best tier the item count reaches is taken off the cart subtotal, server-side, after every per-item price is final. A bundle counts as one item. A tier never stacks with a discount code unless `combineWithCodes` is true. The order records it as `tieredDiscount` and `order.completed` carries it."
    },
    {
      "name": "Revenue splits",
      "description": "Sell one product with co-creators and split every sale automatically (Growth plan and above to invite). The owner invites a co-creator by email with a % share; the co-creator must have a Pocketsflow account with payouts set up and accepts the invite; every sale made after that is split. Up to 5 co-creators per product, shares add up to 100% at most. **How a sale is split:** the platform fee is taken once on the full pre-tax sale, then any affiliate commission; what is left (the split base) is shared: each co-creator gets `floor(base × share)` in cents and the owner keeps the rest, including every rounding remainder. Payment processing costs stay with the owner. Refunds and disputes reverse the shares of that sale; a share refunded after it was paid is netted from the co-creator's next payout. A co-creator account that is banned, deleted or under fraud review has its shares held, not paid. Money is in integer cents of the seller's settlement currency. Shares are recorded as `pending` and paid out after a 14-day hold once split payouts are switched on for the platform (`payoutsEnabled` on `GET /revenue-splits/earnings`)."
    },
    {
      "name": "Signup forms",
      "description": "Signup forms and popups for any website, all feeding your Pocketsflow audience (Growth plan and above). Each form gets a one-line script embed (`<script src=\"https://app.pocketsflow.com/forms.js\" data-pocketsflow-form=\"<id>\" async></script>`) and an iframe fallback. Signups join your audience with the form's tags (after a confirmation click with double opt-in); views, signups and conversion are counted per form. Below Growth, forms stay in your account but stop showing until you upgrade."
    },
    {
      "name": "Testimonials",
      "description": "A testimonial wall with video (Growth plan and above). A set number of days after a purchase, Pocketsflow emails the buyer once asking for a few words or a short video (up to 90 seconds, recorded in the browser or uploaded; MP4, MOV or WebM, 50 MB at most, type checked from the file's bytes). A testimonial is a review with extras: the same stars, plus the buyer's consent, the name they chose and the video. You moderate it — approve, feature or hide — and approved ones show on the product page (the wall) and, featured first, on checkout. Hiding only removes it from the wall: the star rating stays in the product's reviews."
    },
    {
      "name": "Attribution survey",
      "description": "\"How did you hear about us?\" on the thank-you page (Growth plan and above): one question with your answers plus an optional \"Other\" free-text field. Each answer is stored on the order, so the report shows answers and revenue per channel — including the ones links can't track, like podcasts and word of mouth — next to your tracked links' sales for the same period."
    },
    {
      "name": "Memberships",
      "description": "Members-only posts, a community and included products for your subscribers (Pro plan and above). A membership is tied to one or more of your subscription offers: anyone with a live subscription (active, trialing, past due or paused) to one of them is a member, checked on every request, so access ends with the subscription. Members sign in on your storefront at `/members` with an emailed link. Creating or editing a membership and adding content (posts, creator threads and replies) need the plan; reading, archiving, deleting and moderation (hide, pin, lock, delete, ban) never do. Fires the `member.joined` and `member.left` webhooks. Member posting is limited to 5 threads, 30 replies and 200 reactions an hour."
    },
    {
      "name": "Customers",
      "description": "Access your customer base"
    },
    {
      "name": "Subscriptions",
      "description": "Manage subscription offers (the recurring products you sell) and subscribers (buyers' memberships). The hosted subscriber portal link is delivered as `portalUrl` on subscribers and in subscription webhooks — pass it to the buyer unchanged (its `token` is what allows cancel/resume)."
    },
    {
      "name": "Subscription retention",
      "description": "Keep subscribers paying: failed payment recovery (automatic retries on day 1, 3, 5 and 7 after a failed renewal, a secure update-card email to the buyer, then cancel / pause / keep), cancellation save offers in the subscriber portal (pause 1–3 months or a discount for the next payments) and intro pricing (an intro price for the first periods plus a switch-to-yearly offer). Configuration writes need the Pro plan; reads are always open. Events: `subscription.payment_failed`, `subscription.recovered`, `subscription.saved`."
    },
    {
      "name": "Discounts",
      "description": "Create and manage discount codes"
    },
    {
      "name": "Upsells",
      "description": "Configure upsell offers shown at checkout"
    },
    {
      "name": "Personal deadlines",
      "description": "Per-subscriber offer windows (Pro plan): each person gets their own countdown that starts when they join, claim a lead magnet or enter an automation. The offer is enforced at checkout from the person's server-side clock."
    },
    {
      "name": "Creator services",
      "description": "Priority support (Pro and above): your support tickets are answered first, within the promised business hours, and you can ask our team for help writing a dispute response. Dedicated account manager (Volume and above): a named person, their booking link and a shared Slack channel. Dispute fees covered (Volume and above): when a buyer disputes a sale made while you are on the plan, Pocketsflow covers the dispute fee and writes the response, up to a monthly number of disputes. Reads never need the plan, so history stays visible after a downgrade."
    },
    {
      "name": "Google Sheets sync",
      "description": "New sales and new customers are appended to a Google Sheet automatically (Pro plan and above). Connect a Google account (Pocketsflow asks for the `drive.file` permission only, so it can open just the spreadsheets it creates), create the spreadsheet, and every live sale lands on the `Sales` tab and every new buyer on the `Customers` tab within a few minutes. Each sale is written once per spreadsheet, rows are sent in batches and retried with backoff, and a backfill adds up to 365 days of past sales. When the server has no Google connection configured, `GET /sheets-sync` answers `configured: false`."
    },
    {
      "name": "A/B tests",
      "description": "Split a product's visitors between 2–4 versions of its price, headline, cover image or checkout layout, then keep the winner (Pro plan). Each visitor keeps one variant (first-party cookie); price variants are enforced at checkout from the server's record of the visitor. A paid sale is a conversion. The `ab_test.completed` webhook fires when a test ends."
    },
    {
      "name": "Refunds",
      "description": "Refund POLICIES — the refund terms shown to buyers at checkout, referenced by products via `refundPolicy`. These endpoints do not refund payments: refunding a payment is done from the dashboard and has no API-key endpoint. Refunds that happen arrive as the `order.refunded` webhook and `isRefunded` on payments."
    },
    {
      "name": "Reviews",
      "description": "Read your product reviews"
    },
    {
      "name": "Posts",
      "description": "Create, schedule and send newsletter email campaigns, or send one-off emails"
    },
    {
      "name": "Subscribers",
      "description": "Manage your newsletter email contacts and product audiences"
    },
    {
      "name": "Paid newsletter",
      "description": "Turn your newsletter into a paid one (Growth plan): pick the subscription offers that make someone a paid member, choose who reads each post, and read paid subscribers, MRR and conversion"
    },
    {
      "name": "Link in Bio",
      "description": "Manage your Link in Bio page (creator page): profile, links, theme and which products it shows"
    },
    {
      "name": "Images",
      "description": "Upload images"
    },
    {
      "name": "Webhooks",
      "description": "Register endpoints that receive real-time event notifications, rotate their signing secrets, and inspect or replay deliveries from the delivery log. See **Webhook events** for the delivery format and the `WebhookEvent` schema."
    },
    {
      "name": "Webhook events",
      "description": "Events Pocketsflow POSTs to the webhook endpoints you register with `POST /webhooks`. Each delivery is an HTTP `POST` with a JSON body (see the `WebhookEvent` schema) and these headers:\n\n| Header | Value |\n| --- | --- |\n| `X-Pocketsflow-Event` | The event type, e.g. `order.completed` (same as the body's `type`) |\n| `X-Pocketsflow-Event-Id` | The event id, `evt_…` (same as the body's `id`) — use it to de-duplicate retried deliveries |\n| `X-Pocketsflow-Signature-V2` | `t=<unix seconds>,v1=<hex HMAC-SHA256(secret, t + \".\" + rawBody)>` — verify this one and reject stale `t` values to stop replays |\n| `X-Pocketsflow-Signature` | Legacy: hex HMAC-SHA256(secret, rawBody) |\n| `X-Pocketsflow-Timestamp` | Send time in Unix epoch **milliseconds** (informational, not signed) |\n\nThe signing `secret` is returned by `POST /webhooks` (and `GET /webhooks/{id}`, `POST /webhooks/{id}/rotate-secret`). Always verify against the raw request bytes before parsing JSON.\n\nEach attempt has a 15-second timeout; `307`/`308` redirects are followed (up to 3 hops, each re-checked) and other redirects are not. Respond with a `2xx` quickly and do the work asynchronously. A delivery that gets a `5xx` or a `429`, times out, or never reaches your endpoint (connection refused, DNS failure), is retried — up to 4 attempts in total, about 10 s, 1 min and 5 min apart — with the same event id, so make your handler idempotent on it. Every attempt is recorded in the delivery log (`GET /delivered-webhooks`, kept 30 days), from which you can replay an event with `POST /delivered-webhooks/{id}/redeliver`.\n\nThe body is a flat object: the envelope fields (`id`, `type`, `created`, `webhookId`) sit next to the event's own fields. Test deliveries (`POST /webhooks/{id}/test`) add `test: true`. Event types: `order.completed`, `order.refunded`, `product.created`, `product.updated`, `product.deleted`, `review.created`, `customer.created`, `customer.subscription.created`, `customer.subscription.updated`, `customer.subscription.deleted`, `customer.subscription.pause`, `customer.subscription.resumed`, `customer.subscription.trial_will_end`, `invoice.created`, `invoice.upcoming`, `invoice.payment_succeeded`, `invoice.payment_failed`, `payment_intent.succeeded`, `payment_intent.payment_failed`, `subscription.payment_failed`, `subscription.recovered`, `subscription.saved`, `credit.granted`, `credit.balance_changed`, `credit.auto_refill.succeeded`, `credit.auto_refill.failed`, `credit.consent.enabled`, `credit.consent.disabled`, `usage.recorded`, `usage.period_closed`, `order.post_purchase_accepted`, `funnel.lead`, `installment.paid`, `installment.failed`, `ab_test.completed`, `affiliate.tier_changed`, `loyalty.credit_redeemed`, `split.created`, `split.paid`, `course.completed`, `certificate.issued`, `testimonial.submitted`, `member.joined`, `member.left`. Payloads per event are documented at https://docs.pocketsflow.com/api-webhooks/events."
    },
    {
      "name": "Account",
      "description": "Read the authenticated account and set its ad tracking pixels"
    },
    {
      "name": "Plans",
      "description": "What your plan unlocks (`GET /users/entitlements`) and the public plans catalogue (`GET /users/billing-tiers`). A write that turns on a feature your plan does not include answers `403 FEATURE_LOCKED` (see `FeatureLocked`); the creator upgrades at https://app.pocketsflow.com/profile/billing. Plans are bought in the dashboard only."
    },
    {
      "name": "Weekly report",
      "description": "The Monday weekly growth report: last week's sales, visits and conversion, plus one recommended next step. Read it any day, and turn the email on or off. Free on every plan."
    },
    {
      "name": "Partners",
      "description": "Become a Pocketsflow partner, refer users, and earn commissions"
    },
    {
      "name": "Store badge",
      "description": "The \"Powered by Pocketsflow\" badge on your storefront, product pages, checkout, subscription pages, invoices, refund policy and buyer receipts. It links to pocketsflow.com with your referral code: every store it brings in that makes its first payment earns you one free month of your plan (at most 12 a year, after a 14-day clearance). Hiding the badge needs the Starter plan or higher."
    }
  ],
  "paths": {
    "/products": {
      "get": {
        "operationId": "listProducts",
        "summary": "List all products",
        "description": "Retrieve your one-time products in the key's mode, newest first. Returns a bare array (not paginated). Subscription (recurring) offers are a separate resource — list them with `GET /subscriptions`.",
        "tags": [
          "Products"
        ],
        "responses": {
          "200": {
            "description": "List of products",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Product"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createProduct",
        "summary": "Create a product",
        "description": "Create a new one-time product. Send JSON, or `multipart/form-data` to upload `file`, `thumbnail` and `images`. Pay-what-you-want products (`payWant: true`) must also send `hasProductPage: \"yes\"`. Fires the `product.created` webhook.",
        "tags": [
          "Products"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Product name. Required when creating.",
                    "example": "Starter Kit"
                  },
                  "price": {
                    "type": "number",
                    "minimum": 0,
                    "default": 0,
                    "description": "Price in your account currency (`currency` on `GET /users/me`). Decimal amount in major units (e.g. `29.99`), not cents. `0` makes the product free.",
                    "example": 29
                  },
                  "compareAtPrice": {
                    "type": "number",
                    "minimum": 0,
                    "description": "Optional merchandising price shown crossed out next to `price`. Display only — it never affects what the buyer is charged.",
                    "example": 49
                  },
                  "description": {
                    "type": "string",
                    "description": "Product description"
                  },
                  "subtitle": {
                    "type": "string",
                    "description": "Product subtitle"
                  },
                  "published": {
                    "type": "boolean",
                    "default": true,
                    "description": "Whether the product is published (visible and purchasable). New products are always created published; send `false` on update to unpublish."
                  },
                  "slug": {
                    "type": "string",
                    "description": "URL slug for the product page"
                  },
                  "thumbnail": {
                    "type": "string",
                    "description": "Thumbnail image URL. When empty, the first of `images` is used."
                  },
                  "images": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Gallery image references (image ids). Upload new images with `multipart/form-data` (file field `images`)."
                  },
                  "hasProductPage": {
                    "type": "string",
                    "enum": [
                      "yes",
                      "no"
                    ],
                    "description": "Whether the product has its own product page. Must be `\"yes\"` whenever `payWant` is `true` in the same request — otherwise the request is rejected with `400`."
                  },
                  "payWant": {
                    "type": "boolean",
                    "default": false,
                    "description": "Enable pay-what-you-want pricing. Requires `hasProductPage: \"yes\"` in the same request."
                  },
                  "minPrice": {
                    "type": "number",
                    "minimum": 0,
                    "description": "Minimum price for pay-what-you-want (decimal, account currency)"
                  },
                  "maxPrice": {
                    "type": "number",
                    "minimum": 0,
                    "description": "Maximum price for pay-what-you-want (decimal, account currency)"
                  },
                  "refundPolicy": {
                    "type": "string",
                    "description": "Id of one of your refund policies (`_id` from `POST /refunds` / `GET /refunds`) — not policy text. Empty string for none.",
                    "example": "665f1c2a9b3e4a0012ab5566"
                  },
                  "showSales": {
                    "type": "boolean",
                    "default": true,
                    "description": "Show the sales count on the product page"
                  },
                  "showReviews": {
                    "type": "boolean",
                    "default": true,
                    "description": "Show reviews on the product page"
                  },
                  "hasFirstName": {
                    "type": "boolean",
                    "default": false,
                    "description": "Collect the buyer's first name at checkout"
                  },
                  "hasLastName": {
                    "type": "boolean",
                    "default": false,
                    "description": "Collect the buyer's last name at checkout"
                  },
                  "allowGifts": {
                    "type": "boolean",
                    "default": true,
                    "description": "Offer \"This is a gift\" at checkout: the buyer pays and the product is emailed to a recipient. Ignored for credit packs."
                  },
                  "productType": {
                    "type": "string",
                    "enum": [
                      "file",
                      "url",
                      "course",
                      "credits"
                    ],
                    "description": "Catalog type. Additive `credits` is prepaid balance with optional auto-refill. Absent on legacy products."
                  },
                  "availability": {
                    "type": "string",
                    "enum": [
                      "available",
                      "preorder",
                      "waitlist"
                    ],
                    "default": "available",
                    "description": "`available`: on sale as usual. `preorder`: buyers pay now and the content unlocks on `releaseAt` (one-time products only — not `credits`). `waitlist`: no checkout; the product page collects emails and the waitlist is emailed once when you switch the product to `available` or `preorder`.",
                    "example": "preorder"
                  },
                  "releaseAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Release date of a pre-order (ISO 8601). Required when `availability` is `preorder`; must be in the future and at most two years away. On that date the product switches to `available` and every pre-order buyer is emailed.",
                    "example": "2026-11-20T09:00:00.000Z"
                  },
                  "file": {
                    "type": "string",
                    "description": "URL of the downloadable file (file products). Upload a file with `multipart/form-data` (file field `file`)."
                  },
                  "url": {
                    "type": "string",
                    "description": "Link delivered to the buyer (url products)"
                  },
                  "ctaText": {
                    "type": "string",
                    "description": "Buy-button label key or text (defaults to `buyForPrice`)"
                  },
                  "checkoutText": {
                    "type": "string",
                    "description": "Extra text shown on the checkout page"
                  },
                  "thankYouText": {
                    "type": "string",
                    "description": "Text shown after a successful purchase"
                  },
                  "creditAutoRefill": {
                    "type": "object",
                    "description": "Seller auto-refill setting. `topOffCents` is integer cents (two decimal places). Enabling without a positive top-off is ignored.",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "topOffCents": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100000000
                      }
                    }
                  },
                  "licenseKeys": {
                    "type": "object",
                    "description": "License keys: with `enabled`, every sale of this product gets one key. `activationLimit` is the number of activations per key (null = unlimited).",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "activationLimit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 10000,
                        "nullable": true
                      }
                    }
                  }
                }
              },
              "example": {
                "name": "Starter Kit",
                "price": 29,
                "compareAtPrice": 49,
                "description": "Everything to get going.",
                "hasProductPage": "yes"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created product",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — e.g. `payWant` without `hasProductPage: \"yes\"`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Pay What You Want pricing requires a product page. Please enable the product page option."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}": {
      "get": {
        "operationId": "getProduct",
        "summary": "Get product by ID",
        "description": "Retrieve one of your one-time products. A subscription (recurring) offer's id 404s here with a hint — fetch it with `GET /subscriptions/{id}` instead.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The product",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteProduct",
        "summary": "Delete product",
        "description": "Delete one of your one-time products. Fires the `product.deleted` webhook. To delete a subscription (recurring) offer, use `DELETE /subscriptions/{id}`.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "Product deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Product deleted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/update/{id}": {
      "post": {
        "operationId": "updateProduct",
        "summary": "Update product",
        "description": "Partially update one of your one-time products — send only the fields to change (JSON or `multipart/form-data`). If the request sets `payWant: true` it must also send `hasProductPage: \"yes\"`. Fires the `product.updated` webhook. Subscription (recurring) offers cannot be updated with an API key.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Product fields to update — the same fields as `POST /products`, all optional",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Product name. Required when creating.",
                    "example": "Starter Kit"
                  },
                  "price": {
                    "type": "number",
                    "minimum": 0,
                    "default": 0,
                    "description": "Price in your account currency (`currency` on `GET /users/me`). Decimal amount in major units (e.g. `29.99`), not cents. `0` makes the product free.",
                    "example": 29
                  },
                  "compareAtPrice": {
                    "type": "number",
                    "minimum": 0,
                    "description": "Optional merchandising price shown crossed out next to `price`. Display only — it never affects what the buyer is charged.",
                    "example": 49
                  },
                  "description": {
                    "type": "string",
                    "description": "Product description"
                  },
                  "subtitle": {
                    "type": "string",
                    "description": "Product subtitle"
                  },
                  "published": {
                    "type": "boolean",
                    "default": true,
                    "description": "Whether the product is published (visible and purchasable). New products are always created published; send `false` on update to unpublish."
                  },
                  "slug": {
                    "type": "string",
                    "description": "URL slug for the product page"
                  },
                  "thumbnail": {
                    "type": "string",
                    "description": "Thumbnail image URL. When empty, the first of `images` is used."
                  },
                  "images": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Gallery image references (image ids). Upload new images with `multipart/form-data` (file field `images`)."
                  },
                  "hasProductPage": {
                    "type": "string",
                    "enum": [
                      "yes",
                      "no"
                    ],
                    "description": "Whether the product has its own product page. Must be `\"yes\"` whenever `payWant` is `true` in the same request — otherwise the request is rejected with `400`."
                  },
                  "payWant": {
                    "type": "boolean",
                    "default": false,
                    "description": "Enable pay-what-you-want pricing. Requires `hasProductPage: \"yes\"` in the same request."
                  },
                  "minPrice": {
                    "type": "number",
                    "minimum": 0,
                    "description": "Minimum price for pay-what-you-want (decimal, account currency)"
                  },
                  "maxPrice": {
                    "type": "number",
                    "minimum": 0,
                    "description": "Maximum price for pay-what-you-want (decimal, account currency)"
                  },
                  "refundPolicy": {
                    "type": "string",
                    "description": "Id of one of your refund policies (`_id` from `POST /refunds` / `GET /refunds`) — not policy text. Empty string for none.",
                    "example": "665f1c2a9b3e4a0012ab5566"
                  },
                  "showSales": {
                    "type": "boolean",
                    "default": true,
                    "description": "Show the sales count on the product page"
                  },
                  "showReviews": {
                    "type": "boolean",
                    "default": true,
                    "description": "Show reviews on the product page"
                  },
                  "hasFirstName": {
                    "type": "boolean",
                    "default": false,
                    "description": "Collect the buyer's first name at checkout"
                  },
                  "hasLastName": {
                    "type": "boolean",
                    "default": false,
                    "description": "Collect the buyer's last name at checkout"
                  },
                  "allowGifts": {
                    "type": "boolean",
                    "default": true,
                    "description": "Offer \"This is a gift\" at checkout: the buyer pays and the product is emailed to a recipient. Ignored for credit packs."
                  },
                  "productType": {
                    "type": "string",
                    "enum": [
                      "file",
                      "url",
                      "course",
                      "credits"
                    ],
                    "description": "Catalog type. Additive `credits` is prepaid balance with optional auto-refill. Absent on legacy products."
                  },
                  "availability": {
                    "type": "string",
                    "enum": [
                      "available",
                      "preorder",
                      "waitlist"
                    ],
                    "default": "available",
                    "description": "`available`: on sale as usual. `preorder`: buyers pay now and the content unlocks on `releaseAt` (one-time products only — not `credits`). `waitlist`: no checkout; the product page collects emails and the waitlist is emailed once when you switch the product to `available` or `preorder`.",
                    "example": "preorder"
                  },
                  "releaseAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Release date of a pre-order (ISO 8601). Required when `availability` is `preorder`; must be in the future and at most two years away. On that date the product switches to `available` and every pre-order buyer is emailed.",
                    "example": "2026-11-20T09:00:00.000Z"
                  },
                  "file": {
                    "type": "string",
                    "description": "URL of the downloadable file (file products). Upload a file with `multipart/form-data` (file field `file`)."
                  },
                  "url": {
                    "type": "string",
                    "description": "Link delivered to the buyer (url products)"
                  },
                  "ctaText": {
                    "type": "string",
                    "description": "Buy-button label key or text (defaults to `buyForPrice`)"
                  },
                  "checkoutText": {
                    "type": "string",
                    "description": "Extra text shown on the checkout page"
                  },
                  "thankYouText": {
                    "type": "string",
                    "description": "Text shown after a successful purchase"
                  },
                  "creditAutoRefill": {
                    "type": "object",
                    "description": "Seller auto-refill setting. `topOffCents` is integer cents (two decimal places). Enabling without a positive top-off is ignored.",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "topOffCents": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100000000
                      }
                    }
                  },
                  "licenseKeys": {
                    "type": "object",
                    "description": "License keys: with `enabled`, every sale of this product gets one key. `activationLimit` is the number of activations per key (null = unlimited).",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "activationLimit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 10000,
                        "nullable": true
                      }
                    }
                  }
                }
              },
              "example": {
                "price": 39,
                "compareAtPrice": 59
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated product",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/copy/{id}": {
      "post": {
        "operationId": "copyProduct",
        "summary": "Copy a catalog item",
        "description": "Duplicate one of your one-time products (or a subscription offer, whichever owns the id) as a NEW item. Send `{ \"testMode\": false }` to copy a test-mode offer into live mode (\"Copy to live\"); omit the body to copy within the same mode. The copy gets a deduplicated \"Name (Copy)\" name and starts unpublished when copied into live mode.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product or subscription offer id to copy"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "testMode": {
                    "type": "boolean",
                    "description": "Target mode of the copy. Omit to copy within the source's own mode."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The newly created copy (a product or a subscription offer)",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Product"
                    },
                    {
                      "$ref": "#/components/schemas/SubscriptionOffer"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/abandoned-checkout": {
      "patch": {
        "operationId": "setProductAbandonedCheckout",
        "summary": "Turn abandoned checkout recovery on or off",
        "description": "When on, a buyer who typed their email on this product's checkout and left without paying gets one recovery email with a link back to the checkout. On by default for every product. Turning it on needs the Growth plan or higher; turning it off is always allowed. Recovery also stops by itself while the account is below Growth, without touching this switch. `GET /checkout/abandoned/summary` shows what recovery would chase.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "`true` to send recovery emails for this product, `false` to stop"
                  }
                }
              },
              "example": {
                "enabled": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The product's new setting",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "abandonedCheckoutEnabled": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "abandonedCheckoutEnabled": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid product id, or `enabled` is not a boolean",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "enabled must be a boolean"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — turning abandoned checkout recovery on needs the Growth plan or higher (feature `abandonedCheckout`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "abandonedCheckout",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not one of your products",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/customers": {
      "get": {
        "operationId": "listCustomers",
        "summary": "List all customers",
        "description": "Retrieve all your customers in the key's mode (deduplicated by email, each with a computed `numberOfOrders` and a `products` array of `{ _id, name }` for every product or subscription they bought). Optionally filter by product (matches any of the buyer's purchases) and sort. The full list is returned — this endpoint is not paginated.",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "productId",
            "schema": {
              "type": "string"
            },
            "description": "Only customers who bought this product or subscription (any purchase, not just the first)"
          },
          {
            "in": "query",
            "name": "sortBy",
            "schema": {
              "type": "string",
              "enum": [
                "email",
                "country",
                "numberOfOrders",
                "createdAt"
              ],
              "default": "createdAt"
            },
            "description": "Field to sort by"
          },
          {
            "in": "query",
            "name": "sortOrder",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            },
            "description": "Sort direction"
          }
        ],
        "responses": {
          "200": {
            "description": "List of customers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CustomerListItem"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/customers/{id}": {
      "get": {
        "operationId": "getCustomer",
        "summary": "Get customer by ID",
        "description": "Retrieve a customer with their stats, one-time orders, subscriptions and reviews",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Customer id"
          }
        ],
        "responses": {
          "200": {
            "description": "Customer details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/orders": {
      "get": {
        "operationId": "listOrders",
        "summary": "List all orders",
        "description": "Retrieve your one-time product orders, newest first, paginated. Subscription (recurring) transactions are NOT included here — list every payment (one-time and recurring) with `GET /payments`, and subscribers with `GET /subscriptions/subscribers`.",
        "tags": [
          "Orders"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "startDate",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Only orders created on/after this date (ISO 8601). Applied only together with `endDate`; an unparseable date is a `400`."
          },
          {
            "in": "query",
            "name": "endDate",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Only orders created on/before this date (ISO 8601). Applied only together with `startDate`."
          },
          {
            "in": "query",
            "name": "productId",
            "schema": {
              "type": "string"
            },
            "description": "Only orders of this product"
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number"
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 20,
              "maximum": 100
            },
            "description": "Number of orders per page (maximum 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of orders",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orders": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Order"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unparseable `startDate` / `endDate`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Invalid startDate or endDate. Use an ISO 8601 date."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/orders/{id}": {
      "get": {
        "operationId": "getOrder",
        "summary": "Get order by ID",
        "description": "Retrieve one order by its sale id (the `_id` of an order from `GET /orders`, or of a payment from `GET /payments`) — NOT a product id. For a subscription membership and its full payment history, use `GET /orders/subscriptions/{id}` with the subscriber id.",
        "tags": [
          "Orders"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Order (sale) id"
          }
        ],
        "responses": {
          "200": {
            "description": "Order details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/orders/subscriptions/{id}": {
      "get": {
        "operationId": "getSubscriberOrderHistory",
        "summary": "Get a subscriber with payment history (via Orders)",
        "description": "Retrieve a single subscriber (SubscriptionCustomer) by id together with its subscription offer, customer, the payments-partner-sourced live `status`, and `payments` — every subscription `Sale` for this membership (the initial charge plus every renewal). `{id}` is the subscriber `_id`; a malformed or unknown id answers `404`.",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Subscriber id (the SubscriptionCustomer _id)"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscriber details with payment history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriptionCustomer": {
                      "$ref": "#/components/schemas/Subscriber"
                    },
                    "subscription": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/SubscriptionOffer"
                        }
                      ],
                      "nullable": true
                    },
                    "customer": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Customer"
                        }
                      ],
                      "nullable": true
                    },
                    "status": {
                      "type": "string",
                      "description": "Payments-partner-sourced live membership status",
                      "example": "active"
                    },
                    "payments": {
                      "type": "array",
                      "description": "Every subscription payment (initial + renewals) for this subscriber",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/payments": {
      "get": {
        "operationId": "listPayments",
        "summary": "List payments (unified ledger)",
        "description": "Retrieve a paginated ledger of ALL payments — one-time product purchases AND subscription payments (the initial charge plus every renewal). Filter by `type` to narrow to one_time or subscription. Every payment is a `Sale`. Scoped to the key's test/live mode.",
        "tags": [
          "Payments"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "type",
            "schema": {
              "type": "string",
              "enum": [
                "one_time",
                "subscription",
                "all"
              ],
              "default": "all"
            },
            "description": "Payment type: `one_time` (product purchases), `subscription` (subscription charges), or `all` (default)"
          },
          {
            "in": "query",
            "name": "startDate",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Filter payments from this date (ISO 8601)"
          },
          {
            "in": "query",
            "name": "endDate",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Filter payments until this date (ISO 8601)"
          },
          {
            "in": "query",
            "name": "productId",
            "schema": {
              "type": "string"
            },
            "description": "Filter payments by product id"
          },
          {
            "in": "query",
            "name": "subscriptionId",
            "schema": {
              "type": "string"
            },
            "description": "Filter payments by subscription offer id"
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number"
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 20,
              "maximum": 100
            },
            "description": "Number of payments per page (maximum 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of payments",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unparseable `startDate` / `endDate`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Invalid startDate or endDate. Use an ISO 8601 date."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/payments/{id}": {
      "get": {
        "operationId": "getPayment",
        "summary": "Get a payment by ID",
        "description": "Retrieve a single payment (a `Sale`). Resolves the related product OR subscription offer (subscription payments carry an empty productId), the customer, and a `paymentsPartner` block sourced from the stored payments partner payload. Pass `?live=true` to additionally fetch the authoritative live payment from the payments partner as `paymentsPartnerLive` (degrades gracefully on any error — never fails the request).",
        "tags": [
          "Payments"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Payment id (the Sale _id)"
          },
          {
            "in": "query",
            "name": "live",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "When true, fetch the authoritative live payment from the payments partner and include it as `paymentsPartnerLive`"
          }
        ],
        "responses": {
          "200": {
            "description": "Payment details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/reviews": {
      "get": {
        "operationId": "listReviews",
        "summary": "List product reviews",
        "description": "Retrieve all reviews across your products in the key's mode, newest first. Not paginated; filtering is not supported.",
        "tags": [
          "Reviews"
        ],
        "responses": {
          "200": {
            "description": "List of reviews",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Review"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/discounts": {
      "get": {
        "operationId": "listDiscounts",
        "summary": "List all discounts",
        "description": "Retrieve all your discount codes in the key's mode, each with `used` (number of sales that used it)",
        "tags": [
          "Discounts"
        ],
        "responses": {
          "200": {
            "description": "List of discounts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Discount"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createDiscount",
        "summary": "Create a discount",
        "description": "Create a new discount code. Codes are unique per account and mode (`400` if the code exists). `mainProductIds` must all be your own products or subscription offers in the key's mode. Set `expiration` to end a promotion automatically, or `active: false` to stop it now.",
        "tags": [
          "Discounts"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "code",
                  "value",
                  "mainProductIds"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Discount name (non-empty)"
                  },
                  "code": {
                    "type": "string",
                    "description": "Discount code - what customers enter at checkout",
                    "example": "LAUNCH20"
                  },
                  "value": {
                    "type": "number",
                    "exclusiveMinimum": true,
                    "minimum": 0,
                    "description": "Greater than 0. Percent off (`percentage`, at most 100) or a fixed decimal amount off in your account currency (`fixed`)",
                    "example": 20
                  },
                  "valueType": {
                    "type": "string",
                    "enum": [
                      "percentage",
                      "fixed"
                    ],
                    "default": "percentage",
                    "description": "Discount type (defaults to percentage)"
                  },
                  "mainProductIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ids of your own products / subscription offers (in the key's mode) the discount applies to. Required and non-empty on create."
                  },
                  "active": {
                    "type": "boolean",
                    "default": true,
                    "description": "Whether discount is active"
                  },
                  "expiration": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true,
                    "description": "When the code stops working (ISO 8601). `null` or empty for no expiry.",
                    "example": "2026-12-31T23:59:59.000Z"
                  },
                  "uniquePerRecipient": {
                    "type": "boolean",
                    "default": false,
                    "description": "Unique coupons (Growth plan and above; turning it on answers `403 FEATURE_LOCKED` otherwise). When true the discount is only redeemable through one-time codes issued per recipient — the `{{unique_coupon:<discountId>}}` merge tag in an email, or `POST /discounts/{id}/unique-codes` — and its own `code` becomes the code prefix (it no longer works at checkout by itself). Each code works once, only for the email it was issued to, until it expires."
                  },
                  "uniqueExpiryHours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 720,
                    "default": 48,
                    "description": "How long each one-time code lasts after it is issued, in hours"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created discount",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Discount"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/discounts/{id}": {
      "get": {
        "operationId": "getDiscount",
        "summary": "Get discount by ID",
        "description": "Retrieve one of your discounts",
        "tags": [
          "Discounts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Discount id"
          }
        ],
        "responses": {
          "200": {
            "description": "The discount",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Discount"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "updateDiscount",
        "summary": "Update discount",
        "description": "Partially update a discount — only the fields you send change. Send `expiration: null` to remove an expiry date. A new `code` must not clash with another of your discounts.",
        "tags": [
          "Discounts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Discount id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Discount name (non-empty)"
                  },
                  "code": {
                    "type": "string",
                    "description": "Discount code - what customers enter at checkout",
                    "example": "LAUNCH20"
                  },
                  "value": {
                    "type": "number",
                    "exclusiveMinimum": true,
                    "minimum": 0,
                    "description": "Greater than 0. Percent off (`percentage`, at most 100) or a fixed decimal amount off in your account currency (`fixed`)",
                    "example": 20
                  },
                  "valueType": {
                    "type": "string",
                    "enum": [
                      "percentage",
                      "fixed"
                    ],
                    "default": "percentage",
                    "description": "Discount type (defaults to percentage)"
                  },
                  "mainProductIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ids of your own products / subscription offers (in the key's mode) the discount applies to. Required and non-empty on create."
                  },
                  "active": {
                    "type": "boolean",
                    "default": true,
                    "description": "Whether discount is active"
                  },
                  "expiration": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true,
                    "description": "When the code stops working (ISO 8601). `null` or empty for no expiry.",
                    "example": "2026-12-31T23:59:59.000Z"
                  },
                  "uniquePerRecipient": {
                    "type": "boolean",
                    "default": false,
                    "description": "Unique coupons (Growth plan and above; turning it on answers `403 FEATURE_LOCKED` otherwise). When true the discount is only redeemable through one-time codes issued per recipient — the `{{unique_coupon:<discountId>}}` merge tag in an email, or `POST /discounts/{id}/unique-codes` — and its own `code` becomes the code prefix (it no longer works at checkout by itself). Each code works once, only for the email it was issued to, until it expires."
                  },
                  "uniqueExpiryHours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 720,
                    "default": 48,
                    "description": "How long each one-time code lasts after it is issued, in hours"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated discount",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Discount"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteDiscount",
        "summary": "Delete discount",
        "description": "Delete a discount code",
        "tags": [
          "Discounts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Discount id"
          }
        ],
        "responses": {
          "200": {
            "description": "Discount deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Discount removed"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/discounts/apply/{discountCode}/{productId}": {
      "get": {
        "operationId": "applyDiscountCode",
        "summary": "Validate and apply a discount code",
        "description": "PUBLIC (no API key) — used by the checkout. Validates a discount code against a product and returns the discount if it exists, is active, has not expired, and applies to the product. Returns 404 otherwise. One-time codes of a unique-per-recipient discount only work for the email they were sent to: pass it as `email` (the response then also carries `uniqueCode`).",
        "tags": [
          "Discounts"
        ],
        "security": [],
        "parameters": [
          {
            "in": "query",
            "name": "email",
            "schema": {
              "type": "string",
              "format": "email"
            },
            "description": "The buyer's email — required for one-time codes bound to a recipient"
          },
          {
            "in": "query",
            "name": "reservation",
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            },
            "description": "The `promotionToken` the checkout was given when it reserved the code — its own hold doesn't read as in use"
          },
          {
            "in": "path",
            "name": "discountCode",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The discount code the customer entered"
          },
          {
            "in": "path",
            "name": "productId",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Product ID to validate the code against"
          }
        ],
        "responses": {
          "200": {
            "description": "The discount is valid — the discount object is returned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Discount"
                }
              }
            }
          },
          "403": {
            "description": "A one-time code used with a different email than the one it was sent to (`code: UNIQUE_CODE_EMAIL_MISMATCH`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "UNIQUE_CODE_EMAIL_MISMATCH",
                  "code": "UNIQUE_CODE_EMAIL_MISMATCH",
                  "message": "This code was sent to a different email address. Use the email it was sent to."
                }
              }
            }
          },
          "404": {
            "description": "Discount not found, inactive, expired, or not applicable to this product",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Discount not found"
                }
              }
            }
          },
          "409": {
            "description": "The one-time code is reserved by another open checkout (30 minutes, or until that checkout pays or fails) — `code: UNIQUE_CODE_IN_USE`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "UNIQUE_CODE_IN_USE",
                  "code": "UNIQUE_CODE_IN_USE",
                  "message": "This code is already being used in another checkout. Finish that checkout, or try again in 30 minutes."
                }
              }
            }
          },
          "410": {
            "description": "A one-time code that was already used, expired, or whose seller's plan no longer includes unique coupons (`code: UNIQUE_CODE_REDEEMED` / `UNIQUE_CODE_EXPIRED` / `UNIQUE_CODE_UNAVAILABLE`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "UNIQUE_CODE_REDEEMED",
                  "code": "UNIQUE_CODE_REDEEMED",
                  "message": "This code has already been used."
                }
              }
            }
          },
          "422": {
            "description": "A one-time code bound to an email, looked up without `email` (`code: UNIQUE_CODE_EMAIL_REQUIRED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "UNIQUE_CODE_EMAIL_REQUIRED",
                  "code": "UNIQUE_CODE_EMAIL_REQUIRED",
                  "message": "This code is tied to the email address it was sent to. Enter that email to use it."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/discounts/check-product/{productId}": {
      "get": {
        "operationId": "checkProductDiscounts",
        "summary": "Check if a product has discounts",
        "description": "PUBLIC (no API key) — used by the checkout. Returns `true` if any discount applies to the given product, `false` otherwise.",
        "tags": [
          "Discounts"
        ],
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "productId",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Product ID to check"
          }
        ],
        "responses": {
          "200": {
            "description": "Boolean — whether any discount targets this product",
            "content": {
              "application/json": {
                "schema": {
                  "type": "boolean"
                },
                "example": true
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/upsells": {
      "get": {
        "operationId": "listUpsells",
        "summary": "List all upsells",
        "description": "Retrieve your upsells in the key's mode, plus `salesWithUpsells` — your sales that carry an upsell",
        "tags": [
          "Upsells"
        ],
        "responses": {
          "200": {
            "description": "Upsells and the sales that include one",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "upsells": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Upsell"
                      }
                    },
                    "salesWithUpsells": {
                      "type": "array",
                      "description": "Your sales in the key's mode that carry an upsell",
                      "items": {
                        "type": "object",
                        "properties": {
                          "_id": {
                            "type": "string"
                          },
                          "saleId": {
                            "type": "string"
                          },
                          "upsellId": {
                            "type": "string"
                          },
                          "upsellAmount": {
                            "type": "number",
                            "description": "Amount paid for the upsell. Decimal amount in major currency units (e.g. `29.99`), not cents."
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createUpsell",
        "summary": "Create an upsell",
        "description": "Create a new upsell offer. `upsellProductId` must be one of your products (`404` otherwise) and `mainProductIds` a non-empty list of your own products in the key's mode (`400` otherwise). `upsellPrice` must be 0 or more. Upsells are a Growth plan feature (`403 FEATURE_LOCKED` below Growth).",
        "tags": [
          "Upsells"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "mainProductIds",
                  "upsellProductId",
                  "upsellPrice"
                ],
                "properties": {
                  "mainProductIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Product IDs that trigger this upsell (required)"
                  },
                  "upsellProductId": {
                    "type": "string",
                    "description": "Product ID to offer as upsell (required)"
                  },
                  "upsellPrice": {
                    "type": "number",
                    "description": "Upsell price (required). Decimal amount in your account currency, not cents."
                  },
                  "compareAtPrice": {
                    "type": "number",
                    "description": "Optional crossed-out price shown next to `upsellPrice`"
                  },
                  "name": {
                    "type": "string",
                    "description": "Upsell name"
                  },
                  "offer": {
                    "type": "string",
                    "description": "Offer headline text"
                  },
                  "upsellDescription": {
                    "type": "string",
                    "description": "Upsell description shown to customer"
                  },
                  "primaryButtonText": {
                    "type": "string",
                    "description": "Primary button text (e.g., 'Yes, add this!')"
                  },
                  "secondaryButtonText": {
                    "type": "string",
                    "description": "Secondary button text (e.g., 'No thanks')"
                  },
                  "active": {
                    "type": "boolean",
                    "default": true,
                    "description": "Whether upsell is active"
                  },
                  "upsellBackground": {
                    "type": "string",
                    "description": "Card background color override (hex, e.g. '#fffbe6')"
                  },
                  "upsellBorder": {
                    "type": "string",
                    "description": "Card border color override (hex, e.g. '#d9c98f')"
                  },
                  "upsellHeadline": {
                    "type": "string",
                    "description": "Headline text color override (hex, e.g. '#14532d')"
                  },
                  "upsellDescriptionColor": {
                    "type": "string",
                    "description": "Description text color override (hex)"
                  },
                  "upsellCheckboxColor": {
                    "type": "string",
                    "description": "Checkbox accent color override (hex)"
                  },
                  "upsellCheckboxBackground": {
                    "type": "string",
                    "description": "Unchecked checkbox box fill color override (hex)"
                  },
                  "upsellHeadingFont": {
                    "type": "string",
                    "description": "Title font override (font key, e.g. 'poppins')"
                  },
                  "upsellBodyFont": {
                    "type": "string",
                    "description": "Body font override (font key, e.g. 'serif')"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created upsell",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upsell"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — creating an upsell needs the Growth plan or higher (feature `upsells`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "upsells",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "`upsellProductId` is not one of your products",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Upsell product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/upsells/{id}": {
      "get": {
        "operationId": "getUpsell",
        "summary": "Get upsell by ID",
        "description": "Retrieve one of your upsells",
        "tags": [
          "Upsells"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Upsell id"
          }
        ],
        "responses": {
          "200": {
            "description": "The upsell",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upsell"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "updateUpsell",
        "summary": "Update upsell",
        "description": "Update an existing upsell. `upsellProductId`, `upsellPrice` and `mainProductIds` change only when sent (and are validated like on create); every other listed field is written as sent — omitted ones are cleared — so send every text, style and `active` field you want to keep. `PUT /upsells/{id}` is an alias of this endpoint.",
        "tags": [
          "Upsells"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Upsell id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "offer": {
                    "type": "string"
                  },
                  "mainProductIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Product IDs that trigger this upsell"
                  },
                  "upsellProductId": {
                    "type": "string"
                  },
                  "upsellPrice": {
                    "type": "number"
                  },
                  "compareAtPrice": {
                    "type": "number"
                  },
                  "upsellDescription": {
                    "type": "string"
                  },
                  "primaryButtonText": {
                    "type": "string"
                  },
                  "secondaryButtonText": {
                    "type": "string"
                  },
                  "active": {
                    "type": "boolean"
                  },
                  "upsellBackground": {
                    "type": "string"
                  },
                  "upsellBorder": {
                    "type": "string"
                  },
                  "upsellHeadline": {
                    "type": "string"
                  },
                  "upsellDescriptionColor": {
                    "type": "string"
                  },
                  "upsellCheckboxColor": {
                    "type": "string"
                  },
                  "upsellCheckboxBackground": {
                    "type": "string"
                  },
                  "upsellHeadingFont": {
                    "type": "string"
                  },
                  "upsellBodyFont": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated upsell",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upsell"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — saving an upsell that stays live (anything but `active: false`) needs the Growth plan or higher (feature `upsells`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "upsells",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteUpsell",
        "summary": "Delete upsell",
        "description": "Delete an upsell offer",
        "tags": [
          "Upsells"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Upsell id"
          }
        ],
        "responses": {
          "200": {
            "description": "Upsell deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Upsell removed"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscriptions": {
      "get": {
        "operationId": "listSubscriptionOffers",
        "summary": "List all subscription offers",
        "description": "Retrieve all of your subscription OFFERS (the recurring products you sell) in the key's mode. To list your subscribers (buyers) instead, use `GET /subscriptions/subscribers`.",
        "tags": [
          "Subscriptions"
        ],
        "responses": {
          "200": {
            "description": "List of subscription offers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SubscriptionOffer"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createSubscriptionOffer",
        "summary": "Create a subscription offer",
        "description": "Create a recurring subscription offer. Accepts JSON for API clients and multipart/form-data when uploading files or images. Pricing is charged in USD while multi-currency is disabled.",
        "tags": [
          "Subscriptions"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSubscriptionOfferRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CreateSubscriptionOfferRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription offer created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionOffer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscriptions/subscribers": {
      "get": {
        "operationId": "listSubscribers",
        "summary": "List subscribers",
        "description": "Retrieve a paginated list of your subscribers (buyers with a membership in one of your subscription offers). Each subscriber carries a payments-partner-sourced live `status`, `cancelAtPeriodEnd`, the membership id (`membershipId`), the `portalUrl` to hand to the buyer, and the joined subscription offer. Scoped to the key's test/live mode.",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "incomplete",
                "incomplete_expired",
                "trialing",
                "active",
                "past_due",
                "canceled",
                "unpaid",
                "succeeded",
                "refunded",
                "paused"
              ]
            },
            "description": "Filter subscribers by their payments-partner-sourced status"
          },
          {
            "in": "query",
            "name": "buyerEmail",
            "schema": {
              "type": "string"
            },
            "description": "Filter subscribers by buyer email"
          },
          {
            "in": "query",
            "name": "email",
            "schema": {
              "type": "string"
            },
            "description": "Alias for buyerEmail"
          },
          {
            "in": "query",
            "name": "subscriptionId",
            "schema": {
              "type": "string"
            },
            "description": "Filter subscribers by subscription offer id"
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number"
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 20,
              "maximum": 100
            },
            "description": "Number of subscribers per page (maximum 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of subscribers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscribers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Subscriber"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`status` is not a known status, or `buyerEmail` / `email` / `subscriptionId` is not a single valid value",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Invalid buyerEmail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscriptions/subscribers/{id}": {
      "get": {
        "operationId": "getSubscriber",
        "summary": "Get a subscriber with payment history",
        "description": "Retrieve a single subscriber, its subscription offer, the customer record, the payments-partner-sourced live `status`, and its full payment history (`payments` = every subscription `Sale` — the initial charge plus every renewal). Pass `?live=true` to additionally fetch the authoritative membership status from the payments partner as `membership` (degrades gracefully to the stored status on any payments partner error — never fails the request).",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Subscriber id (the SubscriptionCustomer _id)"
          },
          {
            "in": "query",
            "name": "live",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "When true, fetch authoritative live membership status from the payments partner and include it as `membership`"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscriber details with payment history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriber": {
                      "$ref": "#/components/schemas/Subscriber"
                    },
                    "subscription": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/SubscriptionOffer"
                        }
                      ],
                      "nullable": true
                    },
                    "customer": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Customer"
                        }
                      ],
                      "nullable": true
                    },
                    "status": {
                      "type": "string",
                      "description": "Payments-partner-sourced live membership status",
                      "example": "active"
                    },
                    "payments": {
                      "type": "array",
                      "description": "Every subscription payment (initial + renewals) for this subscriber",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    },
                    "membership": {
                      "type": "object",
                      "nullable": true,
                      "description": "Only present when `?live=true`: the authoritative live membership from the payments partner, or null if the live lookup failed"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscriptions/{id}": {
      "get": {
        "operationId": "getSubscriptionOffer",
        "summary": "Get subscription offer by ID",
        "description": "Retrieve one of your subscription OFFERS in the key's mode.",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Subscription offer id"
          }
        ],
        "responses": {
          "200": {
            "description": "The subscription offer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionOffer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteSubscriptionOffer",
        "summary": "Delete a subscription offer",
        "description": "Delete one of your subscription OFFERS in the key's mode. This removes the offer from your catalog; it does not cancel existing subscribers — use `POST /subscriptions/{id}/cancel` with a subscriber id for that.",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Subscription offer id"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription offer deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "id": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Subscription removed",
                  "id": "665f1c2a9b3e4a0012ab7788"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscriptions/{id}/cancel": {
      "post": {
        "operationId": "cancelSubscription",
        "summary": "Cancel a subscriber's subscription",
        "description": "Schedules the subscriber's membership to cancel at the end of the current billing period (same as the subscriber portal). Access continues until then; `cancelAtPeriodEnd` becomes true. `{id}` is the subscriber `_id` or membership id (`mem_…`) — not a `sub_…` id.",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "mem_XXXXXXXXXXXXX"
            },
            "description": "The subscriber's `_id` (SubscriptionCustomer) or its membership id (`mem_…`), as returned by `GET /subscriptions/subscribers`"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription canceled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "status": {
                      "type": "string",
                      "description": "Subscriber status after the action"
                    },
                    "cancelAtPeriodEnd": {
                      "type": "boolean"
                    },
                    "manageUrl": {
                      "type": "string",
                      "description": "Buyer self-service management URL"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscriptions/{id}/pause": {
      "post": {
        "operationId": "pauseSubscription",
        "summary": "Pause a subscriber's subscription",
        "description": "Pauses payment collection on the subscriber's membership. The buyer keeps access and is not charged until `/resume`. If the payments partner refuses, nothing changes (502). `{id}` is the subscriber `_id` or membership id (`mem_…`).",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "mem_XXXXXXXXXXXXX"
            },
            "description": "The subscriber's `_id` (SubscriptionCustomer) or its membership id (`mem_…`), as returned by `GET /subscriptions/subscribers`"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription paused successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "status": {
                      "type": "string",
                      "description": "Subscriber status after the action"
                    },
                    "paused": {
                      "type": "boolean"
                    },
                    "cancelAtPeriodEnd": {
                      "type": "boolean"
                    },
                    "manageUrl": {
                      "type": "string",
                      "description": "Buyer self-service management URL"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The subscriber has no membership on record, so collection cannot be paused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "502": {
            "description": "The payments partner refused the pause; nothing was changed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/subscriptions/{id}/resume": {
      "post": {
        "operationId": "resumeSubscription",
        "summary": "Resume a paused subscription",
        "description": "Restarts payment collection on a paused membership, or otherwise reverses a pending cancel-at-period-end (same as the subscriber portal). `{id}` is the subscriber `_id` or membership id (`mem_…`).",
        "tags": [
          "Subscriptions"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "mem_XXXXXXXXXXXXX"
            },
            "description": "The subscriber's `_id` (SubscriptionCustomer) or its membership id (`mem_…`), as returned by `GET /subscriptions/subscribers`"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription resumed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "status": {
                      "type": "string",
                      "description": "Subscriber status after the action"
                    },
                    "cancelAtPeriodEnd": {
                      "type": "boolean"
                    },
                    "manageUrl": {
                      "type": "string",
                      "description": "Buyer self-service management URL"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "operationId": "listWebhooks",
        "summary": "List all webhooks",
        "description": "Retrieve your webhook endpoints in the key's mode, newest first. Secrets are omitted.",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Your webhook endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createWebhook",
        "summary": "Create a webhook",
        "description": "Register an endpoint that receives the selected events. The endpoint is created in the key's mode and only receives events from that mode. The response carries the signing `secret` — store it (you can also read it with `GET /webhooks/{id}` or replace it with `POST /webhooks/{id}/rotate-secret`). New webhooks start active; use `PATCH /webhooks/{id}` to disable one.",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Endpoint URL. Must be https:// and publicly reachable (private / internal addresses are rejected)."
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "enum": [
                        "order.completed",
                        "order.refunded",
                        "product.created",
                        "product.updated",
                        "product.deleted",
                        "review.created",
                        "customer.created",
                        "customer.subscription.created",
                        "customer.subscription.updated",
                        "customer.subscription.deleted",
                        "customer.subscription.pause",
                        "customer.subscription.resumed",
                        "customer.subscription.trial_will_end",
                        "invoice.created",
                        "invoice.upcoming",
                        "invoice.payment_succeeded",
                        "invoice.payment_failed",
                        "payment_intent.succeeded",
                        "payment_intent.payment_failed",
                        "subscription.payment_failed",
                        "subscription.recovered",
                        "subscription.saved",
                        "credit.granted",
                        "credit.balance_changed",
                        "credit.auto_refill.succeeded",
                        "credit.auto_refill.failed",
                        "credit.consent.enabled",
                        "credit.consent.disabled",
                        "usage.recorded",
                        "usage.period_closed",
                        "order.post_purchase_accepted",
                        "funnel.lead",
                        "installment.paid",
                        "installment.failed",
                        "ab_test.completed",
                        "affiliate.tier_changed",
                        "loyalty.credit_redeemed",
                        "split.created",
                        "split.paid",
                        "course.completed",
                        "certificate.issued",
                        "testimonial.submitted",
                        "member.joined",
                        "member.left"
                      ]
                    },
                    "description": "Events to subscribe to — a non-empty list of known event types (required)"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Optional description (at most 500 characters)"
                  }
                }
              },
              "example": {
                "url": "https://acme.com/webhooks/pocketsflow",
                "events": [
                  "order.completed",
                  "order.refunded"
                ],
                "description": "Fulfilment"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Webhook id (24-character hex MongoDB ObjectId)",
                      "example": "665f1c2a9b3e4a0012ab34cd"
                    },
                    "url": {
                      "type": "string"
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "description": {
                      "type": "string"
                    },
                    "secret": {
                      "type": "string",
                      "description": "Signing secret for verifying deliveries"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing, non-https or disallowed `url` (`{ error }`), an empty or unknown `events` list (`{ error, invalidEvents }`), or a `description` over 500 characters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Unknown webhook events",
                  "invalidEvents": [
                    "order.created"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/webhooks/{id}": {
      "get": {
        "operationId": "getWebhook",
        "summary": "Get webhook by ID",
        "description": "Retrieve one of your webhook endpoints, including its signing `secret`",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Webhook id"
          }
        ],
        "responses": {
          "200": {
            "description": "The webhook",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/Webhook"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateWebhook",
        "summary": "Update webhook",
        "description": "Update an endpoint's `url`, `events`, `description`, or `active` flag. Only the fields you send change; they are validated like on create. The response does not include the `secret`.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Webhook id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "New https:// endpoint URL"
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "enum": [
                        "order.completed",
                        "order.refunded",
                        "product.created",
                        "product.updated",
                        "product.deleted",
                        "review.created",
                        "customer.created",
                        "customer.subscription.created",
                        "customer.subscription.updated",
                        "customer.subscription.deleted",
                        "customer.subscription.pause",
                        "customer.subscription.resumed",
                        "customer.subscription.trial_will_end",
                        "invoice.created",
                        "invoice.upcoming",
                        "invoice.payment_succeeded",
                        "invoice.payment_failed",
                        "payment_intent.succeeded",
                        "payment_intent.payment_failed",
                        "subscription.payment_failed",
                        "subscription.recovered",
                        "subscription.saved",
                        "credit.granted",
                        "credit.balance_changed",
                        "credit.auto_refill.succeeded",
                        "credit.auto_refill.failed",
                        "credit.consent.enabled",
                        "credit.consent.disabled",
                        "usage.recorded",
                        "usage.period_closed",
                        "order.post_purchase_accepted",
                        "funnel.lead",
                        "installment.paid",
                        "installment.failed",
                        "ab_test.completed",
                        "affiliate.tier_changed",
                        "loyalty.credit_redeemed",
                        "split.created",
                        "split.paid",
                        "course.completed",
                        "certificate.issued",
                        "testimonial.submitted",
                        "member.joined",
                        "member.left"
                      ]
                    },
                    "description": "Replaces the subscribed events (non-empty)"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "active": {
                    "type": "boolean",
                    "description": "`false` pauses deliveries"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated webhook (without `secret`)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/Webhook"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid id, `url`, `events`, `description`, or a non-boolean `active`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "active must be a boolean"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Delete webhook",
        "description": "Delete one of your webhook endpoints in the key's mode.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Webhook id"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Webhook deleted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No webhook of yours with this id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Webhook not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/webhooks/{id}/rotate-secret": {
      "post": {
        "operationId": "rotateWebhookSecret",
        "summary": "Rotate a webhook's signing secret",
        "description": "Replace the endpoint's signing secret and return the new one. Deliveries are signed with the new secret immediately — update your verifier first.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Webhook id"
          }
        ],
        "responses": {
          "200": {
            "description": "The new secret",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Webhook id (24-character hex MongoDB ObjectId)",
                      "example": "665f1c2a9b3e4a0012ab34cd"
                    },
                    "secret": {
                      "type": "string",
                      "description": "The new signing secret"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No webhook of yours with this id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Webhook not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "operationId": "testWebhook",
        "summary": "Test webhook",
        "description": "Send a sample event to this endpoint only — its first subscribed event type, with a sample `order.completed`-style payload and `test: true` in the body — and return the delivery outcome. The attempt is recorded in the delivery log.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Webhook id"
          }
        ],
        "responses": {
          "200": {
            "description": "The test delivery outcome (also `200` when your endpoint failed — check `delivered`)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "enum": [
                        "Test webhook sent",
                        "Test webhook delivery failed"
                      ]
                    },
                    "event": {
                      "type": "string",
                      "example": "order.completed"
                    },
                    "eventId": {
                      "type": "string",
                      "example": "evt_6a43ce05fae6c4f0cb5a2193"
                    },
                    "deliveryId": {
                      "type": "string",
                      "description": "Id of the delivery-log entry (`GET /delivered-webhooks/{id}`)"
                    },
                    "delivered": {
                      "type": "boolean",
                      "description": "Whether your endpoint answered 2xx"
                    },
                    "status": {
                      "type": "integer",
                      "description": "HTTP status your endpoint returned, if any"
                    },
                    "durationMs": {
                      "type": "integer"
                    },
                    "error": {
                      "type": "string",
                      "description": "Failure reason, if any"
                    },
                    "results": {
                      "type": "array",
                      "description": "Legacy shape, kept for older clients: `[{ status: \"fulfilled\", value: { webhookId, success, status, durationMs, error } }]`",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No webhook of yours with this id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Webhook not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/delivered-webhooks": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "summary": "List webhook deliveries",
        "description": "The delivery log: every webhook delivery attempt in the key's mode (including tests and redeliveries), newest first, paginated. Entries are kept for 30 days.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number"
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 20,
              "maximum": 100
            },
            "description": "Number of deliveries per page (maximum 100)"
          },
          {
            "in": "query",
            "name": "webhookId",
            "schema": {
              "type": "string"
            },
            "description": "Only deliveries to this endpoint"
          },
          {
            "in": "query",
            "name": "event",
            "schema": {
              "type": "string",
              "enum": [
                "order.completed",
                "order.refunded",
                "product.created",
                "product.updated",
                "product.deleted",
                "review.created",
                "customer.created",
                "customer.subscription.created",
                "customer.subscription.updated",
                "customer.subscription.deleted",
                "customer.subscription.pause",
                "customer.subscription.resumed",
                "customer.subscription.trial_will_end",
                "invoice.created",
                "invoice.upcoming",
                "invoice.payment_succeeded",
                "invoice.payment_failed",
                "payment_intent.succeeded",
                "payment_intent.payment_failed",
                "subscription.payment_failed",
                "subscription.recovered",
                "subscription.saved",
                "credit.granted",
                "credit.balance_changed",
                "credit.auto_refill.succeeded",
                "credit.auto_refill.failed",
                "credit.consent.enabled",
                "credit.consent.disabled",
                "usage.recorded",
                "usage.period_closed",
                "order.post_purchase_accepted",
                "funnel.lead",
                "installment.paid",
                "installment.failed",
                "ab_test.completed",
                "affiliate.tier_changed",
                "loyalty.credit_redeemed",
                "split.created",
                "split.paid",
                "course.completed",
                "certificate.issued",
                "testimonial.submitted",
                "member.joined",
                "member.left",
                "test.event"
              ]
            },
            "description": "Only deliveries of this event type"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of deliveries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveredWebhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Non-positive `page` / `pageSize`, a malformed `webhookId`, or an unknown `event`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Unknown event"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/delivered-webhooks/{id}": {
      "get": {
        "operationId": "getWebhookDelivery",
        "summary": "Get a webhook delivery",
        "description": "Retrieve one delivery-log entry, including the exact body that was sent.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Delivery id"
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveredWebhook": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No delivery of yours with this id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Delivered webhook not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/delivered-webhooks/{id}/redeliver": {
      "post": {
        "operationId": "redeliverWebhook",
        "summary": "Redeliver a webhook",
        "description": "Send a logged delivery's stored body to its endpoint again (same event id, freshly signed) and record the new attempt. Use it to replay events your endpoint missed.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Delivery id"
          }
        ],
        "responses": {
          "200": {
            "description": "The redelivery outcome (also `200` when your endpoint failed — check `delivered`)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "enum": [
                        "Webhook redelivered",
                        "Webhook redelivery failed"
                      ]
                    },
                    "delivered": {
                      "type": "boolean"
                    },
                    "deliveryId": {
                      "type": "string",
                      "description": "Id of the new delivery-log entry"
                    },
                    "eventId": {
                      "type": "string"
                    },
                    "event": {
                      "type": "string"
                    },
                    "attempt": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "integer",
                      "description": "HTTP status your endpoint returned, if any"
                    },
                    "durationMs": {
                      "type": "integer"
                    },
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "The delivery or its webhook endpoint no longer exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Delivered webhook not found"
                }
              }
            }
          },
          "422": {
            "description": "The stored body cannot be redelivered",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Stored payload cannot be redelivered"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/users/me": {
      "get": {
        "operationId": "getCurrentUser",
        "summary": "Get current user information",
        "description": "Retrieve the authenticated account: ids, store name, subdomain, currency, the key's test/live mode, and whether it can sell. Use `subdomain` to build storefront and product URLs.",
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "description": "The account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The account is suspended",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This account has been suspended.",
                  "code": "ACCOUNT_BLOCKED"
                }
              }
            }
          },
          "404": {
            "description": "No account exists for these credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "User not found"
                }
              }
            }
          },
          "410": {
            "description": "The account was deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This account was deleted.",
                  "code": "ACCOUNT_DELETED"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/users/entitlements": {
      "get": {
        "operationId": "getEntitlements",
        "summary": "Get your plan and what it unlocks",
        "description": "The account's active plan, billing interval, platform fee rate, team seats, and one entry per feature key saying whether it is available right now and which plan it needs. Call it before using a paid feature (custom domains, premium templates, ad pixels, upsells, abandoned checkout recovery, affiliates…): a write that turns on a feature with `allowed: false` answers `403 FEATURE_LOCKED`. Plans are bought in the dashboard (https://app.pocketsflow.com/profile/billing), never through the API.",
        "tags": [
          "Plans"
        ],
        "responses": {
          "200": {
            "description": "Your entitlements",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Entitlements"
                },
                "example": {
                  "plan": "tier1",
                  "planName": "Starter",
                  "billingInterval": "month",
                  "feeRate": 0.02,
                  "seats": 1,
                  "enforced": true,
                  "features": {
                    "customDomains": {
                      "allowed": true,
                      "requiredPlan": "tier1",
                      "requiredPlanName": "Starter",
                      "reason": "plan",
                      "grandfatheredUntil": null
                    },
                    "upsells": {
                      "allowed": false,
                      "requiredPlan": "tier2",
                      "requiredPlanName": "Growth",
                      "reason": "locked",
                      "grandfatheredUntil": null
                    },
                    "abandonedCheckout": {
                      "allowed": false,
                      "requiredPlan": "tier2",
                      "requiredPlanName": "Growth",
                      "reason": "locked",
                      "grandfatheredUntil": null
                    },
                    "lowerFee": {
                      "allowed": false,
                      "requiredPlan": "volume",
                      "requiredPlanName": "Volume",
                      "reason": "locked",
                      "grandfatheredUntil": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No account exists for these credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "User not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/users/billing-tiers": {
      "get": {
        "operationId": "listPlans",
        "summary": "List plans",
        "description": "PUBLIC — no API key needed. The plans catalogue: monthly and yearly price (yearly = two months free), platform fee rate (2%; 1% on Volume, 0.5% on Volume Max), newsletter caps, team seats and the feature keys each plan includes. Enterprise is contact-us and not listed. Upgrading happens in the dashboard (https://app.pocketsflow.com/profile/billing).",
        "tags": [
          "Plans"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Every self-serve plan, cheapest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tiers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Plan"
                      }
                    }
                  }
                },
                "example": {
                  "tiers": [
                    {
                      "plan": "free",
                      "name": "Free",
                      "minSubscribers": 0,
                      "maxSubscribers": 1000,
                      "maxSendsPerMonth": 4000,
                      "pricePerMonth": 0,
                      "pricePerYear": 0,
                      "feeRate": 0.02,
                      "seats": 1,
                      "maxUploadMb": 10,
                      "features": []
                    },
                    {
                      "plan": "tier2",
                      "name": "Growth",
                      "minSubscribers": 0,
                      "maxSubscribers": 10000,
                      "maxSendsPerMonth": 40000,
                      "pricePerMonth": 49,
                      "pricePerYear": 490,
                      "feeRate": 0.02,
                      "seats": 5,
                      "isPopular": true,
                      "maxUploadMb": 100,
                      "features": [
                        "removeBadge",
                        "customDomains",
                        "premiumTemplates",
                        "adPixels"
                      ]
                    },
                    {
                      "plan": "volume",
                      "name": "Volume",
                      "minSubscribers": 0,
                      "maxSubscribers": 75000,
                      "maxSendsPerMonth": 300000,
                      "pricePerMonth": 500,
                      "pricePerYear": 5000,
                      "feeRate": 0.01,
                      "seats": null,
                      "maxUploadMb": 250,
                      "features": [
                        "removeBadge",
                        "customDomains",
                        "premiumTemplates",
                        "adPixels"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/users/update": {
      "post": {
        "operationId": "updateTrackingPixels",
        "summary": "Update ad tracking pixels",
        "description": "Set or remove the ad-platform ids loaded on your storefront, product, checkout and order-confirmation pages. Send only the fields you change; an empty string or `null` removes one. Each id must match its vendor's format (`400` with the offending `field` otherwise). Adding or changing a pixel needs the Starter plan or higher; removing one, or re-sending the saved value, never does. Returns the updated account. (This endpoint also takes account settings the dashboard uses; only the fields below are part of the public contract.)",
        "tags": [
          "Account"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "facebookPixel": {
                    "type": "string",
                    "nullable": true,
                    "description": "Meta Pixel ID: must be 15 or 16 digits. Empty string or null removes it."
                  },
                  "googlePixel": {
                    "type": "string",
                    "nullable": true,
                    "description": "Google tag ID: must look like G-XXXXXXX, AW-XXXXXXXXX or GT-XXXXXXX. Empty string or null removes it."
                  },
                  "googleTagManager": {
                    "type": "string",
                    "nullable": true,
                    "description": "Google Tag Manager container ID: must look like GTM-XXXXXXX. Empty string or null removes it."
                  },
                  "tiktokPixel": {
                    "type": "string",
                    "nullable": true,
                    "description": "TikTok Pixel ID: must be 16 to 24 letters and numbers (usually 20). Empty string or null removes it."
                  },
                  "pinterestTag": {
                    "type": "string",
                    "nullable": true,
                    "description": "Pinterest Tag ID: must be 10 to 16 digits (usually 13). Empty string or null removes it."
                  },
                  "snapchatPixel": {
                    "type": "string",
                    "nullable": true,
                    "description": "Snap Pixel ID: must be a UUID like 1a2b3c4d-1a2b-1a2b-1a2b-1a2b3c4d5e6f. Empty string or null removes it."
                  },
                  "twitterPixel": {
                    "type": "string",
                    "nullable": true,
                    "description": "X Pixel ID: must be 4 to 8 letters and numbers, like o1abc. Empty string or null removes it."
                  },
                  "twitterPurchaseEventId": {
                    "type": "string",
                    "nullable": true,
                    "description": "X Purchase event ID: must look like tw-o1abc-o2def. Empty string or null removes it."
                  },
                  "linkedinPartnerId": {
                    "type": "string",
                    "nullable": true,
                    "description": "LinkedIn Partner ID: must be 4 to 10 digits. Empty string or null removes it."
                  },
                  "linkedinConversionId": {
                    "type": "string",
                    "nullable": true,
                    "description": "LinkedIn conversion ID: must be 4 to 12 digits. Empty string or null removes it."
                  },
                  "redditPixel": {
                    "type": "string",
                    "nullable": true,
                    "description": "Reddit Pixel ID: must look like a2_abc123def456. Empty string or null removes it."
                  },
                  "microsoftUetTag": {
                    "type": "string",
                    "nullable": true,
                    "description": "Microsoft Advertising UET tag ID: must be 5 to 12 digits. Empty string or null removes it."
                  }
                }
              },
              "example": {
                "facebookPixel": "123456789012345",
                "googlePixel": "G-ABC1234567",
                "tiktokPixel": ""
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          },
          "400": {
            "description": "A pixel id does not match its vendor's format (`field` names it)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid tracking pixel",
                  "message": "Meta Pixel ID must be 15 or 16 digits."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — adding or changing an ad pixel needs the Starter plan or higher (feature `adPixels`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "adPixels",
                    "currentPlan": "free",
                    "requiredPlan": "tier1",
                    "requiredPlanName": "Starter",
                    "requiredPlanPrice": 15,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/users/weekly-report/preview": {
      "get": {
        "operationId": "getWeeklyReport",
        "summary": "Get your weekly growth report",
        "description": "Last week's growth report — the one emailed every Monday at 08:00 in your timezone (UTC when unknown): revenue and paid orders in your account currency with the change vs the week before, unique visitors, conversion rate, best-selling product, top traffic source, and one recommended next step with a dashboard link. Figures are computed exactly like the dashboard's analytics for the same Monday-to-Monday range, live data only. Also returns the rendered email (`subject`, `html`, `text`). Free on every plan.",
        "tags": [
          "Weekly report"
        ],
        "responses": {
          "200": {
            "description": "Last week's report and the rendered email",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subject": {
                      "type": "string",
                      "example": "Your week: $1,240 from 31 sales (+18%)"
                    },
                    "html": {
                      "type": "string",
                      "description": "The email's HTML."
                    },
                    "text": {
                      "type": "string",
                      "description": "The email's plain-text part."
                    },
                    "report": {
                      "type": "object",
                      "properties": {
                        "currency": {
                          "type": "string",
                          "description": "ISO code every money figure is in (your account currency).",
                          "example": "USD"
                        },
                        "timeZone": {
                          "type": "string",
                          "example": "Europe/Paris"
                        },
                        "weekKey": {
                          "type": "string",
                          "description": "ISO week the report covers.",
                          "example": "2026-W42"
                        },
                        "weekLabel": {
                          "type": "string",
                          "example": "Oct 12 – Oct 18"
                        },
                        "periodStart": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "periodEnd": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "current": {
                          "type": "object",
                          "description": "Last week.",
                          "properties": {
                            "revenue": {
                              "type": "number",
                              "description": "Paid revenue. Decimal amount in major currency units (e.g. `29.99`), not cents.",
                              "example": 1240.5
                            },
                            "orders": {
                              "type": "integer",
                              "example": 31
                            },
                            "visitors": {
                              "type": "integer",
                              "example": 1204
                            },
                            "conversionRate": {
                              "type": "number",
                              "description": "Percent: (paid orders + new subscribers) / visitors.",
                              "example": 2.6
                            }
                          }
                        },
                        "previous": {
                          "type": "object",
                          "description": "The week before, same fields as `current`.",
                          "properties": {
                            "revenue": {
                              "type": "number",
                              "description": "Paid revenue. Decimal amount in major currency units (e.g. `29.99`), not cents.",
                              "example": 1050
                            },
                            "orders": {
                              "type": "integer"
                            },
                            "visitors": {
                              "type": "integer"
                            },
                            "conversionRate": {
                              "type": "number"
                            }
                          }
                        },
                        "revenueChangePct": {
                          "type": "number",
                          "nullable": true,
                          "description": "Revenue change vs the week before, rounded percent. Null when that week had no revenue.",
                          "example": 18
                        },
                        "bestSeller": {
                          "type": "object",
                          "nullable": true,
                          "properties": {
                            "id": {
                              "type": "string",
                              "description": "Product id (24-character hex MongoDB ObjectId)",
                              "example": "665f1c2a9b3e4a0012ab34cd"
                            },
                            "name": {
                              "type": "string"
                            },
                            "orders": {
                              "type": "integer"
                            },
                            "revenue": {
                              "type": "number",
                              "description": "Revenue from this product last week. Decimal amount in major currency units (e.g. `29.99`), not cents."
                            }
                          }
                        },
                        "topSource": {
                          "type": "object",
                          "nullable": true,
                          "properties": {
                            "source": {
                              "type": "string",
                              "description": "Referrer host or utm_source; `direct` for none.",
                              "example": "google.com"
                            },
                            "label": {
                              "type": "string"
                            },
                            "visitors": {
                              "type": "integer"
                            }
                          }
                        },
                        "nextStep": {
                          "type": "object",
                          "description": "The one recommended next step (first matching rule wins).",
                          "properties": {
                            "id": {
                              "type": "string",
                              "enum": [
                                "publish-first-product",
                                "convert-visits",
                                "order-bump",
                                "recover-checkouts",
                                "send-newsletter",
                                "grow-reach"
                              ]
                            },
                            "title": {
                              "type": "string"
                            },
                            "body": {
                              "type": "string"
                            },
                            "cta": {
                              "type": "string"
                            },
                            "path": {
                              "type": "string",
                              "description": "Dashboard path the email button opens (on app.pocketsflow.com).",
                              "example": "/upsells/new?unlock=upsells"
                            },
                            "feature": {
                              "type": "string",
                              "enum": [
                                "upsells",
                                "abandonedCheckout",
                                "affiliates"
                              ],
                              "description": "The paid feature the step leads to, if any."
                            },
                            "locked": {
                              "type": "boolean",
                              "description": "True when your plan does not include `feature` yet."
                            },
                            "requiredPlanName": {
                              "type": "string",
                              "example": "Growth"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No account exists for these credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "User not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/users/weekly-report/settings": {
      "get": {
        "operationId": "getWeeklyReportSettings",
        "summary": "Get weekly report settings",
        "description": "Whether the Monday weekly growth report email is on (it is by default), the timezone it follows and when the next one is due.",
        "tags": [
          "Weekly report"
        ],
        "responses": {
          "200": {
            "description": "Weekly report settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean",
                      "example": true
                    },
                    "timeZone": {
                      "type": "string",
                      "example": "Europe/Paris"
                    },
                    "nextSendAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Next Monday 08:00 in `timeZone`."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No account exists for these credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "User not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateWeeklyReportSettings",
        "summary": "Turn the weekly report on or off",
        "description": "Turn the Monday weekly growth report email on or off for your account. Takes effect immediately.",
        "tags": [
          "Weekly report"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "true to receive the report, false to stop it."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "timeZone": {
                      "type": "string"
                    },
                    "nextSendAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No account exists for these credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "User not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/creator-pages": {
      "get": {
        "operationId": "getCreatorPage",
        "summary": "Get your Link in Bio page",
        "description": "Retrieve your Link in Bio page configuration and content",
        "tags": [
          "Link in Bio"
        ],
        "responses": {
          "200": {
            "description": "Your Link in Bio page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatorPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "You have no Link in Bio page yet — create one with `POST /creator-pages`. Plain-text body.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "Creator page not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createCreatorPage",
        "summary": "Create your Link in Bio page",
        "description": "Create your Link in Bio page. Create-only: if you already have a page this returns `400` — update it with `PUT /creator-pages`. `username` is required and also becomes your store subdomain. Send JSON, or `multipart/form-data` to upload `profilePicture`, `coverImage` and `images` files.",
        "tags": [
          "Link in Bio"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "username"
                ],
                "properties": {
                  "creatorName": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Display name at the top of the page. On create it defaults to your account's first + last name.",
                    "example": "Acme Studio"
                  },
                  "creatorBio": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Short bio under the name"
                  },
                  "username": {
                    "type": "string",
                    "pattern": "^[a-zA-Z0-9_-]{3,30}$",
                    "description": "Page username — 3-30 letters, numbers, hyphens or underscores, stored lowercase. It is also your store subdomain (`https://{username}.pocketsflow.com`), so changing it moves your storefront. Required when creating. `400` if another account has it.",
                    "example": "acme"
                  },
                  "socialLinks": {
                    "type": "array",
                    "maxItems": 20,
                    "description": "Social profile links (icons). Entries without a non-empty `name` and `url` are dropped; at most 20 are kept.",
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "url"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "description": "Network / link label",
                          "example": "instagram"
                        },
                        "url": {
                          "type": "string",
                          "example": "https://instagram.com/acme"
                        },
                        "customName": {
                          "type": "string",
                          "description": "Optional custom label"
                        }
                      }
                    }
                  },
                  "regularLinks": {
                    "type": "array",
                    "maxItems": 20,
                    "description": "Link buttons shown on the page, in order. Same shape and limits as `socialLinks`.",
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "url"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "description": "Button label",
                          "example": "My course"
                        },
                        "url": {
                          "type": "string",
                          "example": "https://acme.com/course"
                        },
                        "customName": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "profilePicture": {
                    "type": "string",
                    "description": "Profile picture URL. Send an empty string to clear it, or upload a file with `multipart/form-data` (file field `profilePicture`)."
                  },
                  "coverImage": {
                    "type": "string",
                    "description": "Cover image URL. Send an empty string to clear it, or upload a file (file field `coverImage`)."
                  },
                  "images": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Gallery images (existing image URLs or ids). Omit to keep the current gallery; send an empty value to clear it. Upload files with the `images` file field."
                  },
                  "template": {
                    "type": "string",
                    "enum": [
                      "template1",
                      "template2",
                      "template3",
                      "template4",
                      "template5",
                      "template6",
                      "template7",
                      "template8"
                    ],
                    "description": "Legacy layout template. Unknown values fall back to `template1`."
                  },
                  "theme": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Legacy theme object (layout, colors, style)"
                  },
                  "themeV2": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Link in Bio v2 theme: `{ templateId, layout (classic|hero|storefront), background, button, text, avatar, grainOverlay }`"
                  },
                  "gaMeasurementId": {
                    "type": "string",
                    "pattern": "^$|^G-[A-Z0-9]{4,14}$",
                    "description": "Google Analytics 4 measurement id (`G-XXXXXXXXXX`); empty string to remove. Invalid values are ignored."
                  },
                  "selectedProductIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Products shown on the page. Must be your products in the key's mode, or the request fails with `400`. Sending this (or `selectedSubscriptionIds`) switches the page to an explicit selection — an empty array hides every product."
                  },
                  "selectedSubscriptionIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Subscription offers shown on the page. Same rules as `selectedProductIds`."
                  },
                  "subscriberForm": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Newsletter sign-up form: `{ enabled, displayType (popup|embedded), title, description, buttonText, successMessage, popupSettings }`"
                  },
                  "bookingEnabled": {
                    "type": "boolean",
                    "description": "Show a booking button on the page"
                  },
                  "bookingButtonLabel": {
                    "type": "string",
                    "description": "Label of the booking button"
                  },
                  "showFirstName": {
                    "type": "boolean",
                    "default": true,
                    "description": "Whether your first name may appear on public surfaces"
                  },
                  "showLastName": {
                    "type": "boolean",
                    "default": true,
                    "description": "Whether your last name may appear on public surfaces"
                  }
                }
              },
              "example": {
                "username": "acme",
                "creatorName": "Acme Studio",
                "creatorBio": "Templates for busy founders.",
                "socialLinks": [
                  {
                    "name": "instagram",
                    "url": "https://instagram.com/acme"
                  }
                ],
                "regularLinks": [
                  {
                    "name": "Free guide",
                    "url": "https://acme.com/guide"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatorPage"
                }
              }
            }
          },
          "400": {
            "description": "You already have a page, the username is invalid or taken, or a selected product is not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "User already has a creator page. Use PUT to update it."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — a premium `themeV2.templateId` needs the Starter plan or higher (feature `premiumTemplates`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "premiumTemplates",
                    "currentPlan": "free",
                    "requiredPlan": "tier1",
                    "requiredPlanName": "Starter",
                    "requiredPlanPrice": 15,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "502": {
            "description": "An uploaded image could not be stored — retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateCreatorPage",
        "summary": "Update your Link in Bio page",
        "description": "Partially update your Link in Bio page — only the fields you send change. Changing `username` also moves your store subdomain. JSON or `multipart/form-data` (file fields `profilePicture`, `coverImage`, `images`).",
        "tags": [
          "Link in Bio"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "creatorName": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Display name at the top of the page. On create it defaults to your account's first + last name.",
                    "example": "Acme Studio"
                  },
                  "creatorBio": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Short bio under the name"
                  },
                  "username": {
                    "type": "string",
                    "pattern": "^[a-zA-Z0-9_-]{3,30}$",
                    "description": "Page username — 3-30 letters, numbers, hyphens or underscores, stored lowercase. It is also your store subdomain (`https://{username}.pocketsflow.com`), so changing it moves your storefront. Required when creating. `400` if another account has it.",
                    "example": "acme"
                  },
                  "socialLinks": {
                    "type": "array",
                    "maxItems": 20,
                    "description": "Social profile links (icons). Entries without a non-empty `name` and `url` are dropped; at most 20 are kept.",
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "url"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "description": "Network / link label",
                          "example": "instagram"
                        },
                        "url": {
                          "type": "string",
                          "example": "https://instagram.com/acme"
                        },
                        "customName": {
                          "type": "string",
                          "description": "Optional custom label"
                        }
                      }
                    }
                  },
                  "regularLinks": {
                    "type": "array",
                    "maxItems": 20,
                    "description": "Link buttons shown on the page, in order. Same shape and limits as `socialLinks`.",
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "url"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "description": "Button label",
                          "example": "My course"
                        },
                        "url": {
                          "type": "string",
                          "example": "https://acme.com/course"
                        },
                        "customName": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "profilePicture": {
                    "type": "string",
                    "description": "Profile picture URL. Send an empty string to clear it, or upload a file with `multipart/form-data` (file field `profilePicture`)."
                  },
                  "coverImage": {
                    "type": "string",
                    "description": "Cover image URL. Send an empty string to clear it, or upload a file (file field `coverImage`)."
                  },
                  "images": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Gallery images (existing image URLs or ids). Omit to keep the current gallery; send an empty value to clear it. Upload files with the `images` file field."
                  },
                  "template": {
                    "type": "string",
                    "enum": [
                      "template1",
                      "template2",
                      "template3",
                      "template4",
                      "template5",
                      "template6",
                      "template7",
                      "template8"
                    ],
                    "description": "Legacy layout template. Unknown values fall back to `template1`."
                  },
                  "theme": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Legacy theme object (layout, colors, style)"
                  },
                  "themeV2": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Link in Bio v2 theme: `{ templateId, layout (classic|hero|storefront), background, button, text, avatar, grainOverlay }`"
                  },
                  "gaMeasurementId": {
                    "type": "string",
                    "pattern": "^$|^G-[A-Z0-9]{4,14}$",
                    "description": "Google Analytics 4 measurement id (`G-XXXXXXXXXX`); empty string to remove. Invalid values are ignored."
                  },
                  "selectedProductIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Products shown on the page. Must be your products in the key's mode, or the request fails with `400`. Sending this (or `selectedSubscriptionIds`) switches the page to an explicit selection — an empty array hides every product."
                  },
                  "selectedSubscriptionIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Subscription offers shown on the page. Same rules as `selectedProductIds`."
                  },
                  "subscriberForm": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Newsletter sign-up form: `{ enabled, displayType (popup|embedded), title, description, buttonText, successMessage, popupSettings }`"
                  },
                  "bookingEnabled": {
                    "type": "boolean",
                    "description": "Show a booking button on the page"
                  },
                  "bookingButtonLabel": {
                    "type": "string",
                    "description": "Label of the booking button"
                  },
                  "showFirstName": {
                    "type": "boolean",
                    "default": true,
                    "description": "Whether your first name may appear on public surfaces"
                  },
                  "showLastName": {
                    "type": "boolean",
                    "default": true,
                    "description": "Whether your last name may appear on public surfaces"
                  }
                }
              },
              "example": {
                "creatorBio": "New bio",
                "regularLinks": [
                  {
                    "name": "Course",
                    "url": "https://acme.com/course"
                  },
                  {
                    "name": "Free guide",
                    "url": "https://acme.com/guide"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatorPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or taken username, a selected product is not yours, or a validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Username already taken"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — switching to a premium `themeV2.templateId` needs the Starter plan or higher (feature `premiumTemplates`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "premiumTemplates",
                    "currentPlan": "free",
                    "requiredPlan": "tier1",
                    "requiredPlanName": "Starter",
                    "requiredPlanPrice": 15,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "You have no Link in Bio page yet — create it with `POST /creator-pages`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Creator page not found. Use POST to create one."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "502": {
            "description": "An uploaded image could not be stored — retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteCreatorPage",
        "summary": "Delete your Link in Bio page",
        "description": "Delete your Link in Bio page. Your account subdomain is kept.",
        "tags": [
          "Link in Bio"
        ],
        "responses": {
          "200": {
            "description": "Page deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Creator page deleted successfully"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "You have no Link in Bio page. Plain-text body.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "Creator page not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/creator-pages/username": {
      "patch": {
        "operationId": "updateCreatorPageUsername",
        "summary": "Change your Link in Bio username",
        "description": "Change only the page username. It also becomes your store subdomain (`https://{username}.pocketsflow.com`).",
        "tags": [
          "Link in Bio"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "username"
                ],
                "properties": {
                  "username": {
                    "type": "string",
                    "pattern": "^[a-zA-Z0-9_-]{3,30}$",
                    "description": "3-30 letters, numbers, hyphens or underscores; stored lowercase",
                    "example": "acme"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Username changed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "username": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "username": "acme",
                  "message": "Username updated successfully"
                }
              }
            }
          },
          "400": {
            "description": "Missing, invalid or taken username",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Username already taken"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "You have no Link in Bio page yet — create it with `POST /creator-pages`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/creator-pages/check-username/{username}": {
      "get": {
        "operationId": "checkCreatorPageUsername",
        "summary": "Check whether a username is available",
        "description": "Check a Link in Bio username / store subdomain before claiming it. Your own current username counts as available.",
        "tags": [
          "Link in Bio"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "username",
            "required": true,
            "schema": {
              "type": "string",
              "example": "acme"
            },
            "description": "The username to check"
          }
        ],
        "responses": {
          "200": {
            "description": "The username is available",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Username is available"
                }
              }
            }
          },
          "400": {
            "description": "Invalid format, or already taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Username already taken"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/posts": {
      "get": {
        "operationId": "listPosts",
        "summary": "List all posts",
        "description": "Retrieve your newsletter posts, newest first, optionally only one `status`. Not paginated.",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "scheduled",
                "sending",
                "sent",
                "failed",
                "automation"
              ]
            },
            "description": "Only posts with this status"
          }
        ],
        "responses": {
          "200": {
            "description": "List of posts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Post"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown `status` value",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createPost",
        "summary": "Create a post",
        "description": "Create a newsletter post. Requires newsletter settings (sender) to be configured first. A post is a `draft` unless you send `status: \"scheduled\"` with `scheduledAt` (queued for that time) or `status: \"automation\"`. Sender address, from name and reply-to default to your newsletter settings. Send it now with `POST /newsletters/posts/{id}/send`.",
        "tags": [
          "Posts"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Internal name of the post (shown in your dashboard, not to recipients). Required when creating.",
                    "example": "June product update"
                  },
                  "subject": {
                    "type": "string",
                    "description": "Email subject line",
                    "example": "What's new in June"
                  },
                  "preview": {
                    "type": "string",
                    "description": "Inbox preview text shown after the subject"
                  },
                  "contentHtml": {
                    "type": "string",
                    "description": "HTML body of the email",
                    "example": "<p>Hi there — here's what's new.</p>"
                  },
                  "contentJson": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional editor document the dashboard composer re-opens. Not used for sending — `contentHtml` is what recipients get."
                  },
                  "fromName": {
                    "type": "string",
                    "description": "Sender display name. Defaults to the default from name in your newsletter settings."
                  },
                  "replyToEmail": {
                    "type": "string",
                    "format": "email",
                    "description": "Reply-to address. Defaults to your configured reply-to (or sender) email."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "scheduled",
                      "automation"
                    ],
                    "default": "draft",
                    "description": "`draft` (default), `scheduled` (send at `scheduledAt`), or `automation` (a template only sent by automations). Other statuses are set by the send flow; values you send outside this list are ignored. Any status other than `draft`/`automation` requires `subject`, `preview`, `fromName` and `contentHtml`."
                  },
                  "scheduledAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When to send the post. Use with `status: \"scheduled\"`; scheduling checks your monthly send allowance (`402` when exceeded)."
                  },
                  "scheduledTimezone": {
                    "type": "string",
                    "default": "UTC",
                    "description": "IANA time zone `scheduledAt` is interpreted in",
                    "example": "Europe/Amsterdam"
                  },
                  "sendToAll": {
                    "type": "boolean",
                    "default": false,
                    "description": "Send to every active subscriber"
                  },
                  "subscriberIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Send to these subscribers (`_id`s from `GET /newsletters/subscribers`)"
                  },
                  "productIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Send to everyone who bought these products / subscription offers. Ids outside the key's test/live mode are dropped."
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Send to subscribers carrying any of these tags"
                  },
                  "audience": {
                    "type": "string",
                    "enum": [
                      "everyone",
                      "free",
                      "paid"
                    ],
                    "default": "everyone",
                    "description": "Who may read the post (paid newsletter, Growth plan). `everyone`: every recipient gets the whole post. `free`: only subscribers without a paid membership. `paid`: paid members get the whole post; free subscribers get the part above the paywall marker plus a subscribe button (see `sendPreviewToFree`). Mark the paywall in `contentHtml` with `{{paywall}}` or an element carrying `data-pf-paywall`. Setting `free` or `paid` returns `403` with `code: FEATURE_LOCKED` when your plan does not include the paid newsletter."
                  },
                  "sendPreviewToFree": {
                    "type": "boolean",
                    "default": true,
                    "description": "`paid` posts only: email free subscribers the preview above the paywall plus a subscribe button. `false` sends the post to paid members only."
                  },
                  "publishToWeb": {
                    "type": "boolean",
                    "default": false,
                    "description": "Show the post on your public newsletter archive (`https://<your-subdomain>.pocketsflow.com/newsletter`) once it is sent. Paid posts show their preview and a paywall to anyone who is not a verified paid member."
                  },
                  "segmentId": {
                    "type": "string",
                    "nullable": true,
                    "description": "Send to the contacts in this saved segment (Growth plan and above; `403 FEATURE_LOCKED` otherwise). Its rules are evaluated when the post goes out. `null` clears it."
                  }
                }
              },
              "example": {
                "name": "June product update",
                "subject": "What's new in June",
                "preview": "Three new templates and a discount",
                "contentHtml": "<p>Hi there — here's what's new.</p>",
                "sendToAll": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "400": {
            "description": "Newsletter settings are not configured, or a non-draft status is missing required fields",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Cannot set status to \"scheduled\" without required fields: subject, contentHtml"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Scheduling would exceed your monthly send allowance (`code: SEND_ALLOWANCE_EXCEEDED`, plus `used`, `allowance`, `remaining`, `projected`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "SEND_ALLOWANCE_EXCEEDED",
                  "message": "You've used 9,800 of your 10,000 included email sends this month on the Starter plan. This send would add 500 more. Upgrade to Growth for 40,000 emails a month."
                }
              }
            }
          },
          "403": {
            "description": "Your account is restricted from sending (`error: SELLER_BLOCKED`), your email sending is paused (`error: EMAIL_SENDING_PAUSED`, scheduling only), scheduling needs a paid plan (`error: PAID_PLAN_REQUIRED`, free plan; drafts stay free), you have more subscribers than your plan allows (`error: PLAN_LIMIT_EXCEEDED`), or `audience` is `free` / `paid` and your plan does not include the paid newsletter (`code: FEATURE_LOCKED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "PLAN_LIMIT_EXCEEDED",
                  "message": "Your current plan (Free) supports up to 1000 subscribers. You have 1212 active subscribers."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "description": "New-sender daily limit (`code: DAILY_SEND_LIMIT`, plus `sentToday`, `dailyLimit`, `remaining`, `projected`): accounts in their first 14 days or first 1,000 sends can send up to 1,000 emails per UTC day. Also returned when you exceed the API rate limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "DAILY_SEND_LIMIT",
                  "sentToday": 900,
                  "dailyLimit": 1000,
                  "remaining": 100,
                  "projected": 250,
                  "message": "New senders can send up to 1,000 emails a day for their first 14 days and first 1,000 sends. You've sent 900 today and this send would add 250. Try a smaller audience, or send the rest tomorrow (the limit resets at 00:00 UTC)."
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/posts/{id}": {
      "get": {
        "operationId": "getPost",
        "summary": "Get post by ID",
        "description": "Retrieve one of your newsletter posts (a malformed id answers `404`)",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Post id"
          }
        ],
        "responses": {
          "200": {
            "description": "The post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "updatePost",
        "summary": "Update post",
        "description": "Partially update a post — only the fields you send change. Sent posts cannot be edited (`400`). Setting `status: \"scheduled\"` with `scheduledAt` (re)queues the post after the send-allowance check; moving a scheduled post to `draft` unschedules it.",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Post id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Internal name of the post (shown in your dashboard, not to recipients). Required when creating.",
                    "example": "June product update"
                  },
                  "subject": {
                    "type": "string",
                    "description": "Email subject line",
                    "example": "What's new in June"
                  },
                  "preview": {
                    "type": "string",
                    "description": "Inbox preview text shown after the subject"
                  },
                  "contentHtml": {
                    "type": "string",
                    "description": "HTML body of the email",
                    "example": "<p>Hi there — here's what's new.</p>"
                  },
                  "contentJson": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional editor document the dashboard composer re-opens. Not used for sending — `contentHtml` is what recipients get."
                  },
                  "fromName": {
                    "type": "string",
                    "description": "Sender display name. Defaults to the default from name in your newsletter settings."
                  },
                  "replyToEmail": {
                    "type": "string",
                    "format": "email",
                    "description": "Reply-to address. Defaults to your configured reply-to (or sender) email."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "scheduled",
                      "automation"
                    ],
                    "default": "draft",
                    "description": "`draft` (default), `scheduled` (send at `scheduledAt`), or `automation` (a template only sent by automations). Other statuses are set by the send flow; values you send outside this list are ignored. Any status other than `draft`/`automation` requires `subject`, `preview`, `fromName` and `contentHtml`."
                  },
                  "scheduledAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When to send the post. Use with `status: \"scheduled\"`; scheduling checks your monthly send allowance (`402` when exceeded)."
                  },
                  "scheduledTimezone": {
                    "type": "string",
                    "default": "UTC",
                    "description": "IANA time zone `scheduledAt` is interpreted in",
                    "example": "Europe/Amsterdam"
                  },
                  "sendToAll": {
                    "type": "boolean",
                    "default": false,
                    "description": "Send to every active subscriber"
                  },
                  "subscriberIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Send to these subscribers (`_id`s from `GET /newsletters/subscribers`)"
                  },
                  "productIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Send to everyone who bought these products / subscription offers. Ids outside the key's test/live mode are dropped."
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Send to subscribers carrying any of these tags"
                  },
                  "audience": {
                    "type": "string",
                    "enum": [
                      "everyone",
                      "free",
                      "paid"
                    ],
                    "default": "everyone",
                    "description": "Who may read the post (paid newsletter, Growth plan). `everyone`: every recipient gets the whole post. `free`: only subscribers without a paid membership. `paid`: paid members get the whole post; free subscribers get the part above the paywall marker plus a subscribe button (see `sendPreviewToFree`). Mark the paywall in `contentHtml` with `{{paywall}}` or an element carrying `data-pf-paywall`. Setting `free` or `paid` returns `403` with `code: FEATURE_LOCKED` when your plan does not include the paid newsletter."
                  },
                  "sendPreviewToFree": {
                    "type": "boolean",
                    "default": true,
                    "description": "`paid` posts only: email free subscribers the preview above the paywall plus a subscribe button. `false` sends the post to paid members only."
                  },
                  "publishToWeb": {
                    "type": "boolean",
                    "default": false,
                    "description": "Show the post on your public newsletter archive (`https://<your-subdomain>.pocketsflow.com/newsletter`) once it is sent. Paid posts show their preview and a paywall to anyone who is not a verified paid member."
                  },
                  "segmentId": {
                    "type": "string",
                    "nullable": true,
                    "description": "Send to the contacts in this saved segment (Growth plan and above; `403 FEATURE_LOCKED` otherwise). Its rules are evaluated when the post goes out. `null` clears it."
                  }
                }
              },
              "example": {
                "status": "scheduled",
                "scheduledAt": "2026-07-01T09:00:00.000Z",
                "scheduledTimezone": "UTC"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "400": {
            "description": "The post was already sent, or a non-draft status is missing required fields",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Cannot edit a post that has already been sent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Scheduling would exceed your monthly send allowance (`code: SEND_ALLOWANCE_EXCEEDED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your account is restricted from sending (`error: SELLER_BLOCKED`), your email sending is paused (`error: EMAIL_SENDING_PAUSED`), scheduling needs a paid plan (`error: PAID_PLAN_REQUIRED`, free plan; saving a draft stays free), or the post moves to a `free` / `paid` audience and your plan does not include the paid newsletter (`code: FEATURE_LOCKED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "description": "New-sender daily limit (`code: DAILY_SEND_LIMIT`, plus `sentToday`, `dailyLimit`, `remaining`, `projected`): accounts in their first 14 days or first 1,000 sends can send up to 1,000 emails per UTC day. Also returned when you exceed the API rate limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "DAILY_SEND_LIMIT",
                  "sentToday": 900,
                  "dailyLimit": 1000,
                  "remaining": 100,
                  "projected": 250,
                  "message": "New senders can send up to 1,000 emails a day for their first 14 days and first 1,000 sends. You've sent 900 today and this send would add 250. Try a smaller audience, or send the rest tomorrow (the limit resets at 00:00 UTC)."
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deletePost",
        "summary": "Delete post",
        "description": "Delete a newsletter post (a scheduled post is unscheduled first). Returns the deleted post.",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Post id"
          }
        ],
        "responses": {
          "200": {
            "description": "The deleted post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/posts/{id}/send": {
      "post": {
        "operationId": "sendPost",
        "summary": "Send post now",
        "description": "Queue the post for immediate delivery to its audience (`sendToAll`, `subscriberIds`, `productIds`, `tags`). The post moves to `sending`, then `sent` (or `failed`). Requires a verified sender email. Calling it again while the post is sending is a no-op (`Post is already sending`).",
        "tags": [
          "Posts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Post id"
          }
        ],
        "responses": {
          "200": {
            "description": "The post was queued for sending",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "enum": [
                        "Post queued for sending",
                        "Post is already sending"
                      ]
                    },
                    "post": {
                      "$ref": "#/components/schemas/Post"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Sender email not verified, the post was already sent, it is an automation post, or it is a `free` / `paid` post and your paid newsletter is off (`code: PAID_NEWSLETTER_DISABLED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Post already sent"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The send would exceed your monthly send allowance (`code: SEND_ALLOWANCE_EXCEEDED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your account is restricted from sending (`error: SELLER_BLOCKED`), your email sending is paused after too many bounces or spam reports (`error: EMAIL_SENDING_PAUSED`), sending campaigns needs a paid plan (`error: PAID_PLAN_REQUIRED`, free plan), you have more subscribers than your plan allows (`error: PLAN_LIMIT_EXCEEDED`), or the post is for `free` / `paid` subscribers and your plan does not include the paid newsletter (`code: FEATURE_LOCKED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "EMAIL_SENDING_PAUSED",
                  "code": "EMAIL_SENDING_PAUSED",
                  "message": "Email sending on your account is paused while we review recent bounces and spam reports. Your store, checkout and payouts are not affected. Contact support and we'll help you get sending again."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "description": "New-sender daily limit (`code: DAILY_SEND_LIMIT`, plus `sentToday`, `dailyLimit`, `remaining`, `projected`): accounts in their first 14 days or first 1,000 sends can send up to 1,000 emails per UTC day. Also returned when you exceed the API rate limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "DAILY_SEND_LIMIT",
                  "sentToday": 900,
                  "dailyLimit": 1000,
                  "remaining": 100,
                  "projected": 250,
                  "message": "New senders can send up to 1,000 emails a day for their first 14 days and first 1,000 sends. You've sent 900 today and this send would add 250. Try a smaller audience, or send the rest tomorrow (the limit resets at 00:00 UTC)."
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/send": {
      "post": {
        "operationId": "sendEmail",
        "summary": "Send email",
        "description": "Send an email to up to 100 recipients programmatically. Requires a verified sender email (configure it in Newsletters → Settings). The email is sent with your standard footer, physical address, and unsubscribe link, and is recorded as a sent post (source: api). Recipients who unsubscribed from you, and addresses that hard-bounced or reported spam (for any sender on Pocketsflow), are skipped (listed in `skipped`); the rest count against your monthly send allowance. If too many of your emails bounce or are reported as spam, email sending on your account is paused (`403 EMAIL_SENDING_PAUSED`) until support reviews it; new accounts can send up to 1,000 emails a day (`429 DAILY_SEND_LIMIT`).",
        "tags": [
          "Posts"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to",
                  "subject"
                ],
                "properties": {
                  "to": {
                    "oneOf": [
                      {
                        "type": "string",
                        "format": "email"
                      },
                      {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "format": "email"
                        },
                        "minItems": 1,
                        "maxItems": 100
                      }
                    ],
                    "description": "A recipient email address, or an array of 1-100 addresses"
                  },
                  "subject": {
                    "type": "string",
                    "description": "Email subject line"
                  },
                  "html": {
                    "type": "string",
                    "description": "HTML body of the email. Required unless `text` is provided."
                  },
                  "text": {
                    "type": "string",
                    "description": "Plain-text body. Used as the text alternative; if `html` is omitted, a simple HTML body is generated from it."
                  },
                  "fromName": {
                    "type": "string",
                    "description": "Sender display name. Defaults to your newsletter settings' default from name."
                  },
                  "replyTo": {
                    "type": "string",
                    "format": "email",
                    "description": "Reply-to address. Defaults to your configured reply-to (or sender) email."
                  },
                  "preview": {
                    "type": "string",
                    "description": "Inbox preview text"
                  }
                }
              },
              "example": {
                "to": [
                  "customer@example.com"
                ],
                "subject": "Your weekly update",
                "html": "<p>Hi there — here's what's new this week.</p>",
                "fromName": "Acme Studio",
                "replyTo": "hello@acme.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Email sent — or, when every recipient has unsubscribed, nothing sent (`status: \"skipped\"`, `id: null`, not metered)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "nullable": true,
                      "description": "ID of the recorded email post (null when every recipient was skipped)"
                    },
                    "accepted": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "email"
                      },
                      "description": "Recipients the email was sent to"
                    },
                    "skipped": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "email"
                      },
                      "description": "Recipients who unsubscribed from you, or whose address hard-bounced or reported spam — never emailed, not metered"
                    },
                    "skippedCount": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "sent",
                        "skipped"
                      ]
                    }
                  }
                },
                "example": {
                  "id": "665f1c2a9b3e4a0012ab9911",
                  "accepted": [
                    "customer@example.com"
                  ],
                  "skipped": [],
                  "skippedCount": 0,
                  "status": "sent"
                }
              }
            }
          },
          "400": {
            "description": "Validation error (invalid recipients, missing subject/body, ...) — returned as `{ error }`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The send would exceed your monthly send allowance (`code: SEND_ALLOWANCE_EXCEEDED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Sender email not verified (`error: EMAIL_SENDER_NOT_VERIFIED`), account restricted (`error: SELLER_BLOCKED`), or email sending paused after too many bounces or spam reports (`error: EMAIL_SENDING_PAUSED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "EMAIL_SENDING_PAUSED",
                  "code": "EMAIL_SENDING_PAUSED",
                  "message": "Email sending on your account is paused while we review recent bounces and spam reports. Your store, checkout and payouts are not affected. Contact support and we'll help you get sending again."
                }
              }
            }
          },
          "429": {
            "description": "New-sender daily limit (`code: DAILY_SEND_LIMIT`, plus `sentToday`, `dailyLimit`, `remaining`, `projected`): accounts in their first 14 days or first 1,000 sends can send up to 1,000 emails per UTC day. Also returned when you exceed the API rate limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "DAILY_SEND_LIMIT",
                  "sentToday": 900,
                  "dailyLimit": 1000,
                  "remaining": 100,
                  "projected": 250,
                  "message": "New senders can send up to 1,000 emails a day for their first 14 days and first 1,000 sends. You've sent 900 today and this send would add 250. Try a smaller audience, or send the rest tomorrow (the limit resets at 00:00 UTC)."
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "502": {
            "description": "No emails could be sent (`error: EMAIL_SEND_FAILED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/newsletters/audiences": {
      "get": {
        "operationId": "listProductAudiences",
        "summary": "List product audiences",
        "description": "Every product and subscription in the key's mode, with how many people bought it (`customers`) and how many of them are active email contacts (`subscribers`). Biggest audiences first.",
        "tags": [
          "Subscribers"
        ],
        "responses": {
          "200": {
            "description": "Product audiences",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "productId": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "kind": {
                        "type": "string",
                        "enum": [
                          "product",
                          "subscription"
                        ]
                      },
                      "subscribers": {
                        "type": "number"
                      },
                      "customers": {
                        "type": "number"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/audiences/sync": {
      "post": {
        "operationId": "syncProductAudience",
        "summary": "Add a product's customers to your subscribers",
        "description": "Adds everyone who bought the product (or every buyer, when `productId` is omitted) to your subscribers and links them to what they bought. Contacts who unsubscribed stay unsubscribed.",
        "tags": [
          "Subscribers"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "productId": {
                    "type": "string",
                    "description": "Product or subscription ID. Omit for all buyers."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sync result: `customers` (buyers found), `added` (new contacts), `updated` (existing contacts newly linked), `unsubscribed` (opted-out buyers, linked but never emailed)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "customers": {
                      "type": "integer"
                    },
                    "added": {
                      "type": "integer"
                    },
                    "updated": {
                      "type": "integer"
                    },
                    "unsubscribed": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid productId",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "productId must be a string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Would exceed your plan's subscriber limit (`error: PLAN_LIMIT_EXCEEDED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/subscribers": {
      "get": {
        "operationId": "listEmailSubscribers",
        "summary": "List all subscribers",
        "description": "Retrieve all your newsletter contacts, newest first. Each carries `productIds`: the products and subscriptions they bought. Not paginated.",
        "tags": [
          "Subscribers"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "productId",
            "schema": {
              "type": "string"
            },
            "description": "Only contacts in this product audience"
          }
        ],
        "responses": {
          "200": {
            "description": "List of subscribers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/EmailSubscriber"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/subscribers/{id}": {
      "get": {
        "operationId": "getEmailSubscriber",
        "summary": "Get subscriber by ID",
        "description": "Retrieve one of your newsletter contacts",
        "tags": [
          "Subscribers"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Subscriber id"
          }
        ],
        "responses": {
          "200": {
            "description": "The subscriber",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailSubscriber"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteEmailSubscriber",
        "summary": "Remove subscriber",
        "description": "Delete a contact from your list. Returns the deleted contact.",
        "tags": [
          "Subscribers"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Subscriber id"
          }
        ],
        "responses": {
          "200": {
            "description": "The deleted subscriber",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailSubscriber"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/paid": {
      "get": {
        "operationId": "getPaidNewsletter",
        "summary": "Get paid newsletter",
        "description": "Your paid newsletter: whether it is on, the subscription offers that make someone a paid member, the public archive link, and the numbers (paid subscribers, MRR, free → paid conversion). Readable on every plan.",
        "tags": [
          "Paid newsletter"
        ],
        "responses": {
          "200": {
            "description": "Paid newsletter settings and numbers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaidNewsletter"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updatePaidNewsletter",
        "summary": "Update paid newsletter",
        "description": "Turn the paid newsletter on or off, and pick its membership: a subscription offer billed monthly (`monthlyOfferId`) and/or one billed yearly (`yearlyOfferId`), both your own and in the same test/live mode. Anyone with a live subscription to either is a paid member and carries the `paid` subscriber tag; the tag is re-synced when the membership changes. Only the fields you send change. Turning it on needs the Growth plan or higher (`403 FEATURE_LOCKED`); turning it off never does.",
        "tags": [
          "Paid newsletter"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Turn the paid newsletter on (needs at least one offer) or off"
                  },
                  "monthlyOfferId": {
                    "type": "string",
                    "nullable": true,
                    "description": "Subscription offer id billed monthly, or null to clear"
                  },
                  "yearlyOfferId": {
                    "type": "string",
                    "nullable": true,
                    "description": "Subscription offer id billed yearly, or null to clear"
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Newsletter name on the public archive"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "One or two lines under the name on the archive"
                  },
                  "archiveEnabled": {
                    "type": "boolean",
                    "description": "Show the public archive at `/newsletter` on your storefront"
                  }
                }
              },
              "example": {
                "enabled": true,
                "monthlyOfferId": "665f1c2a9b3e4a0012ab34cd",
                "title": "The Studio Letter"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated paid newsletter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaidNewsletter"
                }
              }
            }
          },
          "400": {
            "description": "An offer is not yours, is billed at the wrong frequency, the two offers are in different modes, or you tried to turn it on without an offer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "monthlyOfferId must be a subscription offer billed monthly"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — turning the paid newsletter on needs the Growth plan or higher (feature `paidNewsletter`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "paidNewsletter",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/paid/stats": {
      "get": {
        "operationId": "getNewsletterStats",
        "summary": "Get paid newsletter stats",
        "description": "Paid subscribers, free subscribers, MRR from the membership offers (in cents), free → paid conversion, and new / canceled memberships in the last 30 days. Readable on every plan.",
        "tags": [
          "Paid newsletter"
        ],
        "responses": {
          "200": {
            "description": "Paid newsletter numbers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaidNewsletterStats"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/paid/sync": {
      "post": {
        "operationId": "syncPaidSubscribers",
        "summary": "Re-sync paid subscribers",
        "description": "Re-reads every live subscription to the membership offers and updates the `paid` subscriber tag: members missing from your list are added (people who unsubscribed stay unsubscribed), lapsed members lose the tag. This also runs on every subscription change and before every paid send, so you rarely need it.",
        "tags": [
          "Paid newsletter"
        ],
        "responses": {
          "200": {
            "description": "Sync result: `paid` (members found), `added` (new contacts), `untagged` (lapsed members)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "paid": {
                      "type": "integer"
                    },
                    "added": {
                      "type": "integer"
                    },
                    "untagged": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The paid newsletter is off (`code: PAID_NEWSLETTER_DISABLED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — re-syncing paid subscribers needs the Growth plan or higher (feature `paidNewsletter`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "paidNewsletter",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/posts/{id}/audience": {
      "post": {
        "operationId": "setPostAudience",
        "summary": "Set post audience",
        "description": "Choose who may read a post — `everyone`, `free` subscribers only, or `paid` (paid members get it in full; free subscribers get the preview above the paywall and a subscribe button unless `sendPreviewToFree` is false) — and whether it appears on your public archive. Works on sent posts too (it then only changes the archive). Moving a post to `free` or `paid` needs the Growth plan (`403 FEATURE_LOCKED`).",
        "tags": [
          "Paid newsletter"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Post id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "audience": {
                    "type": "string",
                    "enum": [
                      "everyone",
                      "free",
                      "paid"
                    ],
                    "default": "everyone",
                    "description": "Who may read the post (paid newsletter, Growth plan). `everyone`: every recipient gets the whole post. `free`: only subscribers without a paid membership. `paid`: paid members get the whole post; free subscribers get the part above the paywall marker plus a subscribe button (see `sendPreviewToFree`). Mark the paywall in `contentHtml` with `{{paywall}}` or an element carrying `data-pf-paywall`. Setting `free` or `paid` returns `403` with `code: FEATURE_LOCKED` when your plan does not include the paid newsletter."
                  },
                  "sendPreviewToFree": {
                    "type": "boolean",
                    "default": true,
                    "description": "`paid` posts only: email free subscribers the preview above the paywall plus a subscribe button. `false` sends the post to paid members only."
                  },
                  "publishToWeb": {
                    "type": "boolean",
                    "default": false,
                    "description": "Show the post on your public newsletter archive (`https://<your-subdomain>.pocketsflow.com/newsletter`) once it is sent. Paid posts show their preview and a paywall to anyone who is not a verified paid member."
                  }
                }
              },
              "example": {
                "audience": "paid",
                "sendPreviewToFree": true,
                "publishToWeb": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                }
              }
            }
          },
          "400": {
            "description": "Invalid field, nothing to change, the post is sending, or a scheduled paid post has no paid newsletter set up (`code: PAID_NEWSLETTER_DISABLED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "audience must be one of: everyone, free, paid"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — moving a post to a `free` or `paid` audience needs the Growth plan or higher (feature `paidNewsletter`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "paidNewsletter",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/images": {
      "post": {
        "operationId": "uploadImage",
        "summary": "Upload image",
        "description": "Upload an image file as multipart/form-data. The file field must be named `image`; other field names return 400. PNG, JPEG, GIF, WebP and AVIF are accepted — the type is detected from the file's bytes, not its name or declared type.",
        "tags": [
          "Images"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "image"
                ],
                "properties": {
                  "image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Image file to upload"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Image uploaded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "imageUrl": {
                      "type": "string",
                      "description": "CDN URL of the uploaded image"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No file in the `image` field, an unexpected multipart field name, or an invalid multipart upload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "An image file is required (multipart field `image`)."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "413": {
            "description": "File exceeds the authenticated plan's upload limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "The file is not a PNG, JPEG, GIF, WebP or AVIF image (checked from the file's bytes; SVG is rejected)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "allowedTypes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "message": "Unsupported image type. Upload a PNG, JPEG, WebP, GIF or AVIF image.",
                  "allowedTypes": [
                    "image/png",
                    "image/jpeg",
                    "image/gif",
                    "image/webp",
                    "image/avif"
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/refunds": {
      "post": {
        "operationId": "createRefundPolicy",
        "summary": "Create a refund policy",
        "description": "Create a refund POLICY (refund terms shown at checkout) that products reference by id (`refundPolicy`). This does NOT refund a payment — refunds are issued from the dashboard.",
        "tags": [
          "Refunds"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Internal name of the policy"
                  },
                  "policy": {
                    "type": "string",
                    "description": "Policy text shown to buyers"
                  },
                  "period": {
                    "type": "number",
                    "minimum": 0,
                    "default": 0,
                    "description": "Length of the refund window"
                  },
                  "periodType": {
                    "type": "string",
                    "enum": [
                      "days",
                      "weeks"
                    ],
                    "default": "days",
                    "description": "Unit of `period`"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created refund policy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RefundPolicy"
                }
              }
            }
          },
          "400": {
            "description": "A field has the wrong type (`period` must be a non-negative number, `periodType` `days` or `weeks`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "get": {
        "operationId": "listRefundPolicies",
        "summary": "List refund policies",
        "description": "Retrieve your refund POLICIES in the key's mode, newest first, each with `appliedTo` — how many products use it",
        "tags": [
          "Refunds"
        ],
        "responses": {
          "200": {
            "description": "List of refund policies",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RefundPolicy"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/refunds/{id}": {
      "get": {
        "operationId": "getRefundPolicy",
        "summary": "Get refund policy by ID",
        "description": "PUBLIC (no API key) — retrieve a refund POLICY by its id. Checkout renders it for buyers.",
        "tags": [
          "Refunds"
        ],
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Refund policy id"
          }
        ],
        "responses": {
          "200": {
            "description": "The refund policy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RefundPolicy"
                }
              }
            }
          },
          "404": {
            "description": "Refund policy not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Refund not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "updateRefundPolicy",
        "summary": "Update a refund policy",
        "description": "Update one of your refund POLICIES — only the fields you send change.",
        "tags": [
          "Refunds"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Refund policy id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "policy": {
                    "type": "string"
                  },
                  "period": {
                    "type": "number"
                  },
                  "periodType": {
                    "type": "string",
                    "enum": [
                      "days",
                      "weeks"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated refund policy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RefundPolicy"
                }
              }
            }
          },
          "400": {
            "description": "A field has the wrong type (`period` must be a non-negative number, `periodType` `days` or `weeks`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "periodType must be 'days' or 'weeks'"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No refund policy of yours with this id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Refund not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteRefundPolicy",
        "summary": "Delete a refund policy",
        "description": "Delete one of your refund POLICIES. Returns the deleted policy. Products that referenced it keep the stale id — update them.",
        "tags": [
          "Refunds"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Refund policy id"
          }
        ],
        "responses": {
          "200": {
            "description": "The deleted refund policy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RefundPolicy"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Refund policy not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Refund not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/checkout/sessions": {
      "post": {
        "operationId": "createCheckoutSession",
        "summary": "Create checkout session",
        "description": "Create a hosted checkout link for a one-time product OR a subscription (recurring) offer — pass either kind of id as `productId`. Redirect the buyer to the returned `url`. The link lives on your store subdomain, so your account needs a `subdomain` first (`400` otherwise). Payment methods (card, Apple Pay, Google Pay, Link, ACH, and country-specific methods such as iDEAL) are taken from the seller's account — there is no payment-method parameter on this endpoint.",
        "tags": [
          "Checkout"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productId"
                ],
                "properties": {
                  "productId": {
                    "type": "string",
                    "description": "Id of the product OR subscription offer to purchase."
                  },
                  "successUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "Absolute http(s) URL to send the buyer to after a successful payment. Optional."
                  },
                  "cancelUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "Absolute http(s) URL to send the buyer to if they cancel. Optional."
                  },
                  "customerEmail": {
                    "type": "string",
                    "format": "email",
                    "description": "Customer email. Prefills the email on the checkout page (and inside the payment form) and is attached to the resulting order and webhook payload."
                  },
                  "lockEmail": {
                    "type": "boolean",
                    "description": "With customerEmail, makes the prefilled email read-only so the buyer can't change it."
                  },
                  "clientReferenceId": {
                    "type": "string",
                    "maxLength": 256,
                    "description": "Your own correlation id. Echoed back top-level as `clientReferenceId` on the resulting webhooks, including every subscription lifecycle event."
                  },
                  "discountCode": {
                    "type": "string",
                    "description": "Discount code to apply"
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Custom metadata for the checkout, echoed back on webhook payloads. For subscriptions it is captured at activation and replayed on every customer.subscription.* / invoice.* event."
                  }
                }
              },
              "example": {
                "productId": "665f1c2a9b3e4a0012ab34cd",
                "successUrl": "https://acme.com/thanks",
                "cancelUrl": "https://acme.com/pricing",
                "customerEmail": "buyer@example.com",
                "clientReferenceId": "order_8812"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Checkout session created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "description": "Missing `productId`, a non-http(s) `successUrl`/`cancelUrl`, invalid `metadata` / `clientReferenceId`, or no store subdomain yet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "successUrl must be a valid http(s) URL"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "`productId` is not one of your products or subscription offers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/checkout/abandoned/summary": {
      "get": {
        "operationId": "getAbandonedCheckoutSummary",
        "summary": "Get lost checkouts (last 30 days)",
        "description": "How many live checkouts you lost over the last 30 days and what they were worth: a buyer typed their name or email (or had a payment declined) more than an hour ago and never paid. Each buyer counts once per product, and not at all if they bought it on another try. Always counts live checkouts, whatever the key's mode. Readable on every plan; recovering them by email (`PATCH /products/{id}/abandoned-checkout`) needs Growth.",
        "tags": [
          "Checkout"
        ],
        "responses": {
          "200": {
            "description": "Your lost checkouts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AbandonedCheckoutSummary"
                },
                "example": {
                  "count": 7,
                  "amountUsd": 243,
                  "windowDays": 30
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/partners": {
      "post": {
        "operationId": "createPartner",
        "summary": "Become a partner",
        "description": "Register as a Pocketsflow partner. You'll receive a unique referral code to share. When users sign up using your code, you earn commissions on their sales.",
        "tags": [
          "Partners"
        ],
        "responses": {
          "201": {
            "description": "Partner created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Partner"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "Already a partner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "get": {
        "operationId": "getPartner",
        "summary": "Get my partner profile",
        "description": "Retrieve your partner profile including referral code and denormalized stats.",
        "tags": [
          "Partners"
        ],
        "responses": {
          "200": {
            "description": "Partner profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Partner"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not a partner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/partners/register-referral": {
      "post": {
        "operationId": "registerReferral",
        "summary": "Register a referral signup",
        "description": "Call this after a user signs up through a partner's referral link or a creator's \"Powered by Pocketsflow\" badge link. Sets the referral code on the authenticated user and increments the partner's signup count (partner codes, `PF-` + 8 characters) or opens the referring creator's free-month reward (badge codes, `PF-` + 10 characters). Only accounts created within the last 30 days can attach a referral code, and never their own.",
        "tags": [
          "Partners"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "referralCode"
                ],
                "properties": {
                  "referralCode": {
                    "type": "string",
                    "example": "PF-a1b2c3d4",
                    "description": "The partner's referral code from the signup link"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Referral registered successfully"
          },
          "400": {
            "description": "`referralCode` missing or not a string, the account already has a referral, a self-referral, or the account is more than 30 days old",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Invalid or inactive referral code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/partners/referrals": {
      "get": {
        "operationId": "listReferrals",
        "summary": "Get referred users",
        "description": "List all users who signed up using your referral code. Each user includes their live sales count and total revenue in USD.",
        "tags": [
          "Partners"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number"
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 20,
              "maximum": 100
            },
            "description": "Referred users per page (maximum 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of referred users with live sales count and USD revenue",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/PartnerPagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not a partner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/partners/referrals/sales": {
      "get": {
        "operationId": "listReferralSales",
        "summary": "Get sales from referred users",
        "description": "Get the live sales of users who signed up through your referral code (test-mode and refunded sales excluded), newest first. Each item carries the seller, amounts, USD revenue and your commission — never buyer data.",
        "tags": [
          "Partners"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number"
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 20,
              "maximum": 100
            },
            "description": "Sales per page (maximum 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of live (non-test, non-refunded) sales of referred users. No buyer data is included.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "userId": {
                            "type": "string",
                            "description": "The referred seller"
                          },
                          "merchantName": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "number",
                            "description": "Charged amount in `currency`. Decimal amount in major currency units (e.g. `29.99`), not cents."
                          },
                          "grossAmount": {
                            "type": "number",
                            "description": "Gross amount in `currency`. Decimal amount in major currency units (e.g. `29.99`), not cents."
                          },
                          "currency": {
                            "type": "string"
                          },
                          "revenueUsd": {
                            "type": "number",
                            "description": "The sale's revenue in USD. Decimal amount in major currency units (e.g. `29.99`), not cents."
                          },
                          "isSubscription": {
                            "type": "boolean"
                          },
                          "partnerCut": {
                            "type": "number",
                            "description": "Partner cut stored on the sale. Decimal amount in major currency units (e.g. `29.99`), not cents."
                          },
                          "commission": {
                            "type": "number",
                            "description": "Your commission on this sale (= partnerCut). Decimal amount in major currency units (e.g. `29.99`), not cents."
                          }
                        }
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/PartnerPagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not a partner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/partners/stats": {
      "get": {
        "operationId": "getPartnerStats",
        "summary": "Get partner stats",
        "description": "Get live-computed aggregated stats: total signups, total sales, total revenue, and total commission earned based on your commission rate.",
        "tags": [
          "Partners"
        ],
        "responses": {
          "200": {
            "description": "Aggregated partner stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "referralCode": {
                      "type": "string",
                      "example": "PF-a1b2c3d4"
                    },
                    "commissionRate": {
                      "type": "number",
                      "example": 20
                    },
                    "totalSignups": {
                      "type": "number"
                    },
                    "totalSales": {
                      "type": "number"
                    },
                    "totalRevenue": {
                      "type": "number",
                      "description": "Revenue from referred users. Decimal amount in major currency units (e.g. `29.99`), not cents."
                    },
                    "totalCommission": {
                      "type": "number",
                      "description": "Commission earned. Decimal amount in major currency units (e.g. `29.99`), not cents."
                    },
                    "active": {
                      "type": "boolean"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not a partner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/powered-by/me": {
      "get": {
        "operationId": "getStoreBadge",
        "summary": "Get the store badge and its referral stats",
        "description": "Whether the \"Powered by Pocketsflow\" badge shows on your buyer pages, whether your plan lets you hide it, your badge referral code and link, and what the badge earned: link clicks, stores that signed up through it, stores whose first payment is clearing (14 days), and free months earned, applied and banked (at most 12 a year).",
        "tags": [
          "Store badge"
        ],
        "responses": {
          "200": {
            "description": "The badge setting and referral stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "showPoweredBy": {
                      "type": "boolean",
                      "description": "Your saved choice (default true)"
                    },
                    "visible": {
                      "type": "boolean",
                      "description": "Whether buyers see the badge right now. false only when you hid it and your plan allows hiding."
                    },
                    "canHide": {
                      "type": "boolean",
                      "description": "Whether your plan lets you hide the badge"
                    },
                    "requiredPlan": {
                      "type": "string",
                      "example": "tier1",
                      "description": "The cheapest plan that can hide the badge"
                    },
                    "requiredPlanName": {
                      "type": "string",
                      "example": "Starter"
                    },
                    "code": {
                      "type": "string",
                      "example": "PF-k3x9q2m7v1",
                      "description": "Your badge referral code"
                    },
                    "link": {
                      "type": "string",
                      "example": "https://www.pocketsflow.com/?ref=PF-k3x9q2m7v1",
                      "description": "Where the badge links"
                    },
                    "clicks": {
                      "type": "integer",
                      "description": "Badge link clicks"
                    },
                    "signups": {
                      "type": "integer",
                      "description": "Stores that signed up through your badge"
                    },
                    "clearing": {
                      "type": "integer",
                      "description": "Referred stores whose first payment is inside the 14-day clearance"
                    },
                    "freeMonths": {
                      "type": "object",
                      "properties": {
                        "earned": {
                          "type": "integer"
                        },
                        "applied": {
                          "type": "integer",
                          "description": "Applied to a plan payment"
                        },
                        "banked": {
                          "type": "integer",
                          "description": "Earned and not used yet"
                        },
                        "earnedThisYear": {
                          "type": "integer",
                          "description": "Earned in the last 365 days"
                        },
                        "yearlyCap": {
                          "type": "integer",
                          "example": 12
                        }
                      }
                    }
                  }
                },
                "example": {
                  "showPoweredBy": true,
                  "visible": true,
                  "canHide": false,
                  "requiredPlan": "tier1",
                  "requiredPlanName": "Starter",
                  "code": "PF-k3x9q2m7v1",
                  "link": "https://www.pocketsflow.com/?ref=PF-k3x9q2m7v1",
                  "clicks": 418,
                  "signups": 9,
                  "clearing": 1,
                  "freeMonths": {
                    "earned": 3,
                    "applied": 2,
                    "banked": 1,
                    "earnedThisYear": 3,
                    "yearlyCap": 12
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/powered-by": {
      "put": {
        "operationId": "updateStoreBadge",
        "summary": "Show or hide the store badge",
        "description": "Show (`true`) or hide (`false`) the \"Powered by Pocketsflow\" badge on every buyer page and receipt. Showing it is always allowed. Hiding it needs the Starter plan or higher (`403 FEATURE_LOCKED` otherwise); if the plan later ends, the badge shows again automatically and your choice is kept. While the badge is hidden it carries no referral link, so it earns no free months.",
        "tags": [
          "Store badge"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "showPoweredBy"
                ],
                "properties": {
                  "showPoweredBy": {
                    "type": "boolean",
                    "example": false,
                    "description": "true shows the badge, false hides it (Starter plan or higher)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated badge setting and referral stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "showPoweredBy": {
                      "type": "boolean",
                      "description": "Your saved choice (default true)"
                    },
                    "visible": {
                      "type": "boolean",
                      "description": "Whether buyers see the badge right now. false only when you hid it and your plan allows hiding."
                    },
                    "canHide": {
                      "type": "boolean",
                      "description": "Whether your plan lets you hide the badge"
                    },
                    "requiredPlan": {
                      "type": "string",
                      "example": "tier1",
                      "description": "The cheapest plan that can hide the badge"
                    },
                    "requiredPlanName": {
                      "type": "string",
                      "example": "Starter"
                    },
                    "code": {
                      "type": "string",
                      "example": "PF-k3x9q2m7v1",
                      "description": "Your badge referral code"
                    },
                    "link": {
                      "type": "string",
                      "example": "https://www.pocketsflow.com/?ref=PF-k3x9q2m7v1",
                      "description": "Where the badge links"
                    },
                    "clicks": {
                      "type": "integer",
                      "description": "Badge link clicks"
                    },
                    "signups": {
                      "type": "integer",
                      "description": "Stores that signed up through your badge"
                    },
                    "clearing": {
                      "type": "integer",
                      "description": "Referred stores whose first payment is inside the 14-day clearance"
                    },
                    "freeMonths": {
                      "type": "object",
                      "properties": {
                        "earned": {
                          "type": "integer"
                        },
                        "applied": {
                          "type": "integer",
                          "description": "Applied to a plan payment"
                        },
                        "banked": {
                          "type": "integer",
                          "description": "Earned and not used yet"
                        },
                        "earnedThisYear": {
                          "type": "integer",
                          "description": "Earned in the last 365 days"
                        },
                        "yearlyCap": {
                          "type": "integer",
                          "example": 12
                        }
                      }
                    }
                  }
                },
                "example": {
                  "showPoweredBy": true,
                  "visible": true,
                  "canHide": false,
                  "requiredPlan": "tier1",
                  "requiredPlanName": "Starter",
                  "code": "PF-k3x9q2m7v1",
                  "link": "https://www.pocketsflow.com/?ref=PF-k3x9q2m7v1",
                  "clicks": 418,
                  "signups": 9,
                  "clearing": 1,
                  "freeMonths": {
                    "earned": 3,
                    "applied": 2,
                    "banked": 1,
                    "earnedThisYear": 3,
                    "yearlyCap": 12
                  }
                }
              }
            }
          },
          "400": {
            "description": "`showPoweredBy` missing or not a boolean",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Hiding the badge needs a higher plan (`code: FEATURE_LOCKED`, `details.feature: removeBadge`, `details.requiredPlan: tier1`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "removeBadge",
                    "currentPlan": "free",
                    "requiredPlan": "tier1",
                    "requiredPlanName": "Starter",
                    "requiredPlanPrice": 15,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "Account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/licenses/validate": {
      "post": {
        "operationId": "validateLicenseKey",
        "summary": "Validate a license key",
        "description": "Check a key your buyer entered. Pass `instanceId` to also check that this install is activated. PUBLIC — no API key: call it from the software you sell. Rate limited to 60 requests per minute per IP across the three license endpoints. Every answer has the same body (`LicenseValidation`); read `valid`, and `code` when it is false.",
        "tags": [
          "Licenses"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productId",
                  "licenseKey"
                ],
                "properties": {
                  "productId": {
                    "type": "string",
                    "description": "Id of the product the key was sold with. Required: a key only answers for its own product.",
                    "example": "665f1c2a9b3e4a0012ab34cd"
                  },
                  "licenseKey": {
                    "type": "string",
                    "description": "The key the buyer entered. Case, spaces and dashes are ignored; O is read as 0 and I/L as 1.",
                    "example": "7KQ2M-X9D4R-T3VWB-8HNPC"
                  },
                  "instanceId": {
                    "type": "string",
                    "maxLength": 128,
                    "description": "Your id for this install / device (1-128 characters: letters, digits, `.`, `_`, `:`, `-`). Omit on activate to get a generated `ins_…` id back. On validate, also checks that this instance is activated."
                  },
                  "instanceName": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Activate only: a label the creator sees on the order, e.g. the device name."
                  }
                }
              },
              "example": {
                "productId": "665f1c2a9b3e4a0012ab34cd",
                "licenseKey": "7KQ2M-X9D4R-T3VWB-8HNPC",
                "instanceId": "macbook-pro-2f9c",
                "instanceName": "Anna's MacBook Pro"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The key exists for this product — `valid` says whether to unlock",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                },
                "example": {
                  "valid": true,
                  "status": "active",
                  "code": null,
                  "message": "License key is valid.",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "activations": 1,
                  "activationLimit": 3,
                  "activationsRemaining": 2,
                  "buyerEmail": "a***@example.com",
                  "createdAt": "2026-10-15T09:12:44.000Z",
                  "testMode": false,
                  "instance": {
                    "id": "macbook-pro-2f9c",
                    "name": "Anna's MacBook Pro",
                    "createdAt": "2026-10-15T09:20:02.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "`productId` or `licenseKey` missing, or an invalid `instanceId`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                }
              }
            }
          },
          "404": {
            "description": "No such key for this product. A key of another product answers exactly the same.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                },
                "example": {
                  "valid": false,
                  "status": "not_found",
                  "code": "license_not_found",
                  "message": "License key not found for this product.",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "activations": 0,
                  "activationLimit": null,
                  "activationsRemaining": null,
                  "buyerEmail": null,
                  "createdAt": null,
                  "testMode": false,
                  "instance": null
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/licenses/activate": {
      "post": {
        "operationId": "activateLicenseKey",
        "summary": "Activate a license key",
        "description": "Activate the key on one install / device and count it against the key's activation limit. Idempotent per `instanceId`. Keep the returned `instance.id` to deactivate later. PUBLIC — no API key: call it from the software you sell. Rate limited to 60 requests per minute per IP across the three license endpoints. Every answer has the same body (`LicenseValidation`); read `valid`, and `code` when it is false.",
        "tags": [
          "Licenses"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productId",
                  "licenseKey"
                ],
                "properties": {
                  "productId": {
                    "type": "string",
                    "description": "Id of the product the key was sold with. Required: a key only answers for its own product.",
                    "example": "665f1c2a9b3e4a0012ab34cd"
                  },
                  "licenseKey": {
                    "type": "string",
                    "description": "The key the buyer entered. Case, spaces and dashes are ignored; O is read as 0 and I/L as 1.",
                    "example": "7KQ2M-X9D4R-T3VWB-8HNPC"
                  },
                  "instanceId": {
                    "type": "string",
                    "maxLength": 128,
                    "description": "Your id for this install / device (1-128 characters: letters, digits, `.`, `_`, `:`, `-`). Omit on activate to get a generated `ins_…` id back. On validate, also checks that this instance is activated."
                  },
                  "instanceName": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Activate only: a label the creator sees on the order, e.g. the device name."
                  }
                }
              },
              "example": {
                "productId": "665f1c2a9b3e4a0012ab34cd",
                "licenseKey": "7KQ2M-X9D4R-T3VWB-8HNPC",
                "instanceId": "macbook-pro-2f9c",
                "instanceName": "Anna's MacBook Pro"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already activated on this instance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                },
                "example": {
                  "valid": true,
                  "status": "active",
                  "code": null,
                  "message": "License key is valid.",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "activations": 1,
                  "activationLimit": 3,
                  "activationsRemaining": 2,
                  "buyerEmail": "a***@example.com",
                  "createdAt": "2026-10-15T09:12:44.000Z",
                  "testMode": false,
                  "instance": {
                    "id": "macbook-pro-2f9c",
                    "name": "Anna's MacBook Pro",
                    "createdAt": "2026-10-15T09:20:02.000Z"
                  }
                }
              }
            }
          },
          "201": {
            "description": "Activated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                },
                "example": {
                  "valid": true,
                  "status": "active",
                  "code": null,
                  "message": "License key is valid.",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "activations": 1,
                  "activationLimit": 3,
                  "activationsRemaining": 2,
                  "buyerEmail": "a***@example.com",
                  "createdAt": "2026-10-15T09:12:44.000Z",
                  "testMode": false,
                  "instance": {
                    "id": "macbook-pro-2f9c",
                    "name": "Anna's MacBook Pro",
                    "createdAt": "2026-10-15T09:20:02.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "`productId` or `licenseKey` missing, or an invalid `instanceId`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                }
              }
            }
          },
          "403": {
            "description": "The key is disabled (by you, or because its order was refunded or disputed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                }
              }
            }
          },
          "404": {
            "description": "No such key for this product. A key of another product answers exactly the same.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                },
                "example": {
                  "valid": false,
                  "status": "not_found",
                  "code": "license_not_found",
                  "message": "License key not found for this product.",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "activations": 0,
                  "activationLimit": null,
                  "activationsRemaining": null,
                  "buyerEmail": null,
                  "createdAt": null,
                  "testMode": false,
                  "instance": null
                }
              }
            }
          },
          "409": {
            "description": "The key has reached its activation limit (`code: activation_limit_reached`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/licenses/deactivate": {
      "post": {
        "operationId": "deactivateLicenseKey",
        "summary": "Deactivate a license key",
        "description": "Free one instance's activation, e.g. when the user signs out or moves to a new computer. PUBLIC — no API key: call it from the software you sell. Rate limited to 60 requests per minute per IP across the three license endpoints. Every answer has the same body (`LicenseValidation`); read `valid`, and `code` when it is false.",
        "tags": [
          "Licenses"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productId",
                  "licenseKey",
                  "instanceId"
                ],
                "properties": {
                  "productId": {
                    "type": "string",
                    "description": "Id of the product the key was sold with. Required: a key only answers for its own product.",
                    "example": "665f1c2a9b3e4a0012ab34cd"
                  },
                  "licenseKey": {
                    "type": "string",
                    "description": "The key the buyer entered. Case, spaces and dashes are ignored; O is read as 0 and I/L as 1.",
                    "example": "7KQ2M-X9D4R-T3VWB-8HNPC"
                  },
                  "instanceId": {
                    "type": "string",
                    "maxLength": 128,
                    "description": "The instance to free — the `instance.id` returned by activate."
                  }
                }
              },
              "example": {
                "productId": "665f1c2a9b3e4a0012ab34cd",
                "licenseKey": "7KQ2M-X9D4R-T3VWB-8HNPC",
                "instanceId": "macbook-pro-2f9c"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deactivated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                }
              }
            }
          },
          "400": {
            "description": "`productId` or `licenseKey` missing, or an invalid `instanceId`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                }
              }
            }
          },
          "404": {
            "description": "No such key for this product, or that instance is not activated (`code: instance_not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseValidation"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/licenses": {
      "get": {
        "operationId": "listLicenseKeys",
        "summary": "List license keys",
        "description": "Your license keys in the API key's mode, newest first. Filter by product, order, status or buyer email.",
        "tags": [
          "Licenses"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "productId",
            "schema": {
              "type": "string"
            },
            "description": "Only keys of this product"
          },
          {
            "in": "query",
            "name": "orderId",
            "schema": {
              "type": "string"
            },
            "description": "Only the key of this order"
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "disabled"
              ]
            }
          },
          {
            "in": "query",
            "name": "email",
            "schema": {
              "type": "string",
              "format": "email"
            },
            "description": "Only keys bought with this email"
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number"
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 20,
              "maximum": 100
            },
            "description": "Keys per page (maximum 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of license keys",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LicenseKey"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "page": {
                          "type": "integer"
                        },
                        "pageSize": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        },
                        "totalPages": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/licenses/{id}": {
      "get": {
        "operationId": "getLicenseKey",
        "summary": "Get a license key",
        "description": "One license key with its activations.",
        "tags": [
          "Licenses"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "License key id (the `id` field, not the key itself)"
          }
        ],
        "responses": {
          "200": {
            "description": "The license key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseKey"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No license key with this id in your account (current mode)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "License key not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/licenses/{id}/disable": {
      "post": {
        "operationId": "disableLicenseKey",
        "summary": "Disable a license key",
        "description": "Switch the key off: validate and activate answer `valid: false` with `code: license_disabled`.",
        "tags": [
          "Licenses"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "License key id (the `id` field, not the key itself)"
          }
        ],
        "responses": {
          "200": {
            "description": "The disabled key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseKey"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No license key with this id in your account (current mode)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "License key not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/licenses/{id}/enable": {
      "post": {
        "operationId": "enableLicenseKey",
        "summary": "Enable a license key",
        "description": "Switch the key back on. Re-enabling the key of a refunded or disputed order is an explicit override: the order no longer switches it off.",
        "tags": [
          "Licenses"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "License key id (the `id` field, not the key itself)"
          }
        ],
        "responses": {
          "200": {
            "description": "The enabled key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseKey"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No license key with this id in your account (current mode)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "License key not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/licenses/{id}/reset-activations": {
      "post": {
        "operationId": "resetLicenseKeyActivations",
        "summary": "Reset a license key's activations",
        "description": "Remove every activation so the buyer can activate again from scratch (e.g. after replacing their computers).",
        "tags": [
          "Licenses"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "License key id (the `id` field, not the key itself)"
          }
        ],
        "responses": {
          "200": {
            "description": "The key, with no activations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseKey"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No license key with this id in your account (current mode)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "License key not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/analytics/preview": {
      "get": {
        "operationId": "getAnalyticsPreview",
        "summary": "Get headline analytics",
        "description": "Headline totals for the range on every plan: the funnel totals, the top traffic source by net revenue and, when you sell subscriptions, MRR. The detailed reports are under /analytics/advanced (Growth) and /analytics/mrr (Pro).",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-01T00:00:00.000Z"
            },
            "description": "Start of the range (ISO). Default: 30 days before `to`."
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-30T23:59:59.999Z"
            },
            "description": "End of the range (ISO; a bare date covers that whole day). Default: now. At most 732 days after `from`."
          },
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "minimum": -840,
              "maximum": 840,
              "example": -120
            },
            "description": "Your UTC offset as JavaScript's `Date#getTimezoneOffset()` (UTC − local, minutes) — days and months are bucketed in your local time. Default: 0 (UTC)."
          }
        ],
        "responses": {
          "200": {
            "description": "Headline totals",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "range": {
                      "type": "object",
                      "properties": {
                        "from": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "to": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "days": {
                          "type": "integer"
                        },
                        "previous": {
                          "type": "object",
                          "description": "The comparison window: same length, right before `from`",
                          "properties": {
                            "from": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "to": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    },
                    "funnel": {
                      "type": "object",
                      "properties": {
                        "visits": {
                          "type": "integer"
                        },
                        "productViews": {
                          "type": "integer"
                        },
                        "checkouts": {
                          "type": "integer"
                        },
                        "paid": {
                          "type": "integer"
                        },
                        "revenue": {
                          "type": "number",
                          "description": "Net revenue of paid orders, in `currency` (major units, e.g. 12.5)"
                        }
                      }
                    },
                    "topSource": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "key": {
                          "type": "string"
                        },
                        "netRevenue": {
                          "type": "number",
                          "description": "Net revenue, in `currency` (major units, e.g. 12.5)"
                        }
                      }
                    },
                    "mrr": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "mrr": {
                          "type": "number",
                          "description": "MRR, in `currency` (major units, e.g. 12.5)"
                        },
                        "subscribers": {
                          "type": "integer"
                        },
                        "newMrr": {
                          "type": "number",
                          "description": "New MRR, in `currency` (major units, e.g. 12.5)"
                        },
                        "churnedMrr": {
                          "type": "number",
                          "description": "Churned MRR, in `currency` (major units, e.g. 12.5)"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid `from` / `to` (not ISO dates, `from` after `to`, or a range longer than 732 days) or an invalid option",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "from must be before to"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/analytics/advanced/summary": {
      "get": {
        "operationId": "getAnalyticsSummary",
        "summary": "Compare key metrics with the previous period",
        "description": "Revenue, net revenue after refunds, refunds, orders, average order, visits, conversion rate, customers and repeat customers for the range and the previous period of the same length, plus a daily series of both. Growth plan and above.",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-01T00:00:00.000Z"
            },
            "description": "Start of the range (ISO). Default: 30 days before `to`."
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-30T23:59:59.999Z"
            },
            "description": "End of the range (ISO; a bare date covers that whole day). Default: now. At most 732 days after `from`."
          },
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "minimum": -840,
              "maximum": 840,
              "example": -120
            },
            "description": "Your UTC offset as JavaScript's `Date#getTimezoneOffset()` (UTC − local, minutes) — days and months are bucketed in your local time. Default: 0 (UTC)."
          }
        ],
        "responses": {
          "200": {
            "description": "KPIs and daily series",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "range": {
                      "type": "object",
                      "properties": {
                        "from": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "to": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "days": {
                          "type": "integer"
                        },
                        "previous": {
                          "type": "object",
                          "description": "The comparison window: same length, right before `from`",
                          "properties": {
                            "from": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "to": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    },
                    "kpis": {
                      "type": "object",
                      "properties": {
                        "revenue": {
                          "type": "object",
                          "description": "A metric for the range and the previous period of the same length",
                          "properties": {
                            "current": {
                              "type": "number"
                            },
                            "previous": {
                              "type": "number"
                            },
                            "change": {
                              "type": "number",
                              "description": "current − previous"
                            },
                            "changePct": {
                              "type": "number",
                              "nullable": true,
                              "description": "Relative change in percent (null when previous is 0)"
                            }
                          }
                        },
                        "netRevenue": {
                          "type": "object",
                          "description": "A metric for the range and the previous period of the same length",
                          "properties": {
                            "current": {
                              "type": "number"
                            },
                            "previous": {
                              "type": "number"
                            },
                            "change": {
                              "type": "number",
                              "description": "current − previous"
                            },
                            "changePct": {
                              "type": "number",
                              "nullable": true,
                              "description": "Relative change in percent (null when previous is 0)"
                            }
                          }
                        },
                        "refunds": {
                          "type": "object",
                          "description": "A metric for the range and the previous period of the same length",
                          "properties": {
                            "current": {
                              "type": "number"
                            },
                            "previous": {
                              "type": "number"
                            },
                            "change": {
                              "type": "number",
                              "description": "current − previous"
                            },
                            "changePct": {
                              "type": "number",
                              "nullable": true,
                              "description": "Relative change in percent (null when previous is 0)"
                            }
                          }
                        },
                        "orders": {
                          "type": "object",
                          "description": "A metric for the range and the previous period of the same length",
                          "properties": {
                            "current": {
                              "type": "number"
                            },
                            "previous": {
                              "type": "number"
                            },
                            "change": {
                              "type": "number",
                              "description": "current − previous"
                            },
                            "changePct": {
                              "type": "number",
                              "nullable": true,
                              "description": "Relative change in percent (null when previous is 0)"
                            }
                          }
                        },
                        "averageOrder": {
                          "type": "object",
                          "description": "A metric for the range and the previous period of the same length",
                          "properties": {
                            "current": {
                              "type": "number"
                            },
                            "previous": {
                              "type": "number"
                            },
                            "change": {
                              "type": "number",
                              "description": "current − previous"
                            },
                            "changePct": {
                              "type": "number",
                              "nullable": true,
                              "description": "Relative change in percent (null when previous is 0)"
                            }
                          }
                        },
                        "visits": {
                          "type": "object",
                          "description": "A metric for the range and the previous period of the same length",
                          "properties": {
                            "current": {
                              "type": "number"
                            },
                            "previous": {
                              "type": "number"
                            },
                            "change": {
                              "type": "number",
                              "description": "current − previous"
                            },
                            "changePct": {
                              "type": "number",
                              "nullable": true,
                              "description": "Relative change in percent (null when previous is 0)"
                            }
                          }
                        },
                        "conversionRate": {
                          "type": "object",
                          "description": "A metric for the range and the previous period of the same length",
                          "properties": {
                            "current": {
                              "type": "number"
                            },
                            "previous": {
                              "type": "number"
                            },
                            "change": {
                              "type": "number",
                              "description": "current − previous"
                            },
                            "changePct": {
                              "type": "number",
                              "nullable": true,
                              "description": "Relative change in percent (null when previous is 0)"
                            }
                          }
                        },
                        "customers": {
                          "type": "object",
                          "description": "A metric for the range and the previous period of the same length",
                          "properties": {
                            "current": {
                              "type": "number"
                            },
                            "previous": {
                              "type": "number"
                            },
                            "change": {
                              "type": "number",
                              "description": "current − previous"
                            },
                            "changePct": {
                              "type": "number",
                              "nullable": true,
                              "description": "Relative change in percent (null when previous is 0)"
                            }
                          }
                        },
                        "repeatCustomers": {
                          "type": "object",
                          "description": "A metric for the range and the previous period of the same length",
                          "properties": {
                            "current": {
                              "type": "number"
                            },
                            "previous": {
                              "type": "number"
                            },
                            "change": {
                              "type": "number",
                              "description": "current − previous"
                            },
                            "changePct": {
                              "type": "number",
                              "nullable": true,
                              "description": "Relative change in percent (null when previous is 0)"
                            }
                          }
                        }
                      }
                    },
                    "series": {
                      "type": "object",
                      "properties": {
                        "current": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "day": {
                                "type": "string",
                                "example": "2026-09-14"
                              },
                              "revenue": {
                                "type": "number",
                                "description": "Revenue, in `currency` (major units, e.g. 12.5)"
                              },
                              "netRevenue": {
                                "type": "number",
                                "description": "Net revenue, in `currency` (major units, e.g. 12.5)"
                              },
                              "orders": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "previous": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "day": {
                                "type": "string",
                                "example": "2026-09-14"
                              },
                              "revenue": {
                                "type": "number",
                                "description": "Revenue, in `currency` (major units, e.g. 12.5)"
                              },
                              "netRevenue": {
                                "type": "number",
                                "description": "Net revenue, in `currency` (major units, e.g. 12.5)"
                              },
                              "orders": {
                                "type": "integer"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid `from` / `to` (not ISO dates, `from` after `to`, or a range longer than 732 days) or an invalid option",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "from must be before to"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this report (Growth and above). Body: `code: FEATURE_LOCKED`, `details.feature: advancedAnalytics`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "advancedAnalytics",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/analytics/advanced/funnel": {
      "get": {
        "operationId": "getSalesFunnel",
        "summary": "Get the sales funnel",
        "description": "Visits → product views → checkout started → paid, overall (with the previous period), per traffic source (first touch of the visit) and per product. Growth plan and above.",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-01T00:00:00.000Z"
            },
            "description": "Start of the range (ISO). Default: 30 days before `to`."
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-30T23:59:59.999Z"
            },
            "description": "End of the range (ISO; a bare date covers that whole day). Default: now. At most 732 days after `from`."
          },
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "minimum": -840,
              "maximum": 840,
              "example": -120
            },
            "description": "Your UTC offset as JavaScript's `Date#getTimezoneOffset()` (UTC − local, minutes) — days and months are bucketed in your local time. Default: 0 (UTC)."
          }
        ],
        "responses": {
          "200": {
            "description": "The funnel",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "range": {
                      "type": "object",
                      "properties": {
                        "from": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "to": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "days": {
                          "type": "integer"
                        },
                        "previous": {
                          "type": "object",
                          "description": "The comparison window: same length, right before `from`",
                          "properties": {
                            "from": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "to": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "visits": {
                          "type": "integer",
                          "description": "Visits (distinct visitor sessions)"
                        },
                        "productViews": {
                          "type": "integer",
                          "description": "Visits that viewed a product page or opened its checkout"
                        },
                        "checkouts": {
                          "type": "integer",
                          "description": "Visits that opened a checkout (checkout started)"
                        },
                        "paid": {
                          "type": "integer",
                          "description": "Paid orders (subscription renewals excluded)"
                        },
                        "revenue": {
                          "type": "number",
                          "description": "Net revenue of those orders (refunds excluded), in `currency` (major units, e.g. 12.5)"
                        },
                        "viewRate": {
                          "type": "number",
                          "description": "productViews ÷ visits, percent with one decimal"
                        },
                        "checkoutRate": {
                          "type": "number",
                          "description": "checkouts ÷ productViews, percent with one decimal"
                        },
                        "paidRate": {
                          "type": "number",
                          "description": "paid ÷ checkouts, percent with one decimal"
                        },
                        "conversionRate": {
                          "type": "number",
                          "description": "paid ÷ visits, percent with one decimal"
                        }
                      }
                    },
                    "previous": {
                      "type": "object",
                      "properties": {
                        "visits": {
                          "type": "integer",
                          "description": "Visits (distinct visitor sessions)"
                        },
                        "productViews": {
                          "type": "integer",
                          "description": "Visits that viewed a product page or opened its checkout"
                        },
                        "checkouts": {
                          "type": "integer",
                          "description": "Visits that opened a checkout (checkout started)"
                        },
                        "paid": {
                          "type": "integer",
                          "description": "Paid orders (subscription renewals excluded)"
                        },
                        "revenue": {
                          "type": "number",
                          "description": "Net revenue of those orders (refunds excluded), in `currency` (major units, e.g. 12.5)"
                        },
                        "viewRate": {
                          "type": "number",
                          "description": "productViews ÷ visits, percent with one decimal"
                        },
                        "checkoutRate": {
                          "type": "number",
                          "description": "checkouts ÷ productViews, percent with one decimal"
                        },
                        "paidRate": {
                          "type": "number",
                          "description": "paid ÷ checkouts, percent with one decimal"
                        },
                        "conversionRate": {
                          "type": "number",
                          "description": "paid ÷ visits, percent with one decimal"
                        }
                      }
                    },
                    "bySource": {
                      "type": "array",
                      "description": "Top 50 sources by visits, plus any source with paid orders",
                      "items": {
                        "$ref": "#/components/schemas/AnalyticsFunnelRow"
                      }
                    },
                    "byProduct": {
                      "type": "array",
                      "description": "Per product / subscription offer; a product's `visits` are the visits that reached it",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/AnalyticsFunnelRow"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "name": {
                                "type": "string",
                                "nullable": true
                              },
                              "type": {
                                "type": "string",
                                "nullable": true,
                                "enum": [
                                  "product",
                                  "subscription"
                                ]
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid `from` / `to` (not ISO dates, `from` after `to`, or a range longer than 732 days) or an invalid option",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "from must be before to"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this report (Growth and above). Body: `code: FEATURE_LOCKED`, `details.feature: advancedAnalytics`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "advancedAnalytics",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/analytics/advanced/sources": {
      "get": {
        "operationId": "getRevenueBySource",
        "summary": "Get revenue by source or UTM tag",
        "description": "Paid orders and net revenue per traffic source, `utm_source`, `utm_medium` or `utm_campaign`, with each key's net revenue in the previous period. A subscription renewal counts for the source of the subscriber's first payment. Growth plan and above.",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-01T00:00:00.000Z"
            },
            "description": "Start of the range (ISO). Default: 30 days before `to`."
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-30T23:59:59.999Z"
            },
            "description": "End of the range (ISO; a bare date covers that whole day). Default: now. At most 732 days after `from`."
          },
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "minimum": -840,
              "maximum": 840,
              "example": -120
            },
            "description": "Your UTC offset as JavaScript's `Date#getTimezoneOffset()` (UTC − local, minutes) — days and months are bucketed in your local time. Default: 0 (UTC)."
          },
          {
            "in": "query",
            "name": "dimension",
            "schema": {
              "type": "string",
              "enum": [
                "source",
                "utm_source",
                "utm_medium",
                "utm_campaign"
              ],
              "default": "source"
            },
            "description": "What to group by. Orders without that UTM tag are grouped under `none`."
          }
        ],
        "responses": {
          "200": {
            "description": "Revenue per key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "range": {
                      "type": "object",
                      "properties": {
                        "from": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "to": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "days": {
                          "type": "integer"
                        },
                        "previous": {
                          "type": "object",
                          "description": "The comparison window: same length, right before `from`",
                          "properties": {
                            "from": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "to": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    },
                    "dimension": {
                      "type": "string"
                    },
                    "rows": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string",
                            "example": "newsletter"
                          },
                          "orders": {
                            "type": "integer"
                          },
                          "revenue": {
                            "type": "number",
                            "description": "Revenue, in `currency` (major units, e.g. 12.5)"
                          },
                          "refunds": {
                            "type": "number",
                            "description": "Refunded, in `currency` (major units, e.g. 12.5)"
                          },
                          "netRevenue": {
                            "type": "number",
                            "description": "Net revenue, in `currency` (major units, e.g. 12.5)"
                          },
                          "share": {
                            "type": "number",
                            "description": "Share of the range's net revenue, percent with one decimal"
                          },
                          "previousNetRevenue": {
                            "type": "number",
                            "description": "Net revenue in the previous period, in `currency` (major units, e.g. 12.5)"
                          }
                        }
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "orders": {
                          "type": "integer"
                        },
                        "revenue": {
                          "type": "number",
                          "description": "Revenue, in `currency` (major units, e.g. 12.5)"
                        },
                        "netRevenue": {
                          "type": "number",
                          "description": "Net revenue, in `currency` (major units, e.g. 12.5)"
                        },
                        "unattributedOrders": {
                          "type": "integer",
                          "description": "Orders recorded before attribution existed"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid `from` / `to` (not ISO dates, `from` after `to`, or a range longer than 732 days) or an invalid option",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "from must be before to"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this report (Growth and above). Body: `code: FEATURE_LOCKED`, `details.feature: advancedAnalytics`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "advancedAnalytics",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/analytics/advanced/cohorts": {
      "get": {
        "operationId": "getRepeatBuyerCohorts",
        "summary": "Get repeat-buyer cohorts",
        "description": "Buyers grouped by the month of their first order; for each later month, the share who ordered again. Refunded orders and subscription renewals are not purchases here. Growth plan and above.",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "minimum": -840,
              "maximum": 840,
              "example": -120
            },
            "description": "Your UTC offset as JavaScript's `Date#getTimezoneOffset()` (UTC − local, minutes) — days and months are bucketed in your local time. Default: 0 (UTC)."
          },
          {
            "in": "query",
            "name": "months",
            "schema": {
              "type": "integer",
              "enum": [
                3,
                6,
                12
              ],
              "default": 6
            },
            "description": "How many monthly cohorts, ending with the current month"
          }
        ],
        "responses": {
          "200": {
            "description": "Cohort table",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "months": {
                      "type": "integer"
                    },
                    "startMonth": {
                      "type": "string",
                      "example": "2026-05"
                    },
                    "currentMonth": {
                      "type": "string",
                      "example": "2026-10"
                    },
                    "cohorts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "cohort": {
                            "type": "string",
                            "example": "2026-05"
                          },
                          "buyers": {
                            "type": "integer"
                          },
                          "returning": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "Buyers who ordered k months after their first order"
                          },
                          "retention": {
                            "type": "array",
                            "items": {
                              "type": "number"
                            },
                            "description": "returning[k] ÷ buyers, percent"
                          }
                        }
                      }
                    },
                    "repeatRate": {
                      "type": "number",
                      "description": "Buyers who ordered in two or more months, percent with one decimal"
                    },
                    "buyers": {
                      "type": "integer"
                    },
                    "repeatBuyers": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid `from` / `to` (not ISO dates, `from` after `to`, or a range longer than 732 days) or an invalid option",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "from must be before to"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this report (Growth and above). Body: `code: FEATURE_LOCKED`, `details.feature: advancedAnalytics`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "advancedAnalytics",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/analytics/advanced/net-revenue": {
      "get": {
        "operationId": "getNetRevenue",
        "summary": "Get net revenue after refunds",
        "description": "Gross revenue, refunded and net revenue per product, buyer country or month. A refund counts against the order's own date. Growth plan and above.",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-01T00:00:00.000Z"
            },
            "description": "Start of the range (ISO). Default: 30 days before `to`."
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-30T23:59:59.999Z"
            },
            "description": "End of the range (ISO; a bare date covers that whole day). Default: now. At most 732 days after `from`."
          },
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "minimum": -840,
              "maximum": 840,
              "example": -120
            },
            "description": "Your UTC offset as JavaScript's `Date#getTimezoneOffset()` (UTC − local, minutes) — days and months are bucketed in your local time. Default: 0 (UTC)."
          },
          {
            "in": "query",
            "name": "groupBy",
            "schema": {
              "type": "string",
              "enum": [
                "product",
                "country",
                "month"
              ],
              "default": "product"
            },
            "description": "How to break the revenue down"
          }
        ],
        "responses": {
          "200": {
            "description": "Net revenue rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "range": {
                      "type": "object",
                      "properties": {
                        "from": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "to": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "days": {
                          "type": "integer"
                        },
                        "previous": {
                          "type": "object",
                          "description": "The comparison window: same length, right before `from`",
                          "properties": {
                            "from": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "to": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    },
                    "groupBy": {
                      "type": "string"
                    },
                    "rows": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string",
                            "description": "Product / subscription id, ISO country code (`unknown` when not known) or `YYYY-MM`"
                          },
                          "name": {
                            "type": "string",
                            "nullable": true
                          },
                          "orders": {
                            "type": "integer"
                          },
                          "refunds": {
                            "type": "integer"
                          },
                          "grossRevenue": {
                            "type": "number",
                            "description": "Gross revenue, in `currency` (major units, e.g. 12.5)"
                          },
                          "refunded": {
                            "type": "number",
                            "description": "Refunded, in `currency` (major units, e.g. 12.5)"
                          },
                          "netRevenue": {
                            "type": "number",
                            "description": "Net revenue, in `currency` (major units, e.g. 12.5)"
                          },
                          "refundRate": {
                            "type": "number",
                            "description": "Refunded orders ÷ orders, percent with one decimal"
                          }
                        }
                      }
                    },
                    "totals": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid `from` / `to` (not ISO dates, `from` after `to`, or a range longer than 732 days) or an invalid option",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "from must be before to"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this report (Growth and above). Body: `code: FEATURE_LOCKED`, `details.feature: advancedAnalytics`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "advancedAnalytics",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/analytics/mrr": {
      "get": {
        "operationId": "getMrr",
        "summary": "Get MRR report",
        "description": "MRR, new / expansion / contraction / churned MRR, subscribers, logo and revenue churn, ARPU, LTV and trial conversions for the range (with the previous period), a daily series and one row per subscription offer. Computed from your subscription payments: each paid period counts at its monthly value (excluding tax) until it ends without a renewal (plus a short grace for retries). Past days come from a daily snapshot and never change. Days are UTC. Pro plan and above.",
        "tags": [
          "Analytics"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "from",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-01T00:00:00.000Z"
            },
            "description": "Start of the range (ISO). Default: 30 days before `to`."
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "type": "string",
              "format": "date-time",
              "example": "2026-09-30T23:59:59.999Z"
            },
            "description": "End of the range (ISO; a bare date covers that whole day). Default: now. At most 732 days after `from`."
          },
          {
            "in": "query",
            "name": "subscriptionId",
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Only this subscription offer (404 when it is not yours)"
          }
        ],
        "responses": {
          "200": {
            "description": "MRR report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "range": {
                      "type": "object",
                      "properties": {
                        "from": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "to": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "days": {
                          "type": "integer"
                        },
                        "previous": {
                          "type": "object",
                          "description": "The comparison window: same length, right before `from`",
                          "properties": {
                            "from": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "to": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    },
                    "subscriptionId": {
                      "type": "string",
                      "nullable": true
                    },
                    "hasTrials": {
                      "type": "boolean"
                    },
                    "summary": {
                      "$ref": "#/components/schemas/MrrSummary"
                    },
                    "previous": {
                      "$ref": "#/components/schemas/MrrSummary"
                    },
                    "series": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "day": {
                            "type": "string",
                            "example": "2026-09-14"
                          },
                          "mrr": {
                            "type": "number",
                            "description": "MRR at the end of the day, in `currency` (major units, e.g. 12.5)"
                          },
                          "subscribers": {
                            "type": "integer"
                          },
                          "trialing": {
                            "type": "integer"
                          },
                          "newMrr": {
                            "type": "number",
                            "description": "New MRR, in `currency` (major units, e.g. 12.5)"
                          },
                          "expansionMrr": {
                            "type": "number",
                            "description": "Expansion MRR, in `currency` (major units, e.g. 12.5)"
                          },
                          "contractionMrr": {
                            "type": "number",
                            "description": "Contraction MRR, in `currency` (major units, e.g. 12.5)"
                          },
                          "churnedMrr": {
                            "type": "number",
                            "description": "Churned MRR, in `currency` (major units, e.g. 12.5)"
                          },
                          "netMrr": {
                            "type": "number",
                            "description": "Net MRR movement, in `currency` (major units, e.g. 12.5)"
                          }
                        }
                      }
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/MrrSummary"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "subscriptionId": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string",
                                "nullable": true
                              },
                              "frequency": {
                                "type": "string",
                                "enum": [
                                  "weekly",
                                  "monthly",
                                  "yearly"
                                ]
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid `from` / `to` (not ISO dates, `from` after `to`, or a range longer than 732 days) or an invalid option",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "from must be before to"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this report (Pro and above). Body: `code: FEATURE_LOCKED`, `details.feature: mrrReports`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "mrrReports",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro"
                  }
                }
              }
            }
          },
          "404": {
            "description": "`subscriptionId` is not one of your subscription offers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Subscription not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/segments": {
      "get": {
        "operationId": "listSegments",
        "summary": "List segments",
        "description": "Your saved audience segments, newest first, paginated.",
        "tags": [
          "Segments"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number"
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Segments per page (maximum 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of segments",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Segment"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include audience segments (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan: tier2`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createSegment",
        "summary": "Create a segment",
        "description": "Save a segment. The answer carries its first `lastCount`. Product ids in rules must be your own (`400` otherwise).",
        "tags": [
          "Segments"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "definition"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80,
                    "example": "VIP customers"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 280
                  },
                  "definition": {
                    "type": "object",
                    "required": [
                      "groups"
                    ],
                    "description": "Rules in groups. Rules inside a group combine with the group's `match`; groups combine with the top-level `match` (`all` = AND, `any` = OR). Up to 10 groups of 20 rules.",
                    "properties": {
                      "match": {
                        "type": "string",
                        "enum": [
                          "all",
                          "any"
                        ],
                        "default": "all"
                      },
                      "groups": {
                        "type": "array",
                        "minItems": 1,
                        "maxItems": 10,
                        "items": {
                          "type": "object",
                          "required": [
                            "rules"
                          ],
                          "properties": {
                            "match": {
                              "type": "string",
                              "enum": [
                                "all",
                                "any"
                              ],
                              "default": "all"
                            },
                            "rules": {
                              "type": "array",
                              "minItems": 1,
                              "maxItems": 20,
                              "items": {
                                "type": "object",
                                "required": [
                                  "type"
                                ],
                                "description": "One condition. `type` picks which other fields apply: `bought` / `notBought` (`productId`), `spent` (`op` gt|lt, `amountCents` in your account currency), `boughtWithin` / `notBoughtWithin` (`days`), `engagedWithin` (`days`, `event` opened|clicked|any), `tag` / `notTag` (`tag`), `country` (`countries`, ISO 3166-1 alpha-2), `source` (`source`).",
                                "properties": {
                                  "type": {
                                    "type": "string",
                                    "enum": [
                                      "bought",
                                      "notBought",
                                      "spent",
                                      "boughtWithin",
                                      "notBoughtWithin",
                                      "engagedWithin",
                                      "tag",
                                      "notTag",
                                      "country",
                                      "source"
                                    ]
                                  },
                                  "productId": {
                                    "type": "string",
                                    "description": "One of your products or subscription offers"
                                  },
                                  "op": {
                                    "type": "string",
                                    "enum": [
                                      "gt",
                                      "lt"
                                    ],
                                    "default": "gt"
                                  },
                                  "amountCents": {
                                    "type": "integer",
                                    "minimum": 0,
                                    "example": 20000
                                  },
                                  "days": {
                                    "type": "integer",
                                    "minimum": 1,
                                    "maximum": 3650,
                                    "example": 90
                                  },
                                  "event": {
                                    "type": "string",
                                    "enum": [
                                      "opened",
                                      "clicked",
                                      "any"
                                    ],
                                    "default": "any"
                                  },
                                  "tag": {
                                    "type": "string",
                                    "example": "vip"
                                  },
                                  "countries": {
                                    "type": "array",
                                    "items": {
                                      "type": "string",
                                      "example": "FR"
                                    }
                                  },
                                  "source": {
                                    "type": "string",
                                    "enum": [
                                      "form",
                                      "checkout",
                                      "import",
                                      "dashboard",
                                      "api",
                                      "recommendation"
                                    ],
                                    "description": "How the contact joined: your store's form, a purchase, a CSV import, added by hand, or the API"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "example": {
                      "match": "any",
                      "groups": [
                        {
                          "match": "all",
                          "rules": [
                            {
                              "type": "bought",
                              "productId": "665f1c2a9b3e4a0012ab34cd"
                            },
                            {
                              "type": "spent",
                              "op": "gt",
                              "amountCents": 20000
                            }
                          ]
                        },
                        {
                          "rules": [
                            {
                              "type": "tag",
                              "tag": "vip"
                            }
                          ]
                        }
                      ]
                    }
                  },
                  "smartTag": {
                    "type": "string",
                    "nullable": true,
                    "description": "Optional smart tag (lowercase, up to 32 characters): every member carries it, refreshed nightly, and contacts who leave the segment lose it (only where it was added by this segment). `null` turns it off.",
                    "example": "vip"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created segment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Segment"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include audience segments (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan: tier2`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "409": {
            "description": "Another segment already uses this smart tag",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/segments/preview": {
      "post": {
        "operationId": "previewSegment",
        "summary": "Count an unsaved segment",
        "description": "How many active contacts match a definition right now, with up to 5 of them. Nothing is saved.",
        "tags": [
          "Segments"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "definition"
                ],
                "properties": {
                  "definition": {
                    "type": "object",
                    "required": [
                      "groups"
                    ],
                    "description": "Rules in groups. Rules inside a group combine with the group's `match`; groups combine with the top-level `match` (`all` = AND, `any` = OR). Up to 10 groups of 20 rules.",
                    "properties": {
                      "match": {
                        "type": "string",
                        "enum": [
                          "all",
                          "any"
                        ],
                        "default": "all"
                      },
                      "groups": {
                        "type": "array",
                        "minItems": 1,
                        "maxItems": 10,
                        "items": {
                          "type": "object",
                          "required": [
                            "rules"
                          ],
                          "properties": {
                            "match": {
                              "type": "string",
                              "enum": [
                                "all",
                                "any"
                              ],
                              "default": "all"
                            },
                            "rules": {
                              "type": "array",
                              "minItems": 1,
                              "maxItems": 20,
                              "items": {
                                "type": "object",
                                "required": [
                                  "type"
                                ],
                                "description": "One condition. `type` picks which other fields apply: `bought` / `notBought` (`productId`), `spent` (`op` gt|lt, `amountCents` in your account currency), `boughtWithin` / `notBoughtWithin` (`days`), `engagedWithin` (`days`, `event` opened|clicked|any), `tag` / `notTag` (`tag`), `country` (`countries`, ISO 3166-1 alpha-2), `source` (`source`).",
                                "properties": {
                                  "type": {
                                    "type": "string",
                                    "enum": [
                                      "bought",
                                      "notBought",
                                      "spent",
                                      "boughtWithin",
                                      "notBoughtWithin",
                                      "engagedWithin",
                                      "tag",
                                      "notTag",
                                      "country",
                                      "source"
                                    ]
                                  },
                                  "productId": {
                                    "type": "string",
                                    "description": "One of your products or subscription offers"
                                  },
                                  "op": {
                                    "type": "string",
                                    "enum": [
                                      "gt",
                                      "lt"
                                    ],
                                    "default": "gt"
                                  },
                                  "amountCents": {
                                    "type": "integer",
                                    "minimum": 0,
                                    "example": 20000
                                  },
                                  "days": {
                                    "type": "integer",
                                    "minimum": 1,
                                    "maximum": 3650,
                                    "example": 90
                                  },
                                  "event": {
                                    "type": "string",
                                    "enum": [
                                      "opened",
                                      "clicked",
                                      "any"
                                    ],
                                    "default": "any"
                                  },
                                  "tag": {
                                    "type": "string",
                                    "example": "vip"
                                  },
                                  "countries": {
                                    "type": "array",
                                    "items": {
                                      "type": "string",
                                      "example": "FR"
                                    }
                                  },
                                  "source": {
                                    "type": "string",
                                    "enum": [
                                      "form",
                                      "checkout",
                                      "import",
                                      "dashboard",
                                      "api",
                                      "recommendation"
                                    ],
                                    "description": "How the contact joined: your store's form, a purchase, a CSV import, added by hand, or the API"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "example": {
                      "match": "any",
                      "groups": [
                        {
                          "match": "all",
                          "rules": [
                            {
                              "type": "bought",
                              "productId": "665f1c2a9b3e4a0012ab34cd"
                            },
                            {
                              "type": "spent",
                              "op": "gt",
                              "amountCents": 20000
                            }
                          ]
                        },
                        {
                          "rules": [
                            {
                              "type": "tag",
                              "tag": "vip"
                            }
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The live count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "sample": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "_id": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "computedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include audience segments (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan: tier2`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/segments/{id}": {
      "get": {
        "operationId": "getSegment",
        "summary": "Get a segment",
        "description": "One of your segments.",
        "tags": [
          "Segments"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Segment id"
          }
        ],
        "responses": {
          "200": {
            "description": "The segment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Segment"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include audience segments (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan: tier2`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "updateSegment",
        "summary": "Update a segment",
        "description": "Change the name, description, rules or smart tag. Omitted fields stay as they are.",
        "tags": [
          "Segments"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Segment id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80,
                    "example": "VIP customers"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 280
                  },
                  "definition": {
                    "type": "object",
                    "required": [
                      "groups"
                    ],
                    "description": "Rules in groups. Rules inside a group combine with the group's `match`; groups combine with the top-level `match` (`all` = AND, `any` = OR). Up to 10 groups of 20 rules.",
                    "properties": {
                      "match": {
                        "type": "string",
                        "enum": [
                          "all",
                          "any"
                        ],
                        "default": "all"
                      },
                      "groups": {
                        "type": "array",
                        "minItems": 1,
                        "maxItems": 10,
                        "items": {
                          "type": "object",
                          "required": [
                            "rules"
                          ],
                          "properties": {
                            "match": {
                              "type": "string",
                              "enum": [
                                "all",
                                "any"
                              ],
                              "default": "all"
                            },
                            "rules": {
                              "type": "array",
                              "minItems": 1,
                              "maxItems": 20,
                              "items": {
                                "type": "object",
                                "required": [
                                  "type"
                                ],
                                "description": "One condition. `type` picks which other fields apply: `bought` / `notBought` (`productId`), `spent` (`op` gt|lt, `amountCents` in your account currency), `boughtWithin` / `notBoughtWithin` (`days`), `engagedWithin` (`days`, `event` opened|clicked|any), `tag` / `notTag` (`tag`), `country` (`countries`, ISO 3166-1 alpha-2), `source` (`source`).",
                                "properties": {
                                  "type": {
                                    "type": "string",
                                    "enum": [
                                      "bought",
                                      "notBought",
                                      "spent",
                                      "boughtWithin",
                                      "notBoughtWithin",
                                      "engagedWithin",
                                      "tag",
                                      "notTag",
                                      "country",
                                      "source"
                                    ]
                                  },
                                  "productId": {
                                    "type": "string",
                                    "description": "One of your products or subscription offers"
                                  },
                                  "op": {
                                    "type": "string",
                                    "enum": [
                                      "gt",
                                      "lt"
                                    ],
                                    "default": "gt"
                                  },
                                  "amountCents": {
                                    "type": "integer",
                                    "minimum": 0,
                                    "example": 20000
                                  },
                                  "days": {
                                    "type": "integer",
                                    "minimum": 1,
                                    "maximum": 3650,
                                    "example": 90
                                  },
                                  "event": {
                                    "type": "string",
                                    "enum": [
                                      "opened",
                                      "clicked",
                                      "any"
                                    ],
                                    "default": "any"
                                  },
                                  "tag": {
                                    "type": "string",
                                    "example": "vip"
                                  },
                                  "countries": {
                                    "type": "array",
                                    "items": {
                                      "type": "string",
                                      "example": "FR"
                                    }
                                  },
                                  "source": {
                                    "type": "string",
                                    "enum": [
                                      "form",
                                      "checkout",
                                      "import",
                                      "dashboard",
                                      "api",
                                      "recommendation"
                                    ],
                                    "description": "How the contact joined: your store's form, a purchase, a CSV import, added by hand, or the API"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "example": {
                      "match": "any",
                      "groups": [
                        {
                          "match": "all",
                          "rules": [
                            {
                              "type": "bought",
                              "productId": "665f1c2a9b3e4a0012ab34cd"
                            },
                            {
                              "type": "spent",
                              "op": "gt",
                              "amountCents": 20000
                            }
                          ]
                        },
                        {
                          "rules": [
                            {
                              "type": "tag",
                              "tag": "vip"
                            }
                          ]
                        }
                      ]
                    }
                  },
                  "smartTag": {
                    "type": "string",
                    "nullable": true,
                    "description": "Optional smart tag (lowercase, up to 32 characters): every member carries it, refreshed nightly, and contacts who leave the segment lose it (only where it was added by this segment). `null` turns it off.",
                    "example": "vip"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated segment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Segment"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include audience segments (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan: tier2`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteSegment",
        "summary": "Delete a segment",
        "description": "Delete a segment. Its smart tag is taken back from the contacts it was added to. Posts that targeted it no longer have an audience.",
        "tags": [
          "Segments"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Segment id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    },
                    "_id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include audience segments (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan: tier2`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/segments/{id}/count": {
      "get": {
        "operationId": "getSegmentCount",
        "summary": "Count a segment",
        "description": "How many active contacts are in the segment right now.",
        "tags": [
          "Segments"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Segment id"
          }
        ],
        "responses": {
          "200": {
            "description": "The count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "segmentId": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer",
                      "description": "Active (emailable) contacts in the segment now"
                    },
                    "computedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include audience segments (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan: tier2`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/segments/{id}/subscribers": {
      "get": {
        "operationId": "listSegmentSubscribers",
        "summary": "List a segment's contacts",
        "description": "Active contacts in the segment, newest first. Cursor-paginated: pass `nextCursor` as `after` for the next page.",
        "tags": [
          "Segments"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Segment id"
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Contacts per page (maximum 100)"
          },
          {
            "in": "query",
            "name": "after",
            "schema": {
              "type": "string"
            },
            "description": "The `nextCursor` of the previous page"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of contacts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EmailSubscriber"
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include audience segments (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan: tier2`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/customers/profiles": {
      "get": {
        "operationId": "listCustomerProfiles",
        "summary": "List best customers",
        "description": "Your buyers sorted by lifetime value (default), number of orders or last purchase, highest first. Filter by CRM tag, email prefix, minimum lifetime value or minimum orders. Cursor-paginated: pass `nextCursor` as `after`.",
        "tags": [
          "Customer CRM"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "sort",
            "schema": {
              "type": "string",
              "enum": [
                "ltv",
                "orders",
                "lastPurchase"
              ],
              "default": "ltv"
            },
            "description": "What to sort by (always highest first)"
          },
          {
            "in": "query",
            "name": "tag",
            "schema": {
              "type": "string",
              "example": "vip"
            },
            "description": "Only buyers with this CRM tag"
          },
          {
            "in": "query",
            "name": "q",
            "schema": {
              "type": "string",
              "example": "ada@"
            },
            "description": "Only buyers whose email starts with this"
          },
          {
            "in": "query",
            "name": "minLtvCents",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "example": 10000
            },
            "description": "Only buyers with at least this lifetime value (cents)"
          },
          {
            "in": "query",
            "name": "minOrders",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "example": 2
            },
            "description": "Only buyers with at least this many orders"
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            },
            "description": "Buyers per page (maximum 100)"
          },
          {
            "in": "query",
            "name": "after",
            "schema": {
              "type": "string"
            },
            "description": "The `nextCursor` of the previous page"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of buyers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustomerProfileSummary"
                      }
                    },
                    "sort": {
                      "type": "string",
                      "enum": [
                        "ltv",
                        "orders",
                        "lastPurchase"
                      ]
                    },
                    "currency": {
                      "type": "string"
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — the customer CRM needs the Pro plan or higher (feature `customerCrm`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "customerCrm",
                    "currentPlan": "tier2",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/customers/profiles/tags": {
      "get": {
        "operationId": "listCustomerTags",
        "summary": "List customer tags",
        "description": "Every CRM tag used on your buyers, alphabetically.",
        "tags": [
          "Customer CRM"
        ],
        "responses": {
          "200": {
            "description": "The tags",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — the customer CRM needs the Pro plan or higher (feature `customerCrm`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "customerCrm",
                    "currentPlan": "tier2",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/customers/{id}/profile": {
      "get": {
        "operationId": "getCustomerProfile",
        "summary": "Get a customer profile",
        "description": "One buyer on one page: lifetime value (net of refunds, in your account currency), orders timeline, products owned, subscriptions and their status, emails received / opened / clicked, segments, tags and private notes.",
        "tags": [
          "Customer CRM"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "A customer id (`GET /customers`) or a customer profile id (`GET /customers/profiles`)"
          }
        ],
        "responses": {
          "200": {
            "description": "The profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerProfile"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — the customer CRM needs the Pro plan or higher (feature `customerCrm`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "customerCrm",
                    "currentPlan": "tier2",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/customers/{id}/notes": {
      "get": {
        "operationId": "listCustomerNotes",
        "summary": "List customer notes",
        "description": "A buyer's private notes, newest first.",
        "tags": [
          "Customer CRM"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "A customer id (`GET /customers`) or a customer profile id (`GET /customers/profiles`)"
          }
        ],
        "responses": {
          "200": {
            "description": "The notes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustomerNote"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — the customer CRM needs the Pro plan or higher (feature `customerCrm`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "customerCrm",
                    "currentPlan": "tier2",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "addCustomerNote",
        "summary": "Add a customer note",
        "description": "Add a private note (up to 2,000 characters, 200 notes per buyer). The author is recorded: the store owner, the team member who wrote it, or `api` for an API key. Buyers never see notes.",
        "tags": [
          "Customer CRM"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "A customer id (`GET /customers`) or a customer profile id (`GET /customers/profiles`)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "body"
                ],
                "properties": {
                  "body": {
                    "type": "string",
                    "maxLength": 2000,
                    "example": "Asked for an invoice with their VAT number."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The note",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerNote"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — the customer CRM needs the Pro plan or higher (feature `customerCrm`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "customerCrm",
                    "currentPlan": "tier2",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/customers/{id}/notes/{noteId}": {
      "delete": {
        "operationId": "deleteCustomerNote",
        "summary": "Delete a customer note",
        "description": "Delete a note. Team members can delete their own notes; owners and admins any note.",
        "tags": [
          "Customer CRM"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "A customer id (`GET /customers`) or a customer profile id (`GET /customers/profiles`)"
          },
          {
            "in": "path",
            "name": "noteId",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Note id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    },
                    "_id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` (the CRM needs the Pro plan or higher), or a team member deleting someone else's note",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/customers/{id}/tags": {
      "put": {
        "operationId": "setCustomerTags",
        "summary": "Set customer tags",
        "description": "Replace the buyer's CRM tags (up to 20, each up to 40 characters; lower-cased and deduplicated). Send `[]` to clear them. CRM tags label buyers in the dashboard and filter `GET /customers/profiles`; they are separate from email contact tags.",
        "tags": [
          "Customer CRM"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "A customer id (`GET /customers`) or a customer profile id (`GET /customers/profiles`)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tags"
                ],
                "properties": {
                  "tags": {
                    "type": "array",
                    "maxItems": 20,
                    "items": {
                      "type": "string",
                      "maxLength": 40
                    },
                    "example": [
                      "vip",
                      "agency"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved tags",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — the customer CRM needs the Pro plan or higher (feature `customerCrm`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "customerCrm",
                    "currentPlan": "tier2",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/benchmarks": {
      "get": {
        "operationId": "getBenchmarks",
        "summary": "Get benchmarks",
        "description": "Your last-90-day conversion rate, average order value, refund rate and email open rate next to the 25th percentile, median and 75th percentile of similar stores, with one suggestion for each metric below the median.",
        "tags": [
          "Benchmarks"
        ],
        "responses": {
          "200": {
            "description": "Your benchmarks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Benchmarks"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — benchmarks need the Pro plan or higher (feature `benchmarks`). The creator upgrades at https://app.pocketsflow.com/profile/billing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "benchmarks",
                    "currentPlan": "tier2",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/search": {
      "get": {
        "operationId": "searchStore",
        "summary": "Search your store",
        "description": "Search orders, customers, products and subscription offers, subscribers and discounts at once. Each type returns up to `limit` hits, newest first (an id match always comes first). Queries of one character return empty groups.",
        "tags": [
          "Search"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "What to look for: an email, a name, a code, a product name, an id (`#6711A3D5…` or its first 6+ characters) or an amount (`49`, `49.99`). Cut to 100 characters.",
            "schema": {
              "type": "string",
              "maxLength": 100,
              "example": "ana"
            }
          },
          {
            "name": "types",
            "in": "query",
            "required": false,
            "description": "Comma-separated result types (default: all of them)",
            "schema": {
              "type": "string",
              "example": "orders,customers"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Hits per type, 1 to 20",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Grouped results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResults"
                },
                "example": {
                  "query": "ana",
                  "testMode": false,
                  "limit": 5,
                  "groups": [
                    {
                      "type": "orders",
                      "hasMore": false,
                      "hits": [
                        {
                          "id": "6711a3d59e3f1a0012cd7788",
                          "type": "orders",
                          "title": "Ana Lopez",
                          "subtitle": "Notion course · ana@example.com · #12CD7788",
                          "meta": {
                            "amount": 49,
                            "currency": "USD",
                            "status": "paid",
                            "date": "2026-10-12T09:30:00.000Z"
                          },
                          "path": "/orders-one-time/6711a3d59e3f1a0012cd7788",
                          "image": null,
                          "match": "email"
                        }
                      ]
                    },
                    {
                      "type": "discounts",
                      "hasMore": false,
                      "hits": [
                        {
                          "id": "6711a3d59e3f1a0012cd7701",
                          "type": "discounts",
                          "title": "ANA20",
                          "subtitle": "Ana's friends",
                          "meta": {
                            "value": 20,
                            "valueType": "percentage",
                            "status": "active",
                            "date": "2026-10-01T10:00:00.000Z"
                          },
                          "path": "/discounts/6711a3d59e3f1a0012cd7701",
                          "image": null,
                          "match": "code"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`q` is missing, or `types` names an unknown type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "q is required."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscriptions/{id}/usage-pricing": {
      "put": {
        "operationId": "updateSubscriptionUsagePricing",
        "summary": "Set seats and meters on a subscription offer",
        "description": "Replace the per-seat and metered pricing of one of your subscription OFFERS. Meter prices are snapshotted when each billing period opens, so a price change applies from the next period. Turning pricing off is always allowed; turning it on needs the Pro plan.",
        "tags": [
          "Usage billing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Subscription offer id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "seats": {
                    "type": "object",
                    "properties": {
                      "enabled": {
                        "type": "boolean",
                        "description": "Price per seat: the buyer picks seats at checkout and pays price × seats each period"
                      },
                      "minSeats": {
                        "type": "integer",
                        "minimum": 1,
                        "default": 1
                      },
                      "maxSeats": {
                        "type": "integer",
                        "nullable": true,
                        "minimum": 1,
                        "maximum": 10000,
                        "description": "`null` = no maximum"
                      },
                      "licenseKeys": {
                        "type": "boolean",
                        "description": "Issue each subscriber a license key whose activation limit follows their seats"
                      }
                    }
                  },
                  "meters": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "type": "object",
                      "required": [
                        "key",
                        "name",
                        "pricing"
                      ],
                      "properties": {
                        "key": {
                          "type": "string",
                          "pattern": "^[a-z0-9][a-z0-9_.-]{0,39}$",
                          "description": "The meter's id — what you send as `meter` when reporting usage",
                          "example": "api_calls"
                        },
                        "name": {
                          "type": "string",
                          "maxLength": 60,
                          "example": "API calls"
                        },
                        "unit": {
                          "type": "string",
                          "maxLength": 30,
                          "description": "Singular unit name shown to buyers",
                          "example": "call"
                        },
                        "pricing": {
                          "type": "string",
                          "enum": [
                            "per_unit",
                            "graduated",
                            "volume"
                          ],
                          "description": "`per_unit`: every billable unit at `unitAmountCents`. `graduated`: each tier prices the units inside it. `volume`: every billable unit at the rate of the tier the total reaches."
                        },
                        "unitAmountCents": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "`per_unit` only: cents per `packageSize` units (`100` with `packageSize: 1000` = $1 per 1,000)",
                          "example": 100
                        },
                        "packageSize": {
                          "type": "integer",
                          "minimum": 1,
                          "default": 1,
                          "description": "Units one price applies to, so sub-cent unit prices stay exact. Each line is rounded half up to the cent.",
                          "example": 1000
                        },
                        "includedUnits": {
                          "type": "integer",
                          "minimum": 0,
                          "default": 0,
                          "description": "Free units per billing period before anything is charged",
                          "example": 10000
                        },
                        "tiers": {
                          "type": "array",
                          "maxItems": 10,
                          "items": {
                            "type": "object",
                            "required": [
                              "upTo",
                              "unitAmountCents"
                            ],
                            "properties": {
                              "upTo": {
                                "type": "integer",
                                "nullable": true,
                                "minimum": 1,
                                "description": "Inclusive upper bound in billable units (after included units). `null` on the last tier."
                              },
                              "unitAmountCents": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Cents per `packageSize` units in this tier"
                              },
                              "flatAmountCents": {
                                "type": "integer",
                                "minimum": 0,
                                "default": 0,
                                "description": "Flat cents added when any unit lands in this tier"
                              }
                            }
                          },
                          "description": "`graduated` / `volume` only; ascending `upTo`, last one `null`"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "seats": {
                  "enabled": true,
                  "minSeats": 1,
                  "maxSeats": 50
                },
                "meters": [
                  {
                    "key": "api_calls",
                    "name": "API calls",
                    "unit": "call",
                    "pricing": "graduated",
                    "packageSize": 1000,
                    "includedUnits": 10000,
                    "tiers": [
                      {
                        "upTo": 100000,
                        "unitAmountCents": 100
                      },
                      {
                        "upTo": null,
                        "unitAmountCents": 50
                      }
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved usage pricing",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriptionId": {
                      "type": "string"
                    },
                    "usagePricing": {
                      "type": "object",
                      "description": "Seats and meters of a subscription offer. Send `{ \"seats\": { \"enabled\": false }, \"meters\": [] }` to switch back to flat pricing (always allowed).",
                      "properties": {
                        "seats": {
                          "type": "object",
                          "properties": {
                            "enabled": {
                              "type": "boolean",
                              "description": "Price per seat: the buyer picks seats at checkout and pays price × seats each period"
                            },
                            "minSeats": {
                              "type": "integer",
                              "minimum": 1,
                              "default": 1
                            },
                            "maxSeats": {
                              "type": "integer",
                              "nullable": true,
                              "minimum": 1,
                              "maximum": 10000,
                              "description": "`null` = no maximum"
                            },
                            "licenseKeys": {
                              "type": "boolean",
                              "description": "Issue each subscriber a license key whose activation limit follows their seats"
                            }
                          }
                        },
                        "meters": {
                          "type": "array",
                          "maxItems": 5,
                          "items": {
                            "type": "object",
                            "required": [
                              "key",
                              "name",
                              "pricing"
                            ],
                            "properties": {
                              "key": {
                                "type": "string",
                                "pattern": "^[a-z0-9][a-z0-9_.-]{0,39}$",
                                "description": "The meter's id — what you send as `meter` when reporting usage",
                                "example": "api_calls"
                              },
                              "name": {
                                "type": "string",
                                "maxLength": 60,
                                "example": "API calls"
                              },
                              "unit": {
                                "type": "string",
                                "maxLength": 30,
                                "description": "Singular unit name shown to buyers",
                                "example": "call"
                              },
                              "pricing": {
                                "type": "string",
                                "enum": [
                                  "per_unit",
                                  "graduated",
                                  "volume"
                                ],
                                "description": "`per_unit`: every billable unit at `unitAmountCents`. `graduated`: each tier prices the units inside it. `volume`: every billable unit at the rate of the tier the total reaches."
                              },
                              "unitAmountCents": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "`per_unit` only: cents per `packageSize` units (`100` with `packageSize: 1000` = $1 per 1,000)",
                                "example": 100
                              },
                              "packageSize": {
                                "type": "integer",
                                "minimum": 1,
                                "default": 1,
                                "description": "Units one price applies to, so sub-cent unit prices stay exact. Each line is rounded half up to the cent.",
                                "example": 1000
                              },
                              "includedUnits": {
                                "type": "integer",
                                "minimum": 0,
                                "default": 0,
                                "description": "Free units per billing period before anything is charged",
                                "example": 10000
                              },
                              "tiers": {
                                "type": "array",
                                "maxItems": 10,
                                "items": {
                                  "type": "object",
                                  "required": [
                                    "upTo",
                                    "unitAmountCents"
                                  ],
                                  "properties": {
                                    "upTo": {
                                      "type": "integer",
                                      "nullable": true,
                                      "minimum": 1,
                                      "description": "Inclusive upper bound in billable units (after included units). `null` on the last tier."
                                    },
                                    "unitAmountCents": {
                                      "type": "integer",
                                      "minimum": 0,
                                      "description": "Cents per `packageSize` units in this tier"
                                    },
                                    "flatAmountCents": {
                                      "type": "integer",
                                      "minimum": 0,
                                      "default": 0,
                                      "description": "Flat cents added when any unit lands in this tier"
                                    }
                                  }
                                },
                                "description": "`graduated` / `volume` only; ascending `upTo`, last one `null`"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The usage pricing is invalid (the message says which field)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include usage-based pricing (Pro and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscriptions/{id}/usage": {
      "get": {
        "operationId": "getSubscriptionUsage",
        "summary": "Get a subscriber's usage",
        "description": "Usage for one subscriber: running totals and the upcoming overage for the current billing period, a per-day series, recent usage reports and the last 12 closed periods with their charge status. Stays readable after a downgrade.",
        "tags": [
          "Usage billing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab9911"
            },
            "description": "The subscriber's `_id` (as returned by `GET /subscriptions/subscribers`) or its membership id (`mem_…`)"
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "How many recent usage reports to include"
          }
        ],
        "responses": {
          "200": {
            "description": "The subscriber's usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageSummary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "reportSubscriptionUsage",
        "summary": "Report usage for a subscriber",
        "description": "Record usage of one meter for one subscriber. Idempotent: send an `idempotencyKey` (or an `Idempotency-Key` header) and a retry with the same key and body returns the first record with `replayed: true` (200) instead of counting twice; the same key with a different body is refused (409). Rate limited to 300 reports per minute per subscriber — aggregate before reporting if you need more. Usage is billed when the subscriber's billing period ends.",
        "tags": [
          "Usage billing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab9911"
            },
            "description": "The subscriber's `_id` (as returned by `GET /subscriptions/subscribers`) or its membership id (`mem_…`)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "meter",
                  "quantity"
                ],
                "properties": {
                  "meter": {
                    "type": "string",
                    "description": "The meter `key` defined on the subscription offer",
                    "example": "api_calls"
                  },
                  "quantity": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000000000,
                    "description": "Units to add (whole number)",
                    "example": 250
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the usage happened (ISO 8601 or Unix seconds). Defaults to now. Must fall in a period that is still open and not in the future."
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Your unique id for this report (e.g. your request id). Strongly recommended.",
                    "example": "req_8f2a91"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Same idempotency key and body as an earlier report: that record, `replayed: true`",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "usageRecord": {
                      "$ref": "#/components/schemas/UsageRecord"
                    },
                    "replayed": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "The usage was recorded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "usageRecord": {
                      "$ref": "#/components/schemas/UsageRecord"
                    },
                    "period": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "start": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "end": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "replayed": {
                      "type": "boolean",
                      "example": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid meter, quantity, timestamp or idempotency key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include usage-based pricing (Pro and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`IDEMPOTENCY_KEY_REUSED` (same key, different body) or `PERIOD_CLOSED` (the timestamp falls in a billing period that already closed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`UNKNOWN_METER`: the offer has no meter with that key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscriptions/{id}/seats": {
      "post": {
        "operationId": "updateSubscriptionSeats",
        "summary": "Change a subscriber's seats",
        "description": "Set a subscriber's seat count on a per-seat offer. Takes effect at once; seats above the quantity bought at checkout are billed at the end of the period, prorated by the time they were held. Going below the seats bought at checkout is refused (409): the subscription price already covers them each period. Also updates the seat license key's activation limit and sends `customer.subscription.updated`.",
        "tags": [
          "Usage billing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab9911"
            },
            "description": "The subscriber's `_id` (as returned by `GET /subscriptions/subscribers`) or its membership id (`mem_…`)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "quantity"
                ],
                "properties": {
                  "quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10000,
                    "description": "The new seat count",
                    "example": 5
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Seats updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriber": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "quantity": {
                          "type": "integer"
                        },
                        "purchasedQuantity": {
                          "type": "integer"
                        }
                      }
                    },
                    "changed": {
                      "type": "boolean"
                    },
                    "previousQuantity": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not a per-seat offer, or the quantity is outside the offer's min / max",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include usage-based pricing (Pro and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`BELOW_PURCHASED_SEATS` or `SUBSCRIPTION_INACTIVE`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscription-retention/recovery/summary": {
      "get": {
        "operationId": "getRecoverySummary",
        "summary": "Failed payment recovery summary",
        "description": "Recovered revenue and recoveries closed in the window, plus what is at risk right now (open recoveries), per currency.",
        "tags": [
          "Subscription retention"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "days",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 30
            },
            "description": "Report window in days, counting back from now"
          }
        ],
        "responses": {
          "200": {
            "description": "Recovery summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "days": {
                      "type": "integer"
                    },
                    "recovered": {
                      "type": "array",
                      "description": "One entry per currency (amounts are never summed across currencies)",
                      "items": {
                        "type": "object",
                        "properties": {
                          "currency": {
                            "type": "string",
                            "example": "usd"
                          },
                          "amount": {
                            "type": "number",
                            "description": "Decimal amount in major currency units (e.g. `29.99`), not cents",
                            "example": 87
                          },
                          "count": {
                            "type": "integer",
                            "example": 3
                          }
                        }
                      }
                    },
                    "recoveredCount": {
                      "type": "integer"
                    },
                    "atRisk": {
                      "type": "array",
                      "description": "One entry per currency (amounts are never summed across currencies)",
                      "items": {
                        "type": "object",
                        "properties": {
                          "currency": {
                            "type": "string",
                            "example": "usd"
                          },
                          "amount": {
                            "type": "number",
                            "description": "Decimal amount in major currency units (e.g. `29.99`), not cents",
                            "example": 87
                          },
                          "count": {
                            "type": "integer",
                            "example": 3
                          }
                        }
                      }
                    },
                    "atRiskCount": {
                      "type": "integer"
                    },
                    "lostCount": {
                      "type": "integer"
                    },
                    "recoveryRate": {
                      "type": "number",
                      "nullable": true,
                      "description": "Recovered ÷ (recovered + lost) in the window, in percent"
                    },
                    "entitlement": {
                      "type": "object",
                      "description": "Whether your plan includes the feature, and the cheapest plan that does",
                      "properties": {
                        "allowed": {
                          "type": "boolean"
                        },
                        "requiredPlan": {
                          "type": "string",
                          "example": "tier3"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscription-retention/recovery/at-risk": {
      "get": {
        "operationId": "listAtRiskSubscriptions",
        "summary": "List at-risk subscriptions",
        "description": "Subscriptions with a failed renewal, newest first. `status=open` (default) lists the ones still being recovered; `recovered` and `closed` list the outcomes.",
        "tags": [
          "Subscription retention"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "recovered",
                "closed"
              ],
              "default": "open"
            },
            "description": "Which recoveries to list"
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number"
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "description": "Number of recoveries per page (maximum 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of recoveries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "open",
                              "recovered",
                              "canceled",
                              "paused",
                              "kept",
                              "lost"
                            ],
                            "description": "`open` while retries run; `recovered` once paid; `canceled` / `paused` / `kept` after the final action; `lost` when the subscription ended"
                          },
                          "subscriptionId": {
                            "type": "string"
                          },
                          "subscriptionCustomerId": {
                            "type": "string"
                          },
                          "productName": {
                            "type": "string"
                          },
                          "buyerEmail": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "number",
                            "description": "Amount due, decimal major units"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "firstFailedAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "attempts": {
                            "type": "integer",
                            "description": "Charge attempts so far"
                          },
                          "retryStep": {
                            "type": "integer",
                            "description": "Scheduled retries already run (0–4)"
                          },
                          "maxRetries": {
                            "type": "integer",
                            "example": 4
                          },
                          "nextRetryAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "lastFailureMessage": {
                            "type": "string",
                            "nullable": true
                          },
                          "recoveredAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "recoveredAmount": {
                            "type": "number"
                          },
                          "closeReason": {
                            "type": "string",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscription-retention/recovery/settings": {
      "get": {
        "operationId": "getRecoverySettings",
        "summary": "Get failed payment recovery settings",
        "description": "Your recovery settings (defaults until you save them).",
        "tags": [
          "Subscription retention"
        ],
        "responses": {
          "200": {
            "description": "Recovery settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean",
                      "description": "Recover failed renewals"
                    },
                    "finalAction": {
                      "type": "string",
                      "enum": [
                        "cancel",
                        "pause",
                        "keep"
                      ],
                      "description": "After the last retry (day 7) fails: cancel the subscription, pause it (the past-due payment is voided) or keep it as it is"
                    },
                    "emailBuyer": {
                      "type": "boolean",
                      "description": "Email the buyer a secure update-card link"
                    },
                    "retryDays": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      },
                      "example": [
                        1,
                        3,
                        5,
                        7
                      ],
                      "description": "Retry schedule, in days after the first failure"
                    },
                    "entitlement": {
                      "type": "object",
                      "description": "Whether your plan includes the feature, and the cheapest plan that does",
                      "properties": {
                        "allowed": {
                          "type": "boolean"
                        },
                        "requiredPlan": {
                          "type": "string",
                          "example": "tier3"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateRecoverySettings",
        "summary": "Update failed payment recovery settings",
        "description": "Change any of the recovery settings; omitted fields keep their value.",
        "tags": [
          "Subscription retention"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Recover failed renewals"
                  },
                  "finalAction": {
                    "type": "string",
                    "enum": [
                      "cancel",
                      "pause",
                      "keep"
                    ],
                    "description": "What happens after the last retry fails"
                  },
                  "emailBuyer": {
                    "type": "boolean",
                    "description": "Email the buyer a secure update-card link"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean",
                      "description": "Recover failed renewals"
                    },
                    "finalAction": {
                      "type": "string",
                      "enum": [
                        "cancel",
                        "pause",
                        "keep"
                      ],
                      "description": "After the last retry (day 7) fails: cancel the subscription, pause it (the past-due payment is voided) or keep it as it is"
                    },
                    "emailBuyer": {
                      "type": "boolean",
                      "description": "Email the buyer a secure update-card link"
                    },
                    "retryDays": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      },
                      "example": [
                        1,
                        3,
                        5,
                        7
                      ],
                      "description": "Retry schedule, in days after the first failure"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Nothing to update, or an invalid value",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "`finalAction` must be cancel, pause or keep."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include failed payment recovery (Pro and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`. Turning the feature off is always allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscription-retention/save-offers/report": {
      "get": {
        "operationId": "getSaveOffersReport",
        "summary": "Saved subscriptions report",
        "description": "Cancellation flows in the window: how many buyers were saved (pause or discount) and how many canceled, the reasons they gave, revenue kept per period, and the 20 most recent flows.",
        "tags": [
          "Subscription retention"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "days",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 30
            },
            "description": "Report window in days, counting back from now"
          }
        ],
        "responses": {
          "200": {
            "description": "Save offers report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "days": {
                      "type": "integer"
                    },
                    "saved": {
                      "type": "integer"
                    },
                    "savedPause": {
                      "type": "integer"
                    },
                    "savedDiscount": {
                      "type": "integer"
                    },
                    "canceled": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "saveRate": {
                      "type": "number",
                      "nullable": true,
                      "description": "Percent"
                    },
                    "revenueKept": {
                      "type": "array",
                      "description": "One entry per currency (amounts are never summed across currencies)",
                      "items": {
                        "type": "object",
                        "properties": {
                          "currency": {
                            "type": "string",
                            "example": "usd"
                          },
                          "amount": {
                            "type": "number",
                            "description": "Decimal amount in major currency units (e.g. `29.99`), not cents",
                            "example": 87
                          },
                          "count": {
                            "type": "integer",
                            "example": 3
                          }
                        }
                      }
                    },
                    "reasons": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "reason": {
                            "type": "string",
                            "enum": [
                              "too_expensive",
                              "not_using",
                              "missing_features",
                              "technical_issues",
                              "switching",
                              "other"
                            ]
                          },
                          "count": {
                            "type": "integer"
                          },
                          "saved": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "recent": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "subscriptionId": {
                            "type": "string"
                          },
                          "subscriptionCustomerId": {
                            "type": "string"
                          },
                          "productName": {
                            "type": "string"
                          },
                          "buyerEmail": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          },
                          "reasonText": {
                            "type": "string"
                          },
                          "outcome": {
                            "type": "string",
                            "enum": [
                              "saved_pause",
                              "saved_discount",
                              "canceled"
                            ]
                          },
                          "pauseMonths": {
                            "type": "integer",
                            "nullable": true
                          },
                          "discountPercent": {
                            "type": "integer",
                            "nullable": true
                          },
                          "discountMonths": {
                            "type": "integer",
                            "nullable": true
                          },
                          "periodAmount": {
                            "type": "number"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "entitlement": {
                      "type": "object",
                      "description": "Whether your plan includes the feature, and the cheapest plan that does",
                      "properties": {
                        "allowed": {
                          "type": "boolean"
                        },
                        "requiredPlan": {
                          "type": "string",
                          "example": "tier3"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscription-retention/intro/report": {
      "get": {
        "operationId": "getIntroPricingReport",
        "summary": "Intro pricing report",
        "description": "Subscribers currently on an intro price, those who moved to the regular price, switch-to-yearly offers sent and switches completed.",
        "tags": [
          "Subscription retention"
        ],
        "responses": {
          "200": {
            "description": "Intro pricing report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "introActive": {
                      "type": "integer"
                    },
                    "introCompleted": {
                      "type": "integer"
                    },
                    "needsAttention": {
                      "type": "integer",
                      "description": "Price changes the payment processor refused repeatedly (support follows up)"
                    },
                    "yearlyOffered": {
                      "type": "integer"
                    },
                    "yearlySwitched": {
                      "type": "integer"
                    },
                    "entitlement": {
                      "type": "object",
                      "description": "Whether your plan includes the feature, and the cheapest plan that does",
                      "properties": {
                        "allowed": {
                          "type": "boolean"
                        },
                        "requiredPlan": {
                          "type": "string",
                          "example": "tier3"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscription-retention/offers": {
      "get": {
        "operationId": "listRetentionOffers",
        "summary": "List retention settings per subscription offer",
        "description": "Every subscription offer in the key's mode with its save offers, intro price and switch-to-yearly settings (defaults when never saved).",
        "tags": [
          "Subscription retention"
        ],
        "responses": {
          "200": {
            "description": "Your offers and their retention settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "offers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "subscriptionId": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "price": {
                            "type": "number",
                            "description": "Regular price per period, decimal major units"
                          },
                          "frequency": {
                            "type": "string",
                            "enum": [
                              "weekly",
                              "monthly",
                              "yearly"
                            ]
                          },
                          "trialPeriod": {
                            "type": "integer",
                            "description": "Free trial days"
                          },
                          "published": {
                            "type": "boolean"
                          },
                          "saveOffers": {
                            "type": "object",
                            "description": "Cancellation save offers shown when a buyer cancels in the subscriber portal",
                            "properties": {
                              "enabled": {
                                "type": "boolean",
                                "description": "Show the save flow for this offer"
                              },
                              "pauseEnabled": {
                                "type": "boolean",
                                "description": "Offer a pause instead of canceling"
                              },
                              "pauseMaxMonths": {
                                "type": "integer",
                                "minimum": 1,
                                "maximum": 3,
                                "description": "Longest pause the buyer can pick, in months"
                              },
                              "discountEnabled": {
                                "type": "boolean",
                                "description": "Offer a discount instead of canceling"
                              },
                              "discountPercent": {
                                "type": "integer",
                                "minimum": 5,
                                "maximum": 90,
                                "description": "Percent off each of the next `discountMonths` payments"
                              },
                              "discountMonths": {
                                "type": "integer",
                                "minimum": 1,
                                "maximum": 12,
                                "description": "How many payments the discount covers"
                              }
                            }
                          },
                          "intro": {
                            "type": "object",
                            "description": "Intro price for the first billing periods. Not combinable with a free trial; a discount code at checkout replaces it.",
                            "properties": {
                              "enabled": {
                                "type": "boolean"
                              },
                              "price": {
                                "type": "number",
                                "description": "Intro price per period. Decimal amount in major currency units (e.g. `1.00`), not cents. At least 1.00 and lower than the regular price.",
                                "example": 1
                              },
                              "periods": {
                                "type": "integer",
                                "minimum": 1,
                                "maximum": 12,
                                "description": "How many billing periods are charged the intro price"
                              }
                            }
                          },
                          "yearlySwitch": {
                            "type": "object",
                            "description": "Switch-to-yearly offer (monthly offers only): after `afterPayments` paid months the buyer is emailed and sees the offer in the portal. The yearly plan starts when the paid month ends.",
                            "properties": {
                              "enabled": {
                                "type": "boolean"
                              },
                              "afterPayments": {
                                "type": "integer",
                                "minimum": 1,
                                "maximum": 24
                              },
                              "percentOff": {
                                "type": "integer",
                                "minimum": 1,
                                "maximum": 50,
                                "description": "Percent off twelve monthly payments"
                              }
                            }
                          },
                          "updatedAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "entitlements": {
                      "type": "object",
                      "properties": {
                        "cancellationSaveOffers": {
                          "type": "object",
                          "description": "Whether your plan includes the feature, and the cheapest plan that does",
                          "properties": {
                            "allowed": {
                              "type": "boolean"
                            },
                            "requiredPlan": {
                              "type": "string",
                              "example": "tier3"
                            }
                          }
                        },
                        "introPricing": {
                          "type": "object",
                          "description": "Whether your plan includes the feature, and the cheapest plan that does",
                          "properties": {
                            "allowed": {
                              "type": "boolean"
                            },
                            "requiredPlan": {
                              "type": "string",
                              "example": "tier3"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/subscription-retention/offers/{id}": {
      "get": {
        "operationId": "getRetentionOffer",
        "summary": "Get one offer's retention settings",
        "description": "Save offers, intro price and switch-to-yearly settings of one subscription offer.",
        "tags": [
          "Subscription retention"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The subscription offer id (24-character hex MongoDB ObjectId), as returned by `GET /subscriptions`"
          }
        ],
        "responses": {
          "200": {
            "description": "The offer's retention settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriptionId": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "price": {
                      "type": "number",
                      "description": "Regular price per period, decimal major units"
                    },
                    "frequency": {
                      "type": "string",
                      "enum": [
                        "weekly",
                        "monthly",
                        "yearly"
                      ]
                    },
                    "trialPeriod": {
                      "type": "integer",
                      "description": "Free trial days"
                    },
                    "published": {
                      "type": "boolean"
                    },
                    "saveOffers": {
                      "type": "object",
                      "description": "Cancellation save offers shown when a buyer cancels in the subscriber portal",
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "description": "Show the save flow for this offer"
                        },
                        "pauseEnabled": {
                          "type": "boolean",
                          "description": "Offer a pause instead of canceling"
                        },
                        "pauseMaxMonths": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 3,
                          "description": "Longest pause the buyer can pick, in months"
                        },
                        "discountEnabled": {
                          "type": "boolean",
                          "description": "Offer a discount instead of canceling"
                        },
                        "discountPercent": {
                          "type": "integer",
                          "minimum": 5,
                          "maximum": 90,
                          "description": "Percent off each of the next `discountMonths` payments"
                        },
                        "discountMonths": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 12,
                          "description": "How many payments the discount covers"
                        }
                      }
                    },
                    "intro": {
                      "type": "object",
                      "description": "Intro price for the first billing periods. Not combinable with a free trial; a discount code at checkout replaces it.",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "price": {
                          "type": "number",
                          "description": "Intro price per period. Decimal amount in major currency units (e.g. `1.00`), not cents. At least 1.00 and lower than the regular price.",
                          "example": 1
                        },
                        "periods": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 12,
                          "description": "How many billing periods are charged the intro price"
                        }
                      }
                    },
                    "yearlySwitch": {
                      "type": "object",
                      "description": "Switch-to-yearly offer (monthly offers only): after `afterPayments` paid months the buyer is emailed and sees the offer in the portal. The yearly plan starts when the paid month ends.",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "afterPayments": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 24
                        },
                        "percentOff": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 50,
                          "description": "Percent off twelve monthly payments"
                        }
                      }
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateRetentionOffer",
        "summary": "Update one offer's retention settings",
        "description": "Send any of `saveOffers`, `intro`, `yearlySwitch` (each partial). Turning save offers on needs the cancellation save offers feature; turning the intro price or the yearly switch on needs intro pricing (both Pro). Validation errors answer 400 with `code: INVALID_RETENTION_CONFIG` and the offending `field`.",
        "tags": [
          "Subscription retention"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The subscription offer id (24-character hex MongoDB ObjectId), as returned by `GET /subscriptions`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "saveOffers": {
                    "type": "object",
                    "description": "Cancellation save offers shown when a buyer cancels in the subscriber portal",
                    "properties": {
                      "enabled": {
                        "type": "boolean",
                        "description": "Show the save flow for this offer"
                      },
                      "pauseEnabled": {
                        "type": "boolean",
                        "description": "Offer a pause instead of canceling"
                      },
                      "pauseMaxMonths": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 3,
                        "description": "Longest pause the buyer can pick, in months"
                      },
                      "discountEnabled": {
                        "type": "boolean",
                        "description": "Offer a discount instead of canceling"
                      },
                      "discountPercent": {
                        "type": "integer",
                        "minimum": 5,
                        "maximum": 90,
                        "description": "Percent off each of the next `discountMonths` payments"
                      },
                      "discountMonths": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 12,
                        "description": "How many payments the discount covers"
                      }
                    }
                  },
                  "intro": {
                    "type": "object",
                    "description": "Intro price for the first billing periods. Not combinable with a free trial; a discount code at checkout replaces it.",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "price": {
                        "type": "number",
                        "description": "Intro price per period. Decimal amount in major currency units (e.g. `1.00`), not cents. At least 1.00 and lower than the regular price.",
                        "example": 1
                      },
                      "periods": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 12,
                        "description": "How many billing periods are charged the intro price"
                      }
                    }
                  },
                  "yearlySwitch": {
                    "type": "object",
                    "description": "Switch-to-yearly offer (monthly offers only): after `afterPayments` paid months the buyer is emailed and sees the offer in the portal. The yearly plan starts when the paid month ends.",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "afterPayments": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 24
                      },
                      "percentOff": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 50,
                        "description": "Percent off twelve monthly payments"
                      }
                    }
                  }
                }
              },
              "example": {
                "saveOffers": {
                  "enabled": true,
                  "pauseMaxMonths": 2,
                  "discountPercent": 30,
                  "discountMonths": 3
                },
                "intro": {
                  "enabled": true,
                  "price": 1,
                  "periods": 1
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriptionId": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "price": {
                      "type": "number",
                      "description": "Regular price per period, decimal major units"
                    },
                    "frequency": {
                      "type": "string",
                      "enum": [
                        "weekly",
                        "monthly",
                        "yearly"
                      ]
                    },
                    "trialPeriod": {
                      "type": "integer",
                      "description": "Free trial days"
                    },
                    "published": {
                      "type": "boolean"
                    },
                    "saveOffers": {
                      "type": "object",
                      "description": "Cancellation save offers shown when a buyer cancels in the subscriber portal",
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "description": "Show the save flow for this offer"
                        },
                        "pauseEnabled": {
                          "type": "boolean",
                          "description": "Offer a pause instead of canceling"
                        },
                        "pauseMaxMonths": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 3,
                          "description": "Longest pause the buyer can pick, in months"
                        },
                        "discountEnabled": {
                          "type": "boolean",
                          "description": "Offer a discount instead of canceling"
                        },
                        "discountPercent": {
                          "type": "integer",
                          "minimum": 5,
                          "maximum": 90,
                          "description": "Percent off each of the next `discountMonths` payments"
                        },
                        "discountMonths": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 12,
                          "description": "How many payments the discount covers"
                        }
                      }
                    },
                    "intro": {
                      "type": "object",
                      "description": "Intro price for the first billing periods. Not combinable with a free trial; a discount code at checkout replaces it.",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "price": {
                          "type": "number",
                          "description": "Intro price per period. Decimal amount in major currency units (e.g. `1.00`), not cents. At least 1.00 and lower than the regular price.",
                          "example": 1
                        },
                        "periods": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 12,
                          "description": "How many billing periods are charged the intro price"
                        }
                      }
                    },
                    "yearlySwitch": {
                      "type": "object",
                      "description": "Switch-to-yearly offer (monthly offers only): after `afterPayments` paid months the buyer is emailed and sees the offer in the portal. The yearly plan starts when the paid month ends.",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "afterPayments": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 24
                        },
                        "percentOff": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 50,
                          "description": "Percent off twelve monthly payments"
                        }
                      }
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Nothing to update, or an invalid setting",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "The intro price must be at least 1.00 and lower than the regular price.",
                  "code": "INVALID_RETENTION_CONFIG",
                  "field": "intro.price"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this retention feature (Pro and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`. Turning the feature off is always allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/tracked-links": {
      "get": {
        "operationId": "listTrackedLinks",
        "tags": [
          "Tracked links"
        ],
        "summary": "List tracked links with stats",
        "description": "Every tracked link with clicks, unique visitors, sales, revenue and conversion over the range, plus totals, a per-channel breakdown and a daily series. Archived links are left out unless `archived=1`. Stats are `null` (`statsLocked: true`) when your plan does not include tracked links.",
        "parameters": [
          {
            "in": "query",
            "name": "archived",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            },
            "description": "`1` to include archived links"
          },
          {
            "in": "query",
            "name": "startDate",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Stats range start (ISO). Default: the first link's creation (list) or the link's creation (one link). Ranges are capped at 366 days."
          },
          {
            "in": "query",
            "name": "endDate",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Stats range end (ISO). Default: now."
          },
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "minimum": -840,
              "maximum": 840,
              "default": 0
            },
            "description": "Your UTC offset as `Date#getTimezoneOffset()` minutes (UTC − local), so daily rows follow your local days."
          }
        ],
        "responses": {
          "200": {
            "description": "Your links and their stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "links": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/TrackedLink"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "stats": {
                                "$ref": "#/components/schemas/TrackedLinkStats",
                                "nullable": true
                              },
                              "spark": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "integer"
                                },
                                "description": "Clicks per day over the range (oldest first)"
                              }
                            }
                          }
                        ]
                      }
                    },
                    "archivedCount": {
                      "type": "integer"
                    },
                    "shortBase": {
                      "type": "string",
                      "example": "https://redmug.pocketsflow.com/go/"
                    },
                    "statsLocked": {
                      "type": "boolean"
                    },
                    "currency": {
                      "type": "string",
                      "nullable": true,
                      "example": "USD"
                    },
                    "totals": {
                      "$ref": "#/components/schemas/TrackedLinkStats",
                      "nullable": true
                    },
                    "channels": {
                      "type": "array",
                      "nullable": true,
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/TrackedLinkStats"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "channel": {
                                "type": "string",
                                "example": "instagram"
                              },
                              "links": {
                                "type": "integer",
                                "example": 3
                              }
                            }
                          }
                        ]
                      }
                    },
                    "daily": {
                      "type": "array",
                      "nullable": true,
                      "items": {
                        "$ref": "#/components/schemas/TrackedLinkDay"
                      }
                    },
                    "range": {
                      "type": "object",
                      "properties": {
                        "start": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "end": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createTrackedLink",
        "tags": [
          "Tracked links"
        ],
        "summary": "Create a tracked link",
        "description": "Creates a short link to a page on your store. Needs the Starter plan or above. The slug is unique per store and never changes once created (posted links keep working).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "destination"
                ],
                "properties": {
                  "destination": {
                    "type": "string",
                    "maxLength": 2048,
                    "description": "Page the link opens. Must be on your own store: your Pocketsflow subdomain, a domain you connected, or a hosted checkout / product page of one of your products (`https://app.pocketsflow.com/checkout?productId=…`). A bare path (`/ebook`) is on your subdomain. Any other site is refused (400).",
                    "example": "https://redmug.pocketsflow.com/ebook"
                  },
                  "slug": {
                    "type": "string",
                    "maxLength": 48,
                    "description": "The `/go/<slug>` part: letters, numbers and hyphens (lowercased). Omit to derive one from the label or the destination.",
                    "example": "bio"
                  },
                  "label": {
                    "type": "string",
                    "maxLength": 80,
                    "example": "Instagram bio"
                  },
                  "channel": {
                    "type": "string",
                    "enum": [
                      "instagram",
                      "tiktok",
                      "youtube",
                      "x",
                      "facebook",
                      "email",
                      "ads",
                      "other"
                    ],
                    "description": "Where the link is posted. Groups links in the per-channel stats and fills blank UTM fields (`instagram` → `utm_source=instagram&utm_medium=social`).",
                    "example": "instagram"
                  },
                  "utm": {
                    "type": "object",
                    "description": "UTM parameters the redirect adds to the destination (unless it already sets them). Blank fields are filled from the channel and the slug.",
                    "properties": {
                      "source": {
                        "type": "string",
                        "maxLength": 100,
                        "example": "instagram"
                      },
                      "medium": {
                        "type": "string",
                        "maxLength": 100,
                        "example": "social"
                      },
                      "campaign": {
                        "type": "string",
                        "maxLength": 100,
                        "example": "bio"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created link",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "link": {
                      "$ref": "#/components/schemas/TrackedLink"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The destination is not on your store, or the slug is invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Links can only open pages on your own store.",
                  "field": "destination"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include tracked links (Starter and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "409": {
            "description": "You already have a link with this slug",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "You already have a link with this name.",
                  "field": "slug",
                  "suggestion": "bio-k3x"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/tracked-links/{id}": {
      "get": {
        "operationId": "getTrackedLinkStats",
        "tags": [
          "Tracked links"
        ],
        "summary": "Get one tracked link with its stats",
        "description": "The link with its stats, a daily series and the top 20 countries of its visitors over the range. Stats are `null` (`statsLocked: true`) when your plan does not include tracked links.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a3d59e3f1a0012cd7788"
            },
            "description": "Tracked link id"
          },
          {
            "in": "query",
            "name": "startDate",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Stats range start (ISO). Default: the first link's creation (list) or the link's creation (one link). Ranges are capped at 366 days."
          },
          {
            "in": "query",
            "name": "endDate",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Stats range end (ISO). Default: now."
          },
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "minimum": -840,
              "maximum": 840,
              "default": 0
            },
            "description": "Your UTC offset as `Date#getTimezoneOffset()` minutes (UTC − local), so daily rows follow your local days."
          }
        ],
        "responses": {
          "200": {
            "description": "The link and its stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "link": {
                      "$ref": "#/components/schemas/TrackedLink"
                    },
                    "statsLocked": {
                      "type": "boolean"
                    },
                    "stats": {
                      "$ref": "#/components/schemas/TrackedLinkStats",
                      "nullable": true
                    },
                    "daily": {
                      "type": "array",
                      "nullable": true,
                      "items": {
                        "$ref": "#/components/schemas/TrackedLinkDay"
                      }
                    },
                    "countries": {
                      "type": "array",
                      "nullable": true,
                      "items": {
                        "type": "object",
                        "properties": {
                          "country": {
                            "type": "string",
                            "example": "US"
                          },
                          "clicks": {
                            "type": "integer"
                          },
                          "visitors": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "currency": {
                      "type": "string",
                      "nullable": true,
                      "example": "USD"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateTrackedLink",
        "tags": [
          "Tracked links"
        ],
        "summary": "Update or archive a tracked link",
        "description": "Changing the label, destination, channel or UTM needs the Starter plan. Archiving (`archived: true`) and unarchiving always work. The slug cannot change.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a3d59e3f1a0012cd7788"
            },
            "description": "Tracked link id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "destination": {
                    "type": "string",
                    "maxLength": 2048,
                    "description": "Page the link opens. Must be on your own store: your Pocketsflow subdomain, a domain you connected, or a hosted checkout / product page of one of your products (`https://app.pocketsflow.com/checkout?productId=…`). A bare path (`/ebook`) is on your subdomain. Any other site is refused (400).",
                    "example": "https://redmug.pocketsflow.com/ebook"
                  },
                  "channel": {
                    "type": "string",
                    "enum": [
                      "instagram",
                      "tiktok",
                      "youtube",
                      "x",
                      "facebook",
                      "email",
                      "ads",
                      "other"
                    ],
                    "description": "Where the link is posted. Groups links in the per-channel stats and fills blank UTM fields (`instagram` → `utm_source=instagram&utm_medium=social`).",
                    "example": "instagram"
                  },
                  "utm": {
                    "type": "object",
                    "description": "UTM parameters the redirect adds to the destination (unless it already sets them). Blank fields are filled from the channel and the slug.",
                    "properties": {
                      "source": {
                        "type": "string",
                        "maxLength": 100,
                        "example": "instagram"
                      },
                      "medium": {
                        "type": "string",
                        "maxLength": 100,
                        "example": "social"
                      },
                      "campaign": {
                        "type": "string",
                        "maxLength": 100,
                        "example": "bio"
                      }
                    }
                  },
                  "archived": {
                    "type": "boolean",
                    "description": "Hide the link from the list. The short link keeps redirecting."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated link",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "link": {
                      "$ref": "#/components/schemas/TrackedLink"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The destination is not on your store, or the slug was changed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include tracked links (Starter and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/tracked-links/slug-available": {
      "get": {
        "operationId": "checkTrackedLinkSlug",
        "tags": [
          "Tracked links"
        ],
        "summary": "Check a tracked link slug",
        "description": "Whether a slug is free on your store, its normalised form, and a free suggestion when it is taken.",
        "parameters": [
          {
            "in": "query",
            "name": "slug",
            "required": true,
            "schema": {
              "type": "string",
              "example": "bio"
            },
            "description": "The slug to check"
          },
          {
            "in": "query",
            "name": "excludeId",
            "schema": {
              "type": "string"
            },
            "description": "A link id to ignore (when editing it)"
          }
        ],
        "responses": {
          "200": {
            "description": "Availability",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "slug": {
                      "type": "string",
                      "example": "bio"
                    },
                    "valid": {
                      "type": "boolean"
                    },
                    "available": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    },
                    "suggestion": {
                      "type": "string",
                      "example": "bio-k3x"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/courses/{id}": {
      "get": {
        "operationId": "getCourseSettings",
        "tags": [
          "Courses"
        ],
        "summary": "Get a course's drip rules, quizzes and certificate",
        "description": "Every lesson of the course in order with its `lessonId`, drip rule and quiz (correct answers included), plus the certificate and unlock-email switches. Works on any plan; `entitled` says whether your plan applies them.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The course's product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The course settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseSettings"
                }
              }
            }
          },
          "400": {
            "description": "The product isn't a course",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This product isn't a course."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No course with this id in your account, or the lesson isn't in it",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Course not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateCourseSettings",
        "tags": [
          "Courses"
        ],
        "summary": "Set drip rules, quizzes and the certificate",
        "description": "Merge update: every key is optional. Each entry of `lessons` changes only what it names (`drip`, `quiz`; `quiz: null` removes the quiz); lessons you leave out keep their settings. Turning on the certificate shows buyers a certificate once they finish every lesson and pass every quiz marked `mustPass`.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The course's product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lessons": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "lessonId"
                      ],
                      "properties": {
                        "lessonId": {
                          "type": "string",
                          "example": "les_3f9a1c0b7d2e"
                        },
                        "drip": {
                          "$ref": "#/components/schemas/CourseDripRule"
                        },
                        "quiz": {
                          "type": "object",
                          "nullable": true,
                          "description": "The lesson's quiz (see `CourseQuiz`), or null to remove it",
                          "properties": {
                            "passMark": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100,
                              "default": 70,
                              "description": "Score in percent a buyer needs to pass"
                            },
                            "mustPass": {
                              "type": "boolean",
                              "default": false,
                              "description": "Later lessons stay locked until the buyer passes this quiz"
                            },
                            "questions": {
                              "type": "array",
                              "minItems": 1,
                              "maxItems": 50,
                              "items": {
                                "type": "object",
                                "required": [
                                  "prompt",
                                  "options"
                                ],
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "description": "Kept when you send it back; generated otherwise",
                                    "example": "q_9f8e7d6c"
                                  },
                                  "prompt": {
                                    "type": "string",
                                    "maxLength": 500,
                                    "example": "How often should you post?"
                                  },
                                  "type": {
                                    "type": "string",
                                    "enum": [
                                      "single",
                                      "multiple"
                                    ],
                                    "default": "single",
                                    "description": "`single`: exactly one correct option. `multiple`: one or more; the buyer must pick exactly the correct set."
                                  },
                                  "options": {
                                    "type": "array",
                                    "minItems": 2,
                                    "maxItems": 10,
                                    "items": {
                                      "type": "object",
                                      "required": [
                                        "text"
                                      ],
                                      "properties": {
                                        "id": {
                                          "type": "string",
                                          "description": "Kept when you send it back; generated otherwise",
                                          "example": "o_1a2b3c4d"
                                        },
                                        "text": {
                                          "type": "string",
                                          "maxLength": 300,
                                          "example": "Once a week"
                                        },
                                        "correct": {
                                          "type": "boolean",
                                          "description": "A right answer",
                                          "example": true
                                        }
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  },
                  "certificate": {
                    "type": "object",
                    "properties": {
                      "enabled": {
                        "type": "boolean",
                        "example": true
                      }
                    }
                  },
                  "notifyOnUnlock": {
                    "type": "boolean",
                    "description": "Email buyers when a dripped lesson unlocks"
                  }
                }
              },
              "example": {
                "lessons": [
                  {
                    "lessonId": "les_3f9a1c0b7d2e",
                    "drip": {
                      "type": "afterDays",
                      "days": 7
                    }
                  }
                ],
                "certificate": {
                  "enabled": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseSettings"
                }
              }
            }
          },
          "400": {
            "description": "Invalid rule or quiz, or a lessonId that isn't in the course",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "drip.days must be a whole number from 0 to 3650"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — drip content, quizzes and certificates need the Growth plan or higher (feature `dripContent`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "dripContent",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "No course with this id in your account, or the lesson isn't in it",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Course not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/courses/{id}/quizzes": {
      "get": {
        "operationId": "listCourseQuizzes",
        "tags": [
          "Courses"
        ],
        "summary": "List a course's quizzes",
        "description": "Every lesson that has a quiz, with its questions and correct answers.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The course's product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The quizzes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "quizzes": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "type": "object",
                            "properties": {
                              "lessonId": {
                                "type": "string"
                              },
                              "title": {
                                "type": "string"
                              }
                            }
                          },
                          {
                            "$ref": "#/components/schemas/CourseQuiz"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The product isn't a course",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This product isn't a course."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No course with this id in your account, or the lesson isn't in it",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Course not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/courses/{id}/quizzes/{lessonId}": {
      "put": {
        "operationId": "upsertCourseQuiz",
        "tags": [
          "Courses"
        ],
        "summary": "Create or replace a lesson's quiz",
        "description": "Single or multiple choice, 50 questions at most, 2–10 options each. Send `id`s back to keep buyers' previous answers lined up.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The course's product id"
          },
          {
            "in": "path",
            "name": "lessonId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "les_3f9a1c0b7d2e"
            },
            "description": "The lesson's `lessonId` (from `GET /courses/{id}` or the product's `episodes[].lessonId`)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "questions"
                ],
                "properties": {
                  "passMark": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 70,
                    "description": "Score in percent a buyer needs to pass"
                  },
                  "mustPass": {
                    "type": "boolean",
                    "default": false,
                    "description": "Later lessons stay locked until the buyer passes this quiz"
                  },
                  "questions": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "type": "object",
                      "required": [
                        "prompt",
                        "options"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "Kept when you send it back; generated otherwise",
                          "example": "q_9f8e7d6c"
                        },
                        "prompt": {
                          "type": "string",
                          "maxLength": 500,
                          "example": "How often should you post?"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "single",
                            "multiple"
                          ],
                          "default": "single",
                          "description": "`single`: exactly one correct option. `multiple`: one or more; the buyer must pick exactly the correct set."
                        },
                        "options": {
                          "type": "array",
                          "minItems": 2,
                          "maxItems": 10,
                          "items": {
                            "type": "object",
                            "required": [
                              "text"
                            ],
                            "properties": {
                              "id": {
                                "type": "string",
                                "description": "Kept when you send it back; generated otherwise",
                                "example": "o_1a2b3c4d"
                              },
                              "text": {
                                "type": "string",
                                "maxLength": 300,
                                "example": "Once a week"
                              },
                              "correct": {
                                "type": "boolean",
                                "description": "A right answer",
                                "example": true
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "passMark": 70,
                "mustPass": true,
                "questions": [
                  {
                    "id": "q_9f8e7d6c",
                    "prompt": "How often should you post?",
                    "type": "single",
                    "options": [
                      {
                        "id": "o_1a2b3c4d",
                        "text": "Once a week",
                        "correct": true
                      },
                      {
                        "id": "o_5e6f7a8b",
                        "text": "Whenever you feel like it",
                        "correct": false
                      }
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved quiz",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseQuiz"
                }
              }
            }
          },
          "400": {
            "description": "Invalid quiz",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Question 1 needs a correct answer"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — drip content, quizzes and certificates need the Growth plan or higher (feature `dripContent`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "dripContent",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "No course with this id in your account, or the lesson isn't in it",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Course not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteCourseQuiz",
        "tags": [
          "Courses"
        ],
        "summary": "Remove a lesson's quiz",
        "description": "Works on any plan. Buyers' past attempts are kept for your stats.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The course's product id"
          },
          {
            "in": "path",
            "name": "lessonId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "les_3f9a1c0b7d2e"
            },
            "description": "The lesson's `lessonId` (from `GET /courses/{id}` or the product's `episodes[].lessonId`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean",
                      "example": true
                    },
                    "lessonId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The product isn't a course",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This product isn't a course."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such course, or the lesson has no quiz",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This lesson has no quiz"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/courses/{id}/stats": {
      "get": {
        "operationId": "getCourseStats",
        "tags": [
          "Courses"
        ],
        "summary": "Get completion stats",
        "description": "How many buyers enrolled, started and finished, certificates issued, and per lesson how many completed it and how its quiz went.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The course's product id"
          }
        ],
        "responses": {
          "200": {
            "description": "Completion stats",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseStats"
                }
              }
            }
          },
          "400": {
            "description": "The product isn't a course",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This product isn't a course."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No course with this id in your account, or the lesson isn't in it",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Course not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/certificates/{verifyId}": {
      "get": {
        "operationId": "verifyCertificate",
        "tags": [
          "Courses"
        ],
        "summary": "Verify a certificate",
        "description": "PUBLIC — no API key. Check a course certificate by its id (case and dashes don't matter). Answers who it was issued to, for which course and creator, and when. `pdfUrl` downloads the certificate.",
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "verifyId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "7K2Q-X9MD-4HTR"
            },
            "description": "The id printed on the certificate"
          }
        ],
        "responses": {
          "200": {
            "description": "A valid certificate",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Certificate"
                }
              }
            }
          },
          "404": {
            "description": "No certificate has this id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "No certificate has this ID."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/team": {
      "get": {
        "tags": [
          "Team"
        ],
        "operationId": "getTeam",
        "summary": "Get your team",
        "description": "Members, pending invites, teams and seat usage. Seats are re-checked first: after a downgrade the newest members beyond the plan's seats are paused (never removed).",
        "responses": {
          "200": {
            "description": "Your team",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plan": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        }
                      }
                    },
                    "teamsAllowed": {
                      "type": "boolean",
                      "description": "Whether your plan includes team members"
                    },
                    "seats": {
                      "type": "object",
                      "properties": {
                        "limit": {
                          "type": "integer",
                          "nullable": true,
                          "description": "Seats on your plan, owner included; `null` = unlimited"
                        },
                        "used": {
                          "type": "integer",
                          "description": "You plus active members"
                        },
                        "pending": {
                          "type": "integer",
                          "description": "Open invites (each holds a seat)"
                        },
                        "paused": {
                          "type": "integer"
                        }
                      }
                    },
                    "members": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TeamMember"
                      }
                    },
                    "invites": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TeamInvite"
                      }
                    },
                    "teams": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Team"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/team/activity": {
      "get": {
        "tags": [
          "Team"
        ],
        "operationId": "listTeamActivity",
        "summary": "List team activity",
        "description": "Who did what in your store, newest first: team changes and every change a team member made. Kept for 400 days. Page with `before` = the previous page's `nextBefore`.",
        "parameters": [
          {
            "in": "query",
            "name": "before",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only entries older than this (the previous page's `nextBefore`)"
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Entries per page (maximum 100)"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of activity",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TeamActivity"
                      }
                    },
                    "nextBefore": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/team/invites": {
      "post": {
        "tags": [
          "Team"
        ],
        "operationId": "inviteTeamMember",
        "summary": "Invite a team member",
        "description": "Emails a one-time invite link (valid 7 days). The person accepts it signed in with this exact, verified email. Only owners invite owners. Up to 20 invites per hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "role"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "sara@example.com"
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "owner",
                      "admin",
                      "member"
                    ],
                    "example": "member"
                  },
                  "teamIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ids of your teams. Members can use only the areas their teams have; owners and admins can use every area.",
                    "example": [
                      "6710b2c49e3f1a0012cd56ef"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invite created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invite": {
                      "$ref": "#/components/schemas/TeamInvite"
                    },
                    "emailSent": {
                      "type": "boolean",
                      "description": "False when the email could not be sent; resend it"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include team members (Growth and above), every seat is taken (`code: SEAT_LIMIT`, `details.limit`), or the role is not allowed. Body for the plan: `code: FEATURE_LOCKED`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "teams",
                    "currentPlan": "tier1",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "409": {
            "description": "Already on the team or already invited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "ALREADY_INVITED",
                  "code": "ALREADY_INVITED",
                  "message": "This person already has an invite. Resend it instead."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/team/invites/{id}/resend": {
      "post": {
        "tags": [
          "Team"
        ],
        "operationId": "resendTeamInvite",
        "summary": "Resend an invite",
        "description": "Sends a new link (the old one stops working) and restarts the 7 days. Once a minute, at most 5 times.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Invite id"
          }
        ],
        "responses": {
          "200": {
            "description": "Invite resent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "emailSent": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/team/invites/{id}": {
      "delete": {
        "tags": [
          "Team"
        ],
        "operationId": "cancelTeamInvite",
        "summary": "Cancel an invite",
        "description": "The link stops working and its seat is freed.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Invite id"
          }
        ],
        "responses": {
          "200": {
            "description": "Canceled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/team/members/{id}": {
      "patch": {
        "tags": [
          "Team"
        ],
        "operationId": "updateTeamMember",
        "summary": "Change a member's role, teams or status",
        "description": "Only owners make or change owners. A store always keeps at least one owner. Resuming a paused member needs a free seat. Takes effect on the member's next request.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Membership id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "role": {
                    "type": "string",
                    "enum": [
                      "owner",
                      "admin",
                      "member"
                    ]
                  },
                  "teamIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ids of your teams. Members can use only the areas their teams have; owners and admins can use every area.",
                    "example": [
                      "6710b2c49e3f1a0012cd56ef"
                    ]
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TeamMember"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Not allowed (owner-only change) or no free seat to resume",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "ROLE_FORBIDDEN",
                  "code": "ROLE_FORBIDDEN",
                  "message": "Only owners can make someone an owner."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The store would have no owner left",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "LAST_OWNER",
                  "code": "LAST_OWNER",
                  "message": "A store needs at least one owner. Make someone else an owner first."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "tags": [
          "Team"
        ],
        "operationId": "removeTeamMember",
        "summary": "Remove a member",
        "description": "They lose access on their next request.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Membership id"
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/team/teams": {
      "post": {
        "tags": [
          "Team"
        ],
        "operationId": "createTeam",
        "summary": "Create a team",
        "description": "A named group of members and the areas of the store they can use (up to 50 teams).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 60,
                    "example": "Support"
                  },
                  "areas": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "products",
                        "orders",
                        "emails",
                        "marketing",
                        "analytics",
                        "developers"
                      ]
                    },
                    "description": "Parts of the store the team can use: `products` (products and store design), `orders` (orders, customers and refunds), `emails` (emails and automations), `marketing` (discounts, upsells and affiliates), `analytics`, `developers` (API keys and webhooks). Unknown values are dropped.",
                    "example": [
                      "orders",
                      "emails"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Team created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Team"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include team members (Growth and above), every seat is taken (`code: SEAT_LIMIT`, `details.limit`), or the role is not allowed. Body for the plan: `code: FEATURE_LOCKED`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "teams",
                    "currentPlan": "tier1",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/team/teams/{id}": {
      "patch": {
        "tags": [
          "Team"
        ],
        "operationId": "updateTeam",
        "summary": "Rename a team or change its areas",
        "description": "Applies to the team's members on their next request.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Team id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 60
                  },
                  "areas": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "products",
                        "orders",
                        "emails",
                        "marketing",
                        "analytics",
                        "developers"
                      ]
                    },
                    "description": "Parts of the store the team can use: `products` (products and store design), `orders` (orders, customers and refunds), `emails` (emails and automations), `marketing` (discounts, upsells and affiliates), `analytics`, `developers` (API keys and webhooks). Unknown values are dropped.",
                    "example": [
                      "orders",
                      "emails"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Team"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include team members (Growth and above), every seat is taken (`code: SEAT_LIMIT`, `details.limit`), or the role is not allowed. Body for the plan: `code: FEATURE_LOCKED`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "teams",
                    "currentPlan": "tier1",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "tags": [
          "Team"
        ],
        "operationId": "deleteTeam",
        "summary": "Delete a team",
        "description": "Its members stay on the store but lose the team's areas.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Team id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/referrals/me": {
      "get": {
        "operationId": "getReferralStats",
        "summary": "Get your referral link and what it earned",
        "description": "Your referral link, the stores it (and your badge) brought in, paying stores still in the 14-day refund window, and your free months: earned, applied to a plan invoice, banked, and how many of the 12 a year you used. Free on every plan.",
        "tags": [
          "Referrals"
        ],
        "responses": {
          "200": {
            "description": "Referral stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "example": "PF-k3x9q2m7v1ab"
                    },
                    "link": {
                      "type": "string",
                      "example": "https://www.pocketsflow.com/?ref=PF-k3x9q2m7v1ab",
                      "description": "Share this link: a store that signs up through it is yours"
                    },
                    "signups": {
                      "type": "integer",
                      "description": "Stores that signed up (link and badge)"
                    },
                    "clearing": {
                      "type": "integer",
                      "description": "Paying stores inside the 14-day refund window"
                    },
                    "earnedStores": {
                      "type": "integer"
                    },
                    "freeMonths": {
                      "type": "object",
                      "properties": {
                        "earned": {
                          "type": "integer"
                        },
                        "applied": {
                          "type": "integer"
                        },
                        "banked": {
                          "type": "integer",
                          "description": "Earned and not used yet"
                        },
                        "earnedThisYear": {
                          "type": "integer"
                        },
                        "yearlyCap": {
                          "type": "integer",
                          "example": 12
                        }
                      }
                    },
                    "sources": {
                      "type": "object",
                      "properties": {
                        "link": {
                          "type": "object",
                          "properties": {
                            "signups": {
                              "type": "integer"
                            },
                            "clearing": {
                              "type": "integer"
                            },
                            "earned": {
                              "type": "integer"
                            }
                          }
                        },
                        "badge": {
                          "type": "object",
                          "properties": {
                            "signups": {
                              "type": "integer"
                            },
                            "clearing": {
                              "type": "integer"
                            },
                            "earned": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    },
                    "rules": {
                      "type": "object",
                      "properties": {
                        "clearanceDays": {
                          "type": "integer",
                          "example": 14
                        },
                        "yearlyCap": {
                          "type": "integer",
                          "example": 12
                        },
                        "autoApply": {
                          "type": "boolean",
                          "description": "true: earned months go onto your next monthly plan invoice automatically"
                        }
                      }
                    },
                    "recent": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "storeName": {
                            "type": "string",
                            "example": "Northwind Studio"
                          },
                          "subdomain": {
                            "type": "string",
                            "nullable": true,
                            "example": "northwind"
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "link",
                              "badge"
                            ],
                            "description": "Your referral link, or the Powered by Pocketsflow badge"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "signed_up",
                              "clearing",
                              "earned",
                              "not_eligible",
                              "capped",
                              "expired"
                            ],
                            "description": "`signed_up`: no paid plan yet. `clearing`: paid, inside the 14-day refund window. `earned`: you got a free month. `not_eligible`: this store can never earn one. `capped`: you had already earned 12 this year. `expired`: no payment within a year."
                          },
                          "signedUpAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "clearsAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "grantedAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "applied": {
                            "type": "boolean",
                            "description": "The free month was used on one of your plan invoices"
                          }
                        }
                      },
                      "description": "The 20 newest referrals"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/referrals/referred": {
      "get": {
        "operationId": "listReferredStores",
        "summary": "List the stores you referred",
        "description": "Every store that signed up through your referral link or badge, newest first, with where it stands. Pass `nextCursor` back as `before` for the next page.",
        "tags": [
          "Referrals"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "in": "query",
            "name": "before",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Cursor: `nextCursor` from the previous page"
          }
        ],
        "responses": {
          "200": {
            "description": "Referred stores",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "referrals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "storeName": {
                            "type": "string",
                            "example": "Northwind Studio"
                          },
                          "subdomain": {
                            "type": "string",
                            "nullable": true,
                            "example": "northwind"
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "link",
                              "badge"
                            ],
                            "description": "Your referral link, or the Powered by Pocketsflow badge"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "signed_up",
                              "clearing",
                              "earned",
                              "not_eligible",
                              "capped",
                              "expired"
                            ],
                            "description": "`signed_up`: no paid plan yet. `clearing`: paid, inside the 14-day refund window. `earned`: you got a free month. `not_eligible`: this store can never earn one. `capped`: you had already earned 12 this year. `expired`: no payment within a year."
                          },
                          "signedUpAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "clearsAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "grantedAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "applied": {
                            "type": "boolean",
                            "description": "The free month was used on one of your plan invoices"
                          }
                        }
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`before` is not a date",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/recommendations": {
      "get": {
        "operationId": "getRecommendations",
        "summary": "Get your newsletter recommendations",
        "description": "Your recommendation settings, the creators you recommend (with the subscribe clicks and confirmed subscribers each one got from you), and the creators who recommend you with what they sent.",
        "tags": [
          "Recommendations"
        ],
        "responses": {
          "200": {
            "description": "Recommendations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "showRecommendations": {
                      "type": "boolean",
                      "description": "Show your picks after someone subscribes to you"
                    },
                    "recommendable": {
                      "type": "boolean",
                      "description": "Other creators may recommend you (off by default)"
                    },
                    "maxPicks": {
                      "type": "integer",
                      "example": 3
                    },
                    "eligible": {
                      "type": "boolean",
                      "description": "Your store can take part (active, in good standing, at least 7 days old, storefront on)"
                    },
                    "ineligibleReason": {
                      "type": "string",
                      "nullable": true
                    },
                    "picks": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "username": {
                            "type": "string",
                            "example": "northwind"
                          },
                          "name": {
                            "type": "string",
                            "example": "Northwind Studio"
                          },
                          "bio": {
                            "type": "string",
                            "nullable": true
                          },
                          "picture": {
                            "type": "string",
                            "nullable": true
                          },
                          "url": {
                            "type": "string",
                            "example": "https://northwind.pocketsflow.com"
                          },
                          "blurb": {
                            "type": "string",
                            "nullable": true,
                            "maxLength": 140,
                            "description": "Why you recommend them, shown under their name"
                          },
                          "live": {
                            "type": "boolean",
                            "description": "false while they turned recommendations off or their store can't be shown; it comes back on its own"
                          },
                          "clicks": {
                            "type": "integer",
                            "description": "Subscribe clicks from your success message"
                          },
                          "subscribersSent": {
                            "type": "integer",
                            "description": "Confirmed subscribers you sent them"
                          },
                          "addedAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "recommendedYou": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "username": {
                            "type": "string",
                            "example": "northwind"
                          },
                          "name": {
                            "type": "string",
                            "example": "Northwind Studio"
                          },
                          "bio": {
                            "type": "string",
                            "nullable": true
                          },
                          "picture": {
                            "type": "string",
                            "nullable": true
                          },
                          "url": {
                            "type": "string",
                            "example": "https://northwind.pocketsflow.com"
                          },
                          "subscribersSent": {
                            "type": "integer",
                            "description": "Confirmed subscribers they sent you"
                          },
                          "clicks": {
                            "type": "integer"
                          },
                          "listingYouNow": {
                            "type": "boolean"
                          },
                          "since": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "subscribersSent": {
                          "type": "integer"
                        },
                        "subscribersReceived": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateRecommendations",
        "summary": "Update your newsletter recommendations",
        "description": "Turn the success-message recommendations on or off, allow or stop other creators recommending you (turning it off removes you from every list at once), and replace your picks (up to 3, in order). Each pick must allow recommendations and be an active store in good standing.",
        "tags": [
          "Recommendations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "showRecommendations": {
                    "type": "boolean"
                  },
                  "recommendable": {
                    "type": "boolean"
                  },
                  "picks": {
                    "type": "array",
                    "maxItems": 3,
                    "items": {
                      "type": "object",
                      "required": [
                        "username"
                      ],
                      "properties": {
                        "username": {
                          "type": "string",
                          "example": "northwind",
                          "description": "The creator's Pocketsflow subdomain"
                        },
                        "blurb": {
                          "type": "string",
                          "maxLength": 140
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated recommendations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "showRecommendations": {
                      "type": "boolean",
                      "description": "Show your picks after someone subscribes to you"
                    },
                    "recommendable": {
                      "type": "boolean",
                      "description": "Other creators may recommend you (off by default)"
                    },
                    "maxPicks": {
                      "type": "integer",
                      "example": 3
                    },
                    "eligible": {
                      "type": "boolean",
                      "description": "Your store can take part (active, in good standing, at least 7 days old, storefront on)"
                    },
                    "ineligibleReason": {
                      "type": "string",
                      "nullable": true
                    },
                    "picks": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "username": {
                            "type": "string",
                            "example": "northwind"
                          },
                          "name": {
                            "type": "string",
                            "example": "Northwind Studio"
                          },
                          "bio": {
                            "type": "string",
                            "nullable": true
                          },
                          "picture": {
                            "type": "string",
                            "nullable": true
                          },
                          "url": {
                            "type": "string",
                            "example": "https://northwind.pocketsflow.com"
                          },
                          "blurb": {
                            "type": "string",
                            "nullable": true,
                            "maxLength": 140,
                            "description": "Why you recommend them, shown under their name"
                          },
                          "live": {
                            "type": "boolean",
                            "description": "false while they turned recommendations off or their store can't be shown; it comes back on its own"
                          },
                          "clicks": {
                            "type": "integer",
                            "description": "Subscribe clicks from your success message"
                          },
                          "subscribersSent": {
                            "type": "integer",
                            "description": "Confirmed subscribers you sent them"
                          },
                          "addedAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "recommendedYou": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "username": {
                            "type": "string",
                            "example": "northwind"
                          },
                          "name": {
                            "type": "string",
                            "example": "Northwind Studio"
                          },
                          "bio": {
                            "type": "string",
                            "nullable": true
                          },
                          "picture": {
                            "type": "string",
                            "nullable": true
                          },
                          "url": {
                            "type": "string",
                            "example": "https://northwind.pocketsflow.com"
                          },
                          "subscribersSent": {
                            "type": "integer",
                            "description": "Confirmed subscribers they sent you"
                          },
                          "clicks": {
                            "type": "integer"
                          },
                          "listingYouNow": {
                            "type": "boolean"
                          },
                          "since": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "subscribersSent": {
                          "type": "integer"
                        },
                        "subscribersReceived": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input, more than 3 picks, yourself, or a creator who doesn't accept recommendations (`code`: TOO_MANY, SELF, DUPLICATE, NOT_FOUND, NOT_RECOMMENDABLE, NOT_ELIGIBLE)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/recommendations/search": {
      "get": {
        "operationId": "searchRecommendableCreators",
        "summary": "Find creators to recommend",
        "description": "Creators who allow recommendations, by the start of their subdomain (2 characters or more). Up to 8 results.",
        "tags": [
          "Recommendations"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "q",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2,
              "example": "north"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching creators",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "username": {
                            "type": "string",
                            "example": "northwind"
                          },
                          "name": {
                            "type": "string",
                            "example": "Northwind Studio"
                          },
                          "bio": {
                            "type": "string",
                            "nullable": true
                          },
                          "picture": {
                            "type": "string",
                            "nullable": true
                          },
                          "url": {
                            "type": "string",
                            "example": "https://northwind.pocketsflow.com"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/agency": {
      "get": {
        "operationId": "getAgency",
        "summary": "Get your agency",
        "description": "Your agency profile, code, share and payout status.",
        "tags": [
          "Agencies"
        ],
        "responses": {
          "200": {
            "description": "Your agency",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string",
                      "example": "Northwind Studio"
                    },
                    "website": {
                      "type": "string",
                      "nullable": true,
                      "example": "https://northwind.studio"
                    },
                    "code": {
                      "type": "string",
                      "example": "AG-7KQ2M9XP",
                      "description": "Give this to clients"
                    },
                    "inviteLink": {
                      "type": "string",
                      "example": "https://app.pocketsflow.com/profile/agency?code=AG-7KQ2M9XP"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "suspended"
                      ]
                    },
                    "sharePercent": {
                      "type": "number",
                      "example": 20
                    },
                    "shareMonths": {
                      "type": "integer",
                      "example": 12
                    },
                    "payouts": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "description": "false: earnings are recorded and shown, payouts have not started yet"
                        },
                        "minimumCents": {
                          "type": "integer",
                          "example": 2500
                        }
                      }
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "You don't run an agency (`code: NOT_AGENCY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createAgency",
        "summary": "Join the agency program",
        "description": "Turn your account into an agency. Free; one agency per account.",
        "tags": [
          "Agencies"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 80,
                    "example": "Northwind Studio"
                  },
                  "website": {
                    "type": "string",
                    "nullable": true,
                    "example": "northwind.studio"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Your new agency",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string",
                      "example": "Northwind Studio"
                    },
                    "website": {
                      "type": "string",
                      "nullable": true,
                      "example": "https://northwind.studio"
                    },
                    "code": {
                      "type": "string",
                      "example": "AG-7KQ2M9XP",
                      "description": "Give this to clients"
                    },
                    "inviteLink": {
                      "type": "string",
                      "example": "https://app.pocketsflow.com/profile/agency?code=AG-7KQ2M9XP"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "suspended"
                      ]
                    },
                    "sharePercent": {
                      "type": "number",
                      "example": 20
                    },
                    "shareMonths": {
                      "type": "integer",
                      "example": 12
                    },
                    "payouts": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "description": "false: earnings are recorded and shown, payouts have not started yet"
                        },
                        "minimumCents": {
                          "type": "integer",
                          "example": 2500
                        }
                      }
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid name or website",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "You already run an agency",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateAgency",
        "summary": "Update your agency",
        "tags": [
          "Agencies"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 80,
                    "example": "Northwind Studio"
                  },
                  "website": {
                    "type": "string",
                    "nullable": true,
                    "example": "northwind.studio"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Your agency",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string",
                      "example": "Northwind Studio"
                    },
                    "website": {
                      "type": "string",
                      "nullable": true,
                      "example": "https://northwind.studio"
                    },
                    "code": {
                      "type": "string",
                      "example": "AG-7KQ2M9XP",
                      "description": "Give this to clients"
                    },
                    "inviteLink": {
                      "type": "string",
                      "example": "https://app.pocketsflow.com/profile/agency?code=AG-7KQ2M9XP"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "suspended"
                      ]
                    },
                    "sharePercent": {
                      "type": "number",
                      "example": 20
                    },
                    "shareMonths": {
                      "type": "integer",
                      "example": 12
                    },
                    "payouts": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "description": "false: earnings are recorded and shown, payouts have not started yet"
                        },
                        "minimumCents": {
                          "type": "integer",
                          "example": 2500
                        }
                      }
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid name or website",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "You don't run an agency (`code: NOT_AGENCY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/agency/clients": {
      "get": {
        "operationId": "listAgencyClients",
        "summary": "List your client stores",
        "description": "Every store that invited your agency, with its plan, sales and orders in the last 30 days, subscribers and what you earned from it, plus totals.",
        "tags": [
          "Agencies"
        ],
        "responses": {
          "200": {
            "description": "Client stores",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "clients": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "storeUserId": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "subdomain": {
                            "type": "string",
                            "nullable": true
                          },
                          "plan": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              }
                            }
                          },
                          "revenue30dCents": {
                            "type": "integer",
                            "description": "Store sales in the last 30 days (USD), in cents",
                            "nullable": true
                          },
                          "orders30d": {
                            "type": "integer",
                            "nullable": true
                          },
                          "subscribers": {
                            "type": "integer"
                          },
                          "earnedCents": {
                            "type": "integer",
                            "description": "Your earnings from this store so far, in cents"
                          },
                          "pendingCents": {
                            "type": "integer",
                            "description": "Of which still inside the refund window, in cents"
                          },
                          "startedAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "earnUntil": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "earning": {
                            "type": "boolean",
                            "description": "Inside the 12-month window"
                          },
                          "available": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "clients": {
                          "type": "integer"
                        },
                        "revenue30dCents": {
                          "type": "integer",
                          "description": "Client sales, last 30 days, in cents"
                        },
                        "earnedThisMonthCents": {
                          "type": "integer",
                          "description": "Earned this month, in cents"
                        },
                        "pendingCents": {
                          "type": "integer",
                          "description": "Inside the refund window, in cents"
                        },
                        "clearedCents": {
                          "type": "integer",
                          "description": "Cleared, not paid yet, in cents"
                        },
                        "paidCents": {
                          "type": "integer",
                          "description": "Paid out, in cents"
                        },
                        "lifetimeCents": {
                          "type": "integer",
                          "description": "Earned since you joined, in cents"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "You don't run an agency (`code: NOT_AGENCY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/agency/clients/{storeUserId}": {
      "delete": {
        "operationId": "removeAgencyClient",
        "summary": "Stop managing a client store",
        "description": "Your admin access to the store ends and you stop earning from its plan payments. Earnings already recorded stay.",
        "tags": [
          "Agencies"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "storeUserId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Not one of your clients",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/agency/statements": {
      "get": {
        "operationId": "listAgencyStatements",
        "summary": "List your monthly statements",
        "description": "What you earned each month (last 24 months), by status.",
        "tags": [
          "Agencies"
        ],
        "responses": {
          "200": {
            "description": "Monthly totals",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "statements": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "month": {
                            "type": "string",
                            "example": "2026-10"
                          },
                          "earnedCents": {
                            "type": "integer",
                            "description": "Earned, in cents"
                          },
                          "pendingCents": {
                            "type": "integer",
                            "description": "Inside the refund window, in cents"
                          },
                          "clearedCents": {
                            "type": "integer",
                            "description": "Cleared, in cents"
                          },
                          "paidCents": {
                            "type": "integer",
                            "description": "Paid out, in cents"
                          },
                          "payments": {
                            "type": "integer"
                          },
                          "refunds": {
                            "type": "integer"
                          },
                          "clients": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "You don't run an agency (`code: NOT_AGENCY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/agency/statements/{month}": {
      "get": {
        "operationId": "getAgencyStatement",
        "summary": "Get one monthly statement",
        "description": "Every client plan payment (and refund) of the month with your share.",
        "tags": [
          "Agencies"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "month",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}$",
              "example": "2026-10"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The statement",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "month": {
                      "type": "string"
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "baseCents": {
                          "type": "integer",
                          "description": "Client plan payments, net of refunds, in cents"
                        },
                        "earnedCents": {
                          "type": "integer",
                          "description": "Your share, in cents"
                        },
                        "pendingCents": {
                          "type": "integer",
                          "description": "Inside the refund window, in cents"
                        },
                        "paidCents": {
                          "type": "integer",
                          "description": "Paid out, in cents"
                        }
                      }
                    },
                    "lines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "storeName": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "payment",
                              "reversal"
                            ]
                          },
                          "paidAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "baseCents": {
                            "type": "integer",
                            "description": "Plan payment, net of discounts and tax, in cents"
                          },
                          "refundedCents": {
                            "type": "integer",
                            "description": "Refunded, in cents"
                          },
                          "sharePercent": {
                            "type": "number"
                          },
                          "amountCents": {
                            "type": "integer",
                            "description": "Your share (negative for a refund after payout), in cents"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "cleared",
                              "reversed",
                              "rejected",
                              "paid"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Month must look like 2026-10",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "You don't run an agency (`code: NOT_AGENCY`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/agency-access": {
      "get": {
        "operationId": "getStoreAgency",
        "summary": "Get the agency that manages your store",
        "tags": [
          "Agencies"
        ],
        "responses": {
          "200": {
            "description": "The agency, or null",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agency": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "website": {
                          "type": "string",
                          "nullable": true
                        },
                        "code": {
                          "type": "string"
                        },
                        "contactEmail": {
                          "type": "string",
                          "nullable": true
                        },
                        "role": {
                          "type": "string",
                          "enum": [
                            "admin"
                          ]
                        },
                        "startedAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "inviteAgency",
        "summary": "Invite an agency to your store",
        "description": "Give an agency admin access to your store with its agency code. One agency at a time; you can remove it whenever you like.",
        "tags": [
          "Agencies"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "example": "AG-7KQ2M9XP"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already connected to this agency",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agency": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "website": {
                          "type": "string",
                          "nullable": true
                        },
                        "code": {
                          "type": "string"
                        },
                        "contactEmail": {
                          "type": "string",
                          "nullable": true
                        },
                        "role": {
                          "type": "string",
                          "enum": [
                            "admin"
                          ]
                        },
                        "startedAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Connected",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agency": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "website": {
                          "type": "string",
                          "nullable": true
                        },
                        "code": {
                          "type": "string"
                        },
                        "contactEmail": {
                          "type": "string",
                          "nullable": true
                        },
                        "role": {
                          "type": "string",
                          "enum": [
                            "admin"
                          ]
                        },
                        "startedAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid code or your own agency",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No agency uses this code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Another agency manages your store (`code: AGENCY_ALREADY_CONNECTED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "removeStoreAgency",
        "summary": "Remove your store's agency",
        "description": "The agency's access ends at once and it stops earning from your plan.",
        "tags": [
          "Agencies"
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No agency manages your store",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/agency-access/preview/{code}": {
      "get": {
        "operationId": "previewAgency",
        "summary": "Look up an agency code",
        "description": "Who an agency code belongs to, before you invite it.",
        "tags": [
          "Agencies"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "code",
            "required": true,
            "schema": {
              "type": "string",
              "example": "AG-7KQ2M9XP"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The agency",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "website": {
                      "type": "string",
                      "nullable": true
                    },
                    "code": {
                      "type": "string"
                    },
                    "contactEmail": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid code, or your own agency",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No agency uses this code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/post-purchase/products/{id}": {
      "get": {
        "operationId": "getPostPurchaseOffer",
        "summary": "Get a product's post-purchase offer",
        "description": "The one-click offer shown right after a buyer pays for this product.",
        "tags": [
          "Upsells"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The product buyers purchase first"
          }
        ],
        "responses": {
          "200": {
            "description": "The offer settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "postPurchaseOffer": {
                      "$ref": "#/components/schemas/PostPurchaseOffer"
                    }
                  }
                },
                "example": {
                  "postPurchaseOffer": {
                    "enabled": true,
                    "productId": "665f1c2a9b3e4a0012ab34ce",
                    "discountPercent": 20
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updatePostPurchaseOffer",
        "summary": "Set a product's post-purchase offer",
        "description": "Choose the product to offer right after a buyer pays for this one, and an optional discount. Fields you leave out keep their value. Turning the offer on needs the Growth plan.",
        "tags": [
          "Upsells"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The product buyers purchase first"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Show the offer after checkout",
                    "example": true
                  },
                  "productId": {
                    "type": "string",
                    "description": "The product to offer (another fixed-price, one-time product of yours, same mode)",
                    "example": "665f1c2a9b3e4a0012ab34ce"
                  },
                  "discountPercent": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 90,
                    "description": "Percent off the offered product (0-90)",
                    "example": 20
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved offer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "postPurchaseOffer": {
                      "$ref": "#/components/schemas/PostPurchaseOffer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid settings — e.g. the offered product is this product, another seller's, pay-what-you-want, or under 1.00 after the discount (`code` says which)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "The offer price must be at least 1.00 after the discount",
                  "code": "too_cheap"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FEATURE_LOCKED — turning this on needs the Growth plan (or higher). Turning it off and reading the settings are never gated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "postPurchaseUpsells",
                    "currentPlan": "tier1",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/social-proof/products/{id}/settings": {
      "get": {
        "operationId": "getSocialProofSettings",
        "summary": "Get a product's checkout social proof settings",
        "description": "What this product's checkout may show about real purchases.",
        "tags": [
          "Checkout"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "socialProof": {
                      "$ref": "#/components/schemas/SocialProofSettings"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateSocialProofSettings",
        "summary": "Set a product's checkout social proof",
        "description": "Turn checkout social proof on or off and choose what it shows. Fields you leave out keep their value. Turning it on needs the Growth plan. Changes reach checkouts within about a minute.",
        "tags": [
          "Checkout"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "example": true
                  },
                  "purchaseCount": {
                    "type": "boolean",
                    "example": true
                  },
                  "countWindow": {
                    "type": "string",
                    "enum": [
                      "24h",
                      "7d"
                    ],
                    "example": "24h"
                  },
                  "threshold": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000,
                    "example": 3
                  },
                  "recentPurchases": {
                    "type": "boolean",
                    "example": true
                  },
                  "rating": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "socialProof": {
                      "$ref": "#/components/schemas/SocialProofSettings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FEATURE_LOCKED — turning this on needs the Growth plan (or higher). Turning it off and reading the settings are never gated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "socialProof",
                    "currentPlan": "tier1",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/social-proof/products/{id}": {
      "get": {
        "operationId": "getSocialProof",
        "summary": "Get a product's checkout social proof",
        "description": "PUBLIC — no API key. The aggregate numbers a checkout shows for this product (cached about 60 seconds). Use it to show the same proof on your own page.",
        "tags": [
          "Checkout"
        ],
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The aggregate social proof",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SocialProof"
                },
                "example": {
                  "enabled": true,
                  "purchases": {
                    "count": 14,
                    "window": "24h"
                  },
                  "recent": {
                    "place": "Lyon",
                    "placeKind": "city",
                    "minutesAgo": 120
                  },
                  "rating": {
                    "average": 4.8,
                    "count": 15
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/loyalty": {
      "get": {
        "operationId": "getLoyaltySettings",
        "summary": "Get loyalty settings",
        "description": "Your loyalty program: earn rate, credit value, minimum and expiry.",
        "tags": [
          "Loyalty"
        ],
        "responses": {
          "200": {
            "description": "The settings (defaults when never saved)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoyaltySettings"
                },
                "example": {
                  "enabled": true,
                  "earnPointsPerUnit": 1,
                  "pointsPerCreditUnit": 100,
                  "minRedeemPoints": 0,
                  "expiryDays": 365,
                  "currency": "usd",
                  "walletLinksConfigured": true,
                  "planAllows": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateLoyaltySettings",
        "summary": "Update loyalty settings",
        "description": "Turn loyalty on or off and set the rates. Fields you leave out keep their value. A new `expiryDays` applies to every balance from its last purchase. Turning it on (or changing rates) needs the Growth plan; `{ \"enabled\": false }` alone is always allowed.",
        "tags": [
          "Loyalty"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Buyers earn and can spend points. Turning it off stops both (balances are kept).",
                    "example": true
                  },
                  "earnPointsPerUnit": {
                    "type": "number",
                    "minimum": 0.01,
                    "maximum": 1000,
                    "description": "Points earned per 1.00 paid, in your currency (2 decimals)",
                    "example": 1
                  },
                  "pointsPerCreditUnit": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100000,
                    "description": "Points that make 1.00 of store credit",
                    "example": 100
                  },
                  "minRedeemPoints": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Smallest balance a buyer can spend (0 = any)",
                    "example": 0
                  },
                  "expiryDays": {
                    "type": "integer",
                    "nullable": true,
                    "minimum": 1,
                    "maximum": 3650,
                    "description": "Points expire this many days after the buyer's last purchase. `null` = never.",
                    "example": 365
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoyaltySettings"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FEATURE_LOCKED — turning loyalty on, changing its rates or adjusting a balance needs the Growth plan (or higher). Reading, and turning it off, are never gated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "loyalty",
                    "currentPlan": "tier1",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/loyalty/summary": {
      "get": {
        "operationId": "getLoyaltySummary",
        "summary": "Get loyalty liability",
        "description": "Outstanding points and their store-credit value (what you owe buyers), and totals earned, redeemed and expired. Current mode (live / test).",
        "tags": [
          "Loyalty"
        ],
        "responses": {
          "200": {
            "description": "The summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoyaltySummary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/loyalty/members": {
      "get": {
        "operationId": "listLoyaltyMembers",
        "summary": "List loyalty members",
        "description": "Buyers with a balance, largest first.",
        "tags": [
          "Loyalty"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            },
            "description": "Rows per page (max 100)"
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Rows to skip"
          },
          {
            "in": "query",
            "name": "search",
            "schema": {
              "type": "string"
            },
            "description": "Email prefix"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of members",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "members": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LoyaltyMember"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/loyalty/ledger": {
      "get": {
        "operationId": "listLoyaltyLedger",
        "summary": "List loyalty activity",
        "description": "Every points movement, newest first — all buyers, or one buyer with `email`.",
        "tags": [
          "Loyalty"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            },
            "description": "Rows per page (max 100)"
          },
          {
            "in": "query",
            "name": "email",
            "schema": {
              "type": "string"
            },
            "description": "Only this buyer's movements"
          }
        ],
        "responses": {
          "200": {
            "description": "The movements",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LoyaltyLedgerEntry"
                      }
                    },
                    "currency": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/loyalty/members/adjust": {
      "post": {
        "operationId": "adjustLoyaltyPoints",
        "summary": "Adjust a buyer's points",
        "description": "Add points (a goodwill gesture, a migration) or remove them. A balance never goes below 0. Send an `idempotencyKey` to make retries safe. Needs the Growth plan.",
        "tags": [
          "Loyalty"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "points"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "example": "ana@example.com"
                  },
                  "points": {
                    "type": "integer",
                    "description": "Positive adds, negative removes",
                    "example": 500
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 200,
                    "example": "Sorry for the delay"
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "description": "The same key never adjusts twice",
                    "example": "migration-2026-10-ana"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The buyer's balance after the change",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "applied": {
                      "type": "boolean",
                      "description": "False when this idempotencyKey was already used"
                    },
                    "member": {
                      "$ref": "#/components/schemas/LoyaltyMember"
                    },
                    "currency": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FEATURE_LOCKED — turning loyalty on, changing its rates or adjusting a balance needs the Growth plan (or higher). Reading, and turning it off, are never gated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "loyalty",
                    "currentPlan": "tier1",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/affiliate-program": {
      "get": {
        "operationId": "getAffiliateTiers",
        "summary": "Get affiliate tiers and bonuses",
        "description": "Your commission ladder, its period and your milestone bonuses.",
        "tags": [
          "Affiliate tiers"
        ],
        "responses": {
          "200": {
            "description": "The program",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AffiliateProgram"
                },
                "example": {
                  "enabled": true,
                  "period": "month",
                  "tiers": [
                    {
                      "minSales": 0,
                      "rate": 20,
                      "label": "1–10"
                    },
                    {
                      "minSales": 10,
                      "rate": 25,
                      "label": "11–50"
                    },
                    {
                      "minSales": 50,
                      "rate": 30,
                      "label": "51+"
                    }
                  ],
                  "bonuses": [
                    {
                      "sales": 25,
                      "amount": 50
                    }
                  ],
                  "currency": "usd",
                  "periodStartsAt": "2026-10-01T00:00:00.000Z",
                  "periodEndsAt": "2026-11-01T00:00:00.000Z",
                  "planAllows": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateAffiliateTiers",
        "summary": "Update affiliate tiers and bonuses",
        "description": "Set the commission tiers (up to 10, by sales before the sale in the period), the period, and the milestone bonuses (up to 10). Fields you leave out keep their value; `tiers` and `bonuses` replace the whole list. New rates apply to checkouts created from now on — recorded orders keep their rate. Needs the Pro plan; `{ \"enabled\": false }` alone is always allowed.",
        "tags": [
          "Affiliate tiers"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "example": true
                  },
                  "period": {
                    "type": "string",
                    "enum": [
                      "all_time",
                      "month",
                      "quarter",
                      "year"
                    ],
                    "description": "What the tier counts: sales this calendar month / quarter / year (UTC), or all time",
                    "example": "month"
                  },
                  "tiers": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "object",
                      "required": [
                        "minSales",
                        "rate"
                      ],
                      "properties": {
                        "minSales": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "The tier applies once the affiliate has this many paid, non-refunded sales in the period before the sale (0 = from the first sale; 10 = from the 11th)",
                          "example": 10
                        },
                        "rate": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 100,
                          "description": "Commission, percent of the sale",
                          "example": 25
                        }
                      }
                    }
                  },
                  "bonuses": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "object",
                      "required": [
                        "sales",
                        "amount"
                      ],
                      "properties": {
                        "sales": {
                          "type": "integer",
                          "minimum": 1,
                          "description": "All-time paid sales that earn the bonus (25 = the 25th sale)",
                          "example": 25
                        },
                        "amount": {
                          "type": "number",
                          "description": "Bonus, in your currency",
                          "example": 50
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "enabled": true,
                "period": "month",
                "tiers": [
                  {
                    "minSales": 0,
                    "rate": 20
                  },
                  {
                    "minSales": 10,
                    "rate": 25
                  },
                  {
                    "minSales": 50,
                    "rate": 30
                  }
                ],
                "bonuses": [
                  {
                    "sales": 25,
                    "amount": 50
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved program",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AffiliateProgram"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FEATURE_LOCKED — turning tiers and bonuses on or changing them needs the Pro plan (or higher). Reading, and turning it off, are never gated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "tieredAffiliates",
                    "currentPlan": "tier1",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/affiliate-program/affiliates": {
      "get": {
        "operationId": "listAffiliateTierProgress",
        "summary": "List affiliates' tier progress",
        "description": "Each affiliate's current tier, the rate their next sale earns, and how far the next tier and bonus are (current mode, up to 200 affiliates).",
        "tags": [
          "Affiliate tiers"
        ],
        "responses": {
          "200": {
            "description": "Progress per affiliate",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "affiliates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "6711b4e69e3f1a0012cd8899"
                          },
                          "email": {
                            "type": "string",
                            "example": "leo@example.com"
                          },
                          "active": {
                            "type": "boolean"
                          },
                          "progress": {
                            "$ref": "#/components/schemas/AffiliateTierProgress"
                          },
                          "bonusesEarned": {
                            "type": "integer",
                            "example": 1
                          }
                        }
                      }
                    },
                    "currency": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/affiliate-program/affiliates/{id}": {
      "get": {
        "operationId": "getAffiliateTierProgress",
        "summary": "Get one affiliate's tier progress",
        "description": "The affiliate's progress and every milestone bonus they earned.",
        "tags": [
          "Affiliate tiers"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711b4e69e3f1a0012cd8899"
            },
            "description": "Affiliate id"
          }
        ],
        "responses": {
          "200": {
            "description": "The affiliate's progress",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "active": {
                      "type": "boolean"
                    },
                    "progress": {
                      "$ref": "#/components/schemas/AffiliateTierProgress"
                    },
                    "bonuses": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AffiliateBonus"
                      }
                    },
                    "currency": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/affiliate-program/bonuses": {
      "get": {
        "operationId": "listAffiliateBonuses",
        "summary": "List affiliate milestone bonuses",
        "description": "Bonuses your affiliates earned, newest first (`status` filters; default: earned and funded).",
        "tags": [
          "Affiliate tiers"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Rows per page (max 100)"
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "earned",
                "funded"
              ]
            },
            "description": "Only bonuses in this state"
          }
        ],
        "responses": {
          "200": {
            "description": "The bonuses",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bonuses": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/AffiliateBonus"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "affiliateEmail": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        ]
                      }
                    },
                    "currency": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/affiliate-program/mine": {
      "get": {
        "operationId": "getMyAffiliateTierProgress",
        "summary": "Get my progress as an affiliate",
        "description": "For the account behind the key as an AFFILIATE: your tier, rate, and next tier / bonus with every creator you promote (live sales).",
        "tags": [
          "Affiliate tiers"
        ],
        "responses": {
          "200": {
            "description": "Your progress per creator",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "programs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "affiliateId": {
                            "type": "string",
                            "example": "6711b4e69e3f1a0012cd8899"
                          },
                          "creator": {
                            "type": "object",
                            "properties": {
                              "name": {
                                "type": "string"
                              },
                              "subdomain": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          },
                          "currency": {
                            "type": "string"
                          },
                          "progress": {
                            "$ref": "#/components/schemas/AffiliateTierProgress"
                          },
                          "bonuses": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/AffiliateBonus"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/limited-drop": {
      "get": {
        "operationId": "getLimitedDrop",
        "summary": "Get a product's limited drop",
        "description": "The drop's quantity and its live counts: sold, held in open checkouts, and left.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The limited drop",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "limitedDrop": {
                      "$ref": "#/components/schemas/LimitedDrop"
                    }
                  }
                },
                "example": {
                  "limitedDrop": {
                    "enabled": true,
                    "quantity": 100,
                    "restockOnRefund": false,
                    "sold": 63,
                    "reserved": 2,
                    "remaining": 35,
                    "soldOut": false,
                    "startedAt": "2026-10-16T09:00:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateLimitedDrop",
        "summary": "Set a product's limited drop",
        "description": "Sell a fixed number of copies (“Only 100 copies”). When every copy is sold, the checkout refuses new buyers with `409 SOLD_OUT` and the product page shows Sold out. Fields you leave out keep their value. Raising the quantity reopens sales. Turning the drop on needs the Starter plan; turning it off never does. The counts are never written directly.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Turn the drop on or off",
                    "example": true
                  },
                  "quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000000,
                    "description": "Copies for sale (needed to turn the drop on)",
                    "example": 100
                  },
                  "restockOnRefund": {
                    "type": "boolean",
                    "description": "Put a refunded order's copy back on sale",
                    "example": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved drop",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "limitedDrop": {
                      "$ref": "#/components/schemas/LimitedDrop"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The settings are invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Set how many copies are for sale (quantity) to start the drop"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — turning a limited drop on needs the Starter plan or higher (feature `limitedDrops`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "limitedDrops",
                    "currentPlan": "free",
                    "requiredPlan": "tier1",
                    "requiredPlanName": "Starter",
                    "requiredPlanPrice": 15,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/drop": {
      "get": {
        "operationId": "getLimitedDropAvailability",
        "summary": "Get how many copies of a drop are left",
        "description": "PUBLIC — no API key. The live counter a product page shows (cached a few seconds). Use it to show “35 of 100 left” on your own site.",
        "tags": [
          "Products"
        ],
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The drop's availability",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LimitedDropAvailability"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/pdf-stamping": {
      "get": {
        "operationId": "getPdfStamping",
        "summary": "Get a product's PDF stamping setting",
        "description": "Whether the PDFs buyers download carry their email, order number and date.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The setting",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pdfStamping": {
                      "$ref": "#/components/schemas/PdfStamping"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updatePdfStamping",
        "summary": "Turn PDF stamping on or off",
        "description": "When on, every PDF of this product is stamped with the buyer's email, order number and date on each page as they download it. If a file can't be stamped, the buyer gets the original. Turning it on needs the Starter plan; turning it off never does.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved setting",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pdfStamping": {
                      "$ref": "#/components/schemas/PdfStamping"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The settings are invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "enabled must be a boolean"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — turning PDF stamping on needs the Starter plan or higher (feature `pdfStamping`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "pdfStamping",
                    "currentPlan": "free",
                    "requiredPlan": "tier1",
                    "requiredPlanName": "Starter",
                    "requiredPlanPrice": 15,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/download-limits": {
      "get": {
        "operationId": "getDownloadLimits",
        "summary": "Get a product's download limits",
        "description": "How many times each file can be downloaded per order, and for how long.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The limits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "downloadLimits": {
                      "$ref": "#/components/schemas/DownloadLimits"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateDownloadLimits",
        "summary": "Set a product's download limits",
        "description": "Limit downloads per file per order and/or make the links expire some days after the purchase. Fields you leave out keep their value; `null` clears a limit. Never retroactive: only orders placed after the limits were turned on are limited, unless `applyToExisting` is true. Turning them on needs the Growth plan; turning them off never does.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "example": true
                  },
                  "maxDownloads": {
                    "type": "integer",
                    "nullable": true,
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "Downloads per file per order; null = no count limit",
                    "example": 5
                  },
                  "expiryDays": {
                    "type": "integer",
                    "nullable": true,
                    "minimum": 1,
                    "maximum": 3650,
                    "description": "Days the links work after the purchase; null = never",
                    "example": 7
                  },
                  "applyToExisting": {
                    "type": "boolean",
                    "description": "Also limit orders placed before now",
                    "example": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved limits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "downloadLimits": {
                      "$ref": "#/components/schemas/DownloadLimits"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The settings are invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Set a download limit (maxDownloads) or a link expiry (expiryDays) to turn limits on"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — turning download limits on needs the Growth plan or higher (feature `downloadLimits`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "downloadLimits",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/orders/{id}/downloads": {
      "get": {
        "operationId": "getOrderDownloads",
        "summary": "Get an order's downloads",
        "description": "What the buyer of this order has downloaded, what they have left and when their links expire.",
        "tags": [
          "Orders"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Order id"
          }
        ],
        "responses": {
          "200": {
            "description": "The order's downloads",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "downloads": {
                      "$ref": "#/components/schemas/OrderDownloads"
                    }
                  }
                },
                "example": {
                  "downloads": {
                    "limited": true,
                    "maxDownloads": 5,
                    "expiresAt": "2026-10-23T09:12:00.000Z",
                    "expired": false,
                    "remaining": 3,
                    "exhausted": false,
                    "files": [
                      {
                        "key": "main",
                        "name": "Field Guide",
                        "used": 2,
                        "remaining": 3
                      }
                    ],
                    "stamped": true,
                    "totalDownloads": 2,
                    "lastDownloadAt": "2026-10-16T10:02:00.000Z",
                    "resetAt": null,
                    "resetCount": 0
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/orders/{id}/downloads/reset": {
      "post": {
        "operationId": "resetOrderDownloads",
        "summary": "Reset a buyer's downloads",
        "description": "Give the buyer of this order all their downloads back. The link expiry starts again from now. Never gated by plan.",
        "tags": [
          "Orders"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Order id"
          }
        ],
        "responses": {
          "200": {
            "description": "The order's downloads after the reset",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "downloads": {
                      "$ref": "#/components/schemas/OrderDownloads"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid order id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Invalid order id"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/funnels/templates": {
      "get": {
        "operationId": "listFunnelTemplates",
        "summary": "List funnel sequence templates",
        "description": "The three sequence templates: `welcome` (Welcome → value → offer, 5 emails over 7 days), `minicourse` (4 lessons, one a day) and `launch` (3 emails over 72 hours).",
        "tags": [
          "Sales funnels"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "leadMagnetName",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 120
            },
            "description": "Write the subjects with this lead magnet name"
          },
          {
            "in": "query",
            "name": "offerName",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 120
            },
            "description": "Write the subjects with this offer name"
          }
        ],
        "responses": {
          "200": {
            "description": "The templates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "templates": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FunnelTemplate"
                      }
                    }
                  }
                },
                "example": {
                  "templates": [
                    {
                      "id": "launch",
                      "emails": [
                        {
                          "subject": "your product is here",
                          "sendAfterHours": 0,
                          "offer": "full"
                        },
                        {
                          "subject": "What's inside your product",
                          "sendAfterHours": 24,
                          "offer": "full"
                        },
                        {
                          "subject": "Last call for your product",
                          "sendAfterHours": 72,
                          "offer": "full"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/funnels": {
      "get": {
        "operationId": "listFunnels",
        "summary": "List funnels",
        "description": "Your sales funnels in the key's mode (live or test), newest first, with their lead magnet and offer products and the last computed stats. Open on every plan.",
        "tags": [
          "Sales funnels"
        ],
        "responses": {
          "200": {
            "description": "Your funnels",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "funnels": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Funnel"
                      }
                    }
                  }
                },
                "example": {
                  "funnels": [
                    {
                      "_id": "6710c3d59e3f1a0012cd7a01",
                      "name": "Free Notion Starter → Notion Business OS",
                      "templateId": "welcome",
                      "status": "active",
                      "leadMagnetProductId": "665f1c2a9b3e4a0012ab34cd",
                      "offerProductId": "665f1c2a9b3e4a0012ab34ce",
                      "offerUrl": "https://acme.pocketsflow.com/notion-os",
                      "offerDiscount": {
                        "discountId": "6710c3d59e3f1a0012cd7a09",
                        "code": "FUNNEL20",
                        "valueType": "percentage",
                        "value": 20,
                        "expiresAt": null
                      },
                      "automationId": "6710c3d59e3f1a0012cd7a02",
                      "emailPostIds": [
                        "6710c3d59e3f1a0012cd7a03",
                        "6710c3d59e3f1a0012cd7a04",
                        "6710c3d59e3f1a0012cd7a05",
                        "6710c3d59e3f1a0012cd7a06",
                        "6710c3d59e3f1a0012cd7a07"
                      ],
                      "leadTag": "lead-free-notion-starter",
                      "activatedAt": "2026-10-02T10:00:00.000Z",
                      "stats": {
                        "leads": 412,
                        "emailsSent": 1630,
                        "opens": 702,
                        "clicks": 188,
                        "purchases": 23,
                        "revenue": 1127,
                        "conversionRate": 5.6,
                        "currency": "USD",
                        "updatedAt": "2026-10-16T09:30:00.000Z"
                      },
                      "createdAt": "2026-10-02T09:58:12.000Z",
                      "updatedAt": "2026-10-16T09:30:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createFunnel",
        "summary": "Create a funnel",
        "description": "Create a sales funnel from a template: creates the discount (when `discount` is sent), the email posts (status `automation`) and a draft automation that starts when someone gets the lead magnet. The funnel starts as `draft` — turn it on with `POST /funnels/{id}/activate`. `offerUrl` is the link the offer emails use; the discount code is appended as `?discount=`. Growth plan and above.",
        "tags": [
          "Sales funnels"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "templateId",
                  "leadMagnetProductId",
                  "offerProductId",
                  "offerUrl"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Defaults to `<lead magnet> → <offer>`"
                  },
                  "templateId": {
                    "type": "string",
                    "enum": [
                      "welcome",
                      "minicourse",
                      "launch"
                    ],
                    "description": "The sequence template (see `GET /funnels/templates`)"
                  },
                  "leadMagnetProductId": {
                    "type": "string",
                    "description": "One of your free products ($0, or pay what you want from $0)",
                    "example": "665f1c2a9b3e4a0012ab34cd"
                  },
                  "offerProductId": {
                    "type": "string",
                    "description": "One of your paid products",
                    "example": "665f1c2a9b3e4a0012ab34ce"
                  },
                  "offerUrl": {
                    "type": "string",
                    "description": "http(s) link to the offer: its product page or checkout",
                    "example": "https://acme.pocketsflow.com/notion-os"
                  },
                  "discount": {
                    "type": "object",
                    "nullable": true,
                    "description": "Optional code for the offer, written into the offer emails. A new discount code is created for the offer product.",
                    "required": [
                      "code",
                      "value"
                    ],
                    "properties": {
                      "code": {
                        "type": "string",
                        "description": "3-32 letters, numbers, `-` or `_` (stored uppercase)",
                        "example": "FUNNEL20"
                      },
                      "valueType": {
                        "type": "string",
                        "enum": [
                          "percentage",
                          "fixed"
                        ],
                        "default": "percentage"
                      },
                      "value": {
                        "type": "number",
                        "description": "Percent (1-100) or an amount in your currency",
                        "example": 20
                      },
                      "expiresAt": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "When the code stops working (future)"
                      }
                    }
                  }
                }
              },
              "example": {
                "templateId": "welcome",
                "leadMagnetProductId": "665f1c2a9b3e4a0012ab34cd",
                "offerProductId": "665f1c2a9b3e4a0012ab34ce",
                "offerUrl": "https://acme.pocketsflow.com/notion-os",
                "discount": {
                  "code": "FUNNEL20",
                  "valueType": "percentage",
                  "value": 20
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created funnel (draft)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Funnel"
                },
                "example": {
                  "_id": "6710c3d59e3f1a0012cd7a01",
                  "name": "Free Notion Starter → Notion Business OS",
                  "templateId": "welcome",
                  "status": "draft",
                  "leadMagnetProductId": "665f1c2a9b3e4a0012ab34cd",
                  "offerProductId": "665f1c2a9b3e4a0012ab34ce",
                  "offerUrl": "https://acme.pocketsflow.com/notion-os",
                  "offerDiscount": {
                    "discountId": "6710c3d59e3f1a0012cd7a09",
                    "code": "FUNNEL20",
                    "valueType": "percentage",
                    "value": 20,
                    "expiresAt": null
                  },
                  "automationId": "6710c3d59e3f1a0012cd7a02",
                  "emailPostIds": [
                    "6710c3d59e3f1a0012cd7a03",
                    "6710c3d59e3f1a0012cd7a04",
                    "6710c3d59e3f1a0012cd7a05",
                    "6710c3d59e3f1a0012cd7a06",
                    "6710c3d59e3f1a0012cd7a07"
                  ],
                  "leadTag": "lead-free-notion-starter",
                  "activatedAt": null,
                  "stats": {
                    "leads": 412,
                    "emailsSent": 1630,
                    "opens": 702,
                    "clicks": 188,
                    "purchases": 23,
                    "revenue": 1127,
                    "conversionRate": 5.6,
                    "currency": "USD",
                    "updatedAt": "2026-10-16T09:30:00.000Z"
                  },
                  "createdAt": "2026-10-02T09:58:12.000Z",
                  "updatedAt": "2026-10-16T09:30:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed: unknown template, the lead magnet is not one of your free products (`LEAD_MAGNET_NOT_FREE`), the offer is not paid (`OFFER_NOT_PAID`), or the discount code exists (`DISCOUNT_CODE_TAKEN`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "The lead magnet must be free (a $0 price, or pay what you want from $0)",
                  "code": "LEAD_MAGNET_NOT_FREE"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include sales funnels (Growth and above). Body: `code: FEATURE_LOCKED`, `details.feature: salesFunnels`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/funnels/{id}": {
      "get": {
        "operationId": "getFunnel",
        "summary": "Get a funnel",
        "description": "One funnel with its products, the emails of its sequence (in send order, with their timing) and the activation checklist.",
        "tags": [
          "Sales funnels"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7a01"
            },
            "description": "Funnel id"
          }
        ],
        "responses": {
          "200": {
            "description": "The funnel",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FunnelDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "renameFunnel",
        "summary": "Rename a funnel",
        "description": "Change the funnel's name. Everything else is edited on its automation and email posts.",
        "tags": [
          "Sales funnels"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7a01"
            },
            "description": "Funnel id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              },
              "example": {
                "name": "Notion starter funnel"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The renamed funnel",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Funnel"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteFunnel",
        "summary": "Delete a funnel",
        "description": "Delete the funnel, its leads and its sequence automation. Its email posts and discount code are kept (leads may already hold an email with that code).",
        "tags": [
          "Sales funnels"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7a01"
            },
            "description": "Funnel id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Funnel deleted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/funnels/{id}/activate": {
      "post": {
        "operationId": "activateFunnel",
        "summary": "Activate a funnel",
        "description": "Turn the funnel on: its automation is activated under the same rules as any automation (a paid plan — `PAID_PLAN_REQUIRED` — and a typed trigger), the sender email must be verified (`SENDER_NOT_VERIFIED`) and every email must exist (`EMAILS_MISSING`). Growth plan and above.",
        "tags": [
          "Sales funnels"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7a01"
            },
            "description": "Funnel id"
          }
        ],
        "responses": {
          "200": {
            "description": "The active funnel",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Funnel"
                }
              }
            }
          },
          "400": {
            "description": "Not ready: the sender is not verified, an email is missing, or the lead magnet is no longer free",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Verify your sender email in Email settings before you activate this funnel.",
                  "code": "SENDER_NOT_VERIFIED"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include sales funnels (Growth and above). Body: `code: FEATURE_LOCKED`, `details.feature: salesFunnels`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The funnel's automation was deleted (`SEQUENCE_MISSING`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/funnels/{id}/pause": {
      "post": {
        "operationId": "pauseFunnel",
        "summary": "Pause a funnel",
        "description": "Switch the funnel's automation off. New claims of the lead magnet no longer enter the funnel; leads already in the sequence receive the emails they were scheduled for, like any automation. Open on every plan.",
        "tags": [
          "Sales funnels"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7a01"
            },
            "description": "Funnel id"
          }
        ],
        "responses": {
          "200": {
            "description": "The paused funnel",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Funnel"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/funnels/{id}/stats": {
      "get": {
        "operationId": "getFunnelStats",
        "summary": "Get funnel stats",
        "description": "Leads, emails sent, unique opens and clicks per email, and attributed offer sales: a purchase of the offer counts when the buyer's email got the lead magnet first and bought within 30 days. Refunded sales never count. Revenue is in your account currency. Also refreshes the funnel's `stats`.",
        "tags": [
          "Sales funnels"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7a01"
            },
            "description": "Funnel id"
          }
        ],
        "responses": {
          "200": {
            "description": "The funnel's numbers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FunnelAnalytics"
                },
                "example": {
                  "funnelId": "6710c3d59e3f1a0012cd7a01",
                  "attributionWindowDays": 30,
                  "totals": {
                    "leads": 412,
                    "emailsSent": 1630,
                    "opens": 702,
                    "clicks": 188,
                    "purchases": 23,
                    "buyers": 23,
                    "revenue": 1127,
                    "conversionRate": 5.6,
                    "currency": "USD"
                  },
                  "stages": {
                    "leads": 412,
                    "opened": 301,
                    "clicked": 96,
                    "purchased": 23
                  },
                  "emails": [
                    {
                      "nodeId": "sendEmail-2",
                      "postId": "6710c3d59e3f1a0012cd7a03",
                      "subject": "Here's Free Notion Starter",
                      "sendAfterHours": 0,
                      "sent": 409,
                      "opened": 268,
                      "clicked": 41
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/revenue-splits": {
      "get": {
        "operationId": "listRevenueSplits",
        "summary": "List revenue splits",
        "description": "Every co-creator you invited to your products (all statuses), newest first, with what each one earned so far. Open on every plan.",
        "tags": [
          "Revenue splits"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "productId",
            "required": false,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Only this product's splits"
          },
          {
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "invited",
                "active",
                "declined",
                "removed",
                "expired"
              ]
            },
            "description": "Only splits in this status"
          }
        ],
        "responses": {
          "200": {
            "description": "Your splits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "splits": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RevenueSplit"
                      }
                    }
                  }
                },
                "example": {
                  "splits": [
                    {
                      "id": "6710c3d59e3f1a0012cd7c01",
                      "productId": "665f1c2a9b3e4a0012ab34cd",
                      "product": {
                        "id": "665f1c2a9b3e4a0012ab34cd",
                        "name": "Notion Business OS",
                        "thumbnail": "665f1c2a9b3e4a0012ab9911"
                      },
                      "owner": null,
                      "collaboratorEmail": "maya@studio.co",
                      "collaborator": {
                        "name": "Maya Chen"
                      },
                      "sharePercent": 30,
                      "shareBps": 3000,
                      "status": "active",
                      "invitedAt": "2026-10-01T09:12:00.000Z",
                      "inviteExpiresAt": "2026-10-15T09:12:00.000Z",
                      "acceptedAt": "2026-10-01T15:40:00.000Z",
                      "declinedAt": null,
                      "removedAt": null,
                      "totals": [
                        {
                          "currency": "usd",
                          "pendingCents": 41160,
                          "heldCents": 0,
                          "paidCents": 117600,
                          "reversedCents": 2940,
                          "clawbackOwedCents": 0,
                          "shareCount": 54
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createRevenueSplit",
        "summary": "Invite a co-creator",
        "description": "Invite a co-creator to one of your products with a % share. They get an email and must accept (with a payouts account set up) before sales are split; sales made before they accept are not split. Up to 5 co-creators per product; shares add up to 100% at most. Growth plan and above.",
        "tags": [
          "Revenue splits"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productId",
                  "email",
                  "sharePercent"
                ],
                "properties": {
                  "productId": {
                    "type": "string",
                    "description": "One of your products",
                    "example": "665f1c2a9b3e4a0012ab34cd"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "The co-creator's email (their Pocketsflow account email)",
                    "example": "maya@studio.co"
                  },
                  "sharePercent": {
                    "type": "number",
                    "minimum": 0.01,
                    "maximum": 100,
                    "description": "Their share of each sale's split base, in percent (up to 2 decimals)",
                    "example": 30
                  }
                }
              },
              "example": {
                "productId": "665f1c2a9b3e4a0012ab34cd",
                "email": "maya@studio.co",
                "sharePercent": 30
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The invite",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueSplit"
                },
                "example": {
                  "id": "6710c3d59e3f1a0012cd7c01",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "product": {
                    "id": "665f1c2a9b3e4a0012ab34cd",
                    "name": "Notion Business OS",
                    "thumbnail": "665f1c2a9b3e4a0012ab9911"
                  },
                  "owner": null,
                  "collaboratorEmail": "maya@studio.co",
                  "collaborator": null,
                  "sharePercent": 30,
                  "shareBps": 3000,
                  "status": "invited",
                  "invitedAt": "2026-10-01T09:12:00.000Z",
                  "inviteExpiresAt": "2026-10-15T09:12:00.000Z",
                  "acceptedAt": null,
                  "declinedAt": null,
                  "removedAt": null,
                  "totals": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — inviting a co-creator needs the Growth plan or higher (feature `revenueSplits`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "revenueSplits",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The co-creator is already on this product (`ALREADY_INVITED`), the product has 5 co-creators (`TOO_MANY_COLLABORATORS`) or the shares would go over 100% (`SHARES_OVER_100`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "SHARES_OVER_100",
                  "message": "Shares can add up to 100% at most. 20% is left on this product."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/revenue-splits/products/{id}": {
      "get": {
        "operationId": "getProductRevenueSplit",
        "summary": "Get a product's split",
        "description": "One of your products' split settings: its co-creators, the share you keep, slots left, and totals.",
        "tags": [
          "Revenue splits"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The product's split",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "product": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "thumbnail": {
                          "type": "string",
                          "nullable": true,
                          "description": "Image id or URL of the product thumbnail"
                        }
                      }
                    },
                    "splits": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RevenueSplit"
                      }
                    },
                    "allocatedPercent": {
                      "type": "number",
                      "description": "Shares of invited and active co-creators"
                    },
                    "ownerPercent": {
                      "type": "number",
                      "description": "What you keep of the split base"
                    },
                    "collaboratorCount": {
                      "type": "integer"
                    },
                    "maxCollaborators": {
                      "type": "integer",
                      "example": 5
                    },
                    "slotsLeft": {
                      "type": "integer"
                    },
                    "totals": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RevenueSplitTotals"
                      }
                    }
                  }
                },
                "example": {
                  "product": {
                    "id": "665f1c2a9b3e4a0012ab34cd",
                    "name": "Notion Business OS",
                    "thumbnail": "665f1c2a9b3e4a0012ab9911"
                  },
                  "splits": [
                    {
                      "id": "6710c3d59e3f1a0012cd7c01",
                      "productId": "665f1c2a9b3e4a0012ab34cd",
                      "product": {
                        "id": "665f1c2a9b3e4a0012ab34cd",
                        "name": "Notion Business OS",
                        "thumbnail": "665f1c2a9b3e4a0012ab9911"
                      },
                      "owner": null,
                      "collaboratorEmail": "maya@studio.co",
                      "collaborator": {
                        "name": "Maya Chen"
                      },
                      "sharePercent": 30,
                      "shareBps": 3000,
                      "status": "active",
                      "invitedAt": "2026-10-01T09:12:00.000Z",
                      "inviteExpiresAt": "2026-10-15T09:12:00.000Z",
                      "acceptedAt": "2026-10-01T15:40:00.000Z",
                      "declinedAt": null,
                      "removedAt": null,
                      "totals": [
                        {
                          "currency": "usd",
                          "pendingCents": 41160,
                          "heldCents": 0,
                          "paidCents": 117600,
                          "reversedCents": 2940,
                          "clawbackOwedCents": 0,
                          "shareCount": 54
                        }
                      ]
                    }
                  ],
                  "allocatedPercent": 30,
                  "ownerPercent": 70,
                  "collaboratorCount": 1,
                  "maxCollaborators": 5,
                  "slotsLeft": 4,
                  "totals": [
                    {
                      "currency": "usd",
                      "pendingCents": 41160,
                      "heldCents": 0,
                      "paidCents": 117600,
                      "reversedCents": 2940,
                      "clawbackOwedCents": 0,
                      "shareCount": 54
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/revenue-splits/sales": {
      "get": {
        "operationId": "listRevenueSplitSales",
        "summary": "List split sales",
        "description": "Your split sales, one row per sale, newest first: the sale amount, platform fee, affiliate commission, split base, your part and each co-creator's share.",
        "tags": [
          "Revenue splits"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "productId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only this product"
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`nextCursor` of the previous page"
          }
        ],
        "responses": {
          "200": {
            "description": "Split sales",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sales": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RevenueSplitSale"
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                },
                "example": {
                  "sales": [
                    {
                      "saleId": "6710c3d59e3f1a0012cd7c09",
                      "productId": "665f1c2a9b3e4a0012ab34cd",
                      "productName": "Notion Business OS",
                      "saleDate": "2026-10-14T18:03:11.000Z",
                      "currency": "usd",
                      "saleAmountCents": 9900,
                      "platformFeeCents": 198,
                      "affiliateCents": 0,
                      "baseCents": 9702,
                      "ownerCents": 6792,
                      "shares": [
                        {
                          "splitId": "6710c3d59e3f1a0012cd7c01",
                          "collaboratorEmail": "maya@studio.co",
                          "collaboratorName": "Maya Chen",
                          "sharePercent": 30,
                          "amountCents": 2910,
                          "status": "pending",
                          "reversalReason": null,
                          "clawbackCents": 0,
                          "paidAt": null
                        }
                      ]
                    }
                  ],
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/revenue-splits/sales/{id}": {
      "get": {
        "operationId": "getRevenueSplitSale",
        "summary": "Get a sale's split",
        "description": "How one order was split. The product owner sees every share; a co-creator sees only their own.",
        "tags": [
          "Revenue splits"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7c09"
            },
            "description": "Order id"
          }
        ],
        "responses": {
          "200": {
            "description": "The sale's split",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueSplitSale"
                },
                "example": {
                  "saleId": "6710c3d59e3f1a0012cd7c09",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "productName": "Notion Business OS",
                  "saleDate": "2026-10-14T18:03:11.000Z",
                  "currency": "usd",
                  "saleAmountCents": 9900,
                  "platformFeeCents": 198,
                  "affiliateCents": 0,
                  "baseCents": 9702,
                  "ownerCents": 6792,
                  "shares": [
                    {
                      "splitId": "6710c3d59e3f1a0012cd7c01",
                      "collaboratorEmail": "maya@studio.co",
                      "collaboratorName": "Maya Chen",
                      "sharePercent": 30,
                      "amountCents": 2910,
                      "status": "pending",
                      "reversalReason": null,
                      "clawbackCents": 0,
                      "paidAt": null
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/revenue-splits/earnings": {
      "get": {
        "operationId": "getRevenueSplitEarnings",
        "summary": "Get shared product earnings",
        "description": "What you earn as a co-creator on other creators' products: totals per currency, each shared product, and your share of each sale (newest first, paginated). Buyer details are never included. `payoutsEnabled: false` means shares are recorded as pending and not paid out yet.",
        "tags": [
          "Revenue splits"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "productId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only this product"
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`nextCursor` of the previous page"
          }
        ],
        "responses": {
          "200": {
            "description": "Your shared earnings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payoutsEnabled": {
                      "type": "boolean"
                    },
                    "holdDays": {
                      "type": "integer",
                      "description": "Days a sale ages before its shares are paid"
                    },
                    "totals": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RevenueSplitTotals"
                      }
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "splitId": {
                            "type": "string"
                          },
                          "productId": {
                            "type": "string"
                          },
                          "product": {
                            "type": "object",
                            "nullable": true,
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              },
                              "thumbnail": {
                                "type": "string",
                                "nullable": true,
                                "description": "Image id or URL of the product thumbnail"
                              }
                            }
                          },
                          "owner": {
                            "type": "object",
                            "nullable": true,
                            "properties": {
                              "name": {
                                "type": "string"
                              },
                              "subdomain": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          },
                          "sharePercent": {
                            "type": "number"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "invited",
                              "active",
                              "declined",
                              "removed",
                              "expired"
                            ]
                          },
                          "acceptedAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "removedAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "totals": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/RevenueSplitTotals"
                            }
                          }
                        }
                      }
                    },
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RevenueSplitEarningEntry"
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                },
                "example": {
                  "payoutsEnabled": false,
                  "holdDays": 14,
                  "totals": [
                    {
                      "currency": "usd",
                      "pendingCents": 41160,
                      "heldCents": 0,
                      "paidCents": 117600,
                      "reversedCents": 2940,
                      "clawbackOwedCents": 0,
                      "shareCount": 54
                    }
                  ],
                  "products": [
                    {
                      "splitId": "6710c3d59e3f1a0012cd7c01",
                      "productId": "665f1c2a9b3e4a0012ab34cd",
                      "product": {
                        "id": "665f1c2a9b3e4a0012ab34cd",
                        "name": "Notion Business OS",
                        "thumbnail": "665f1c2a9b3e4a0012ab9911"
                      },
                      "owner": {
                        "name": "Acme Studio",
                        "subdomain": "acme"
                      },
                      "sharePercent": 30,
                      "status": "active",
                      "acceptedAt": "2026-10-01T15:40:00.000Z",
                      "removedAt": null,
                      "totals": [
                        {
                          "currency": "usd",
                          "pendingCents": 41160,
                          "heldCents": 0,
                          "paidCents": 117600,
                          "reversedCents": 2940,
                          "clawbackOwedCents": 0,
                          "shareCount": 54
                        }
                      ]
                    }
                  ],
                  "entries": [],
                  "nextCursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/revenue-splits/invites": {
      "get": {
        "operationId": "listRevenueSplitInvites",
        "summary": "List my split invites",
        "description": "Pending invites sent to your account's email, to accept or decline. Open on every plan.",
        "tags": [
          "Revenue splits"
        ],
        "responses": {
          "200": {
            "description": "Pending invites",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invites": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RevenueSplit"
                      }
                    }
                  }
                },
                "example": {
                  "invites": [
                    {
                      "id": "6710c3d59e3f1a0012cd7c01",
                      "productId": "665f1c2a9b3e4a0012ab34cd",
                      "product": {
                        "id": "665f1c2a9b3e4a0012ab34cd",
                        "name": "Notion Business OS",
                        "thumbnail": "665f1c2a9b3e4a0012ab9911"
                      },
                      "owner": {
                        "name": "Acme Studio",
                        "subdomain": "acme"
                      },
                      "collaboratorEmail": "maya@studio.co",
                      "collaborator": {
                        "name": "Maya Chen"
                      },
                      "sharePercent": 30,
                      "shareBps": 3000,
                      "status": "invited",
                      "invitedAt": "2026-10-01T09:12:00.000Z",
                      "inviteExpiresAt": "2026-10-15T09:12:00.000Z",
                      "acceptedAt": null,
                      "declinedAt": null,
                      "removedAt": null,
                      "totals": []
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/revenue-splits/invites/{id}/accept": {
      "post": {
        "operationId": "acceptRevenueSplitInvite",
        "summary": "Accept a split invite",
        "description": "Accept an invite sent to your account's email. Your account needs payouts set up. Every sale of the product from now on is split; earlier sales are not. Open on every plan.",
        "tags": [
          "Revenue splits"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7c01"
            },
            "description": "Invite (split) id"
          }
        ],
        "responses": {
          "200": {
            "description": "The active split",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueSplit"
                },
                "example": {
                  "id": "6710c3d59e3f1a0012cd7c01",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "product": {
                    "id": "665f1c2a9b3e4a0012ab34cd",
                    "name": "Notion Business OS",
                    "thumbnail": "665f1c2a9b3e4a0012ab9911"
                  },
                  "owner": null,
                  "collaboratorEmail": "maya@studio.co",
                  "collaborator": {
                    "name": "Maya Chen"
                  },
                  "sharePercent": 30,
                  "shareBps": 3000,
                  "status": "active",
                  "invitedAt": "2026-10-01T09:12:00.000Z",
                  "inviteExpiresAt": "2026-10-15T09:12:00.000Z",
                  "acceptedAt": "2026-10-01T15:40:00.000Z",
                  "declinedAt": null,
                  "removedAt": null,
                  "totals": [
                    {
                      "currency": "usd",
                      "pendingCents": 41160,
                      "heldCents": 0,
                      "paidCents": 117600,
                      "reversedCents": 2940,
                      "clawbackOwedCents": 0,
                      "shareCount": 54
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The account cannot join a split right now (`ACCOUNT_RESTRICTED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "ACCOUNT_RESTRICTED",
                  "message": "This account cannot join a revenue split right now. Contact support."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Payouts are not set up (`PAYOUTS_REQUIRED`), or you own the product (`OWN_PRODUCT`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "PAYOUTS_REQUIRED",
                  "message": "Set up payouts first, so your share has somewhere to go. Then accept the invite."
                }
              }
            }
          },
          "410": {
            "description": "The invite expired (`INVITE_EXPIRED`) or is no longer open (`INVITE_CLOSED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/revenue-splits/invites/{id}/decline": {
      "post": {
        "operationId": "declineRevenueSplitInvite",
        "summary": "Decline a split invite",
        "description": "Decline an invite sent to your account's email.",
        "tags": [
          "Revenue splits"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7c01"
            },
            "description": "Invite (split) id"
          }
        ],
        "responses": {
          "200": {
            "description": "The declined invite",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueSplit"
                },
                "example": {
                  "id": "6710c3d59e3f1a0012cd7c01",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "product": {
                    "id": "665f1c2a9b3e4a0012ab34cd",
                    "name": "Notion Business OS",
                    "thumbnail": "665f1c2a9b3e4a0012ab9911"
                  },
                  "owner": null,
                  "collaboratorEmail": "maya@studio.co",
                  "collaborator": {
                    "name": "Maya Chen"
                  },
                  "sharePercent": 30,
                  "shareBps": 3000,
                  "status": "declined",
                  "invitedAt": "2026-10-01T09:12:00.000Z",
                  "inviteExpiresAt": "2026-10-15T09:12:00.000Z",
                  "acceptedAt": null,
                  "declinedAt": "2026-10-02T08:00:00.000Z",
                  "removedAt": null,
                  "totals": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "description": "The invite is no longer open (`INVITE_CLOSED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/revenue-splits/{id}": {
      "get": {
        "operationId": "getRevenueSplit",
        "summary": "Get a revenue split",
        "description": "One split you own or are the co-creator of, with its totals.",
        "tags": [
          "Revenue splits"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7c01"
            },
            "description": "Split id"
          }
        ],
        "responses": {
          "200": {
            "description": "The split",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueSplit"
                },
                "example": {
                  "id": "6710c3d59e3f1a0012cd7c01",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "product": {
                    "id": "665f1c2a9b3e4a0012ab34cd",
                    "name": "Notion Business OS",
                    "thumbnail": "665f1c2a9b3e4a0012ab9911"
                  },
                  "owner": null,
                  "collaboratorEmail": "maya@studio.co",
                  "collaborator": {
                    "name": "Maya Chen"
                  },
                  "sharePercent": 30,
                  "shareBps": 3000,
                  "status": "active",
                  "invitedAt": "2026-10-01T09:12:00.000Z",
                  "inviteExpiresAt": "2026-10-15T09:12:00.000Z",
                  "acceptedAt": "2026-10-01T15:40:00.000Z",
                  "declinedAt": null,
                  "removedAt": null,
                  "totals": [
                    {
                      "currency": "usd",
                      "pendingCents": 41160,
                      "heldCents": 0,
                      "paidCents": 117600,
                      "reversedCents": 2940,
                      "clawbackOwedCents": 0,
                      "shareCount": 54
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "removeRevenueSplit",
        "summary": "Remove a revenue split",
        "description": "The owner removes a co-creator (or cancels a pending invite), or the co-creator leaves. Sales from now on are no longer split; shares already recorded stay owed and are paid as usual. Open on every plan.",
        "tags": [
          "Revenue splits"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7c01"
            },
            "description": "Split id"
          }
        ],
        "responses": {
          "200": {
            "description": "The removed split",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueSplit"
                },
                "example": {
                  "id": "6710c3d59e3f1a0012cd7c01",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "product": {
                    "id": "665f1c2a9b3e4a0012ab34cd",
                    "name": "Notion Business OS",
                    "thumbnail": "665f1c2a9b3e4a0012ab9911"
                  },
                  "owner": null,
                  "collaboratorEmail": "maya@studio.co",
                  "collaborator": {
                    "name": "Maya Chen"
                  },
                  "sharePercent": 30,
                  "shareBps": 3000,
                  "status": "removed",
                  "invitedAt": "2026-10-01T09:12:00.000Z",
                  "inviteExpiresAt": "2026-10-15T09:12:00.000Z",
                  "acceptedAt": "2026-10-01T15:40:00.000Z",
                  "declinedAt": null,
                  "removedAt": "2026-10-16T10:00:00.000Z",
                  "totals": [
                    {
                      "currency": "usd",
                      "pendingCents": 41160,
                      "heldCents": 0,
                      "paidCents": 117600,
                      "reversedCents": 2940,
                      "clawbackOwedCents": 0,
                      "shareCount": 54
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The split is not active (`NOT_LIVE`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/revenue-splits/{id}/resend": {
      "post": {
        "operationId": "resendRevenueSplitInvite",
        "summary": "Resend a split invite",
        "description": "Email a pending (or expired) invite again with a new link and a new 14-day expiry. Once a minute at most, 6 sends per invite.",
        "tags": [
          "Revenue splits"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710c3d59e3f1a0012cd7c01"
            },
            "description": "Split id"
          }
        ],
        "responses": {
          "200": {
            "description": "The invite",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RevenueSplit"
                },
                "example": {
                  "id": "6710c3d59e3f1a0012cd7c01",
                  "productId": "665f1c2a9b3e4a0012ab34cd",
                  "product": {
                    "id": "665f1c2a9b3e4a0012ab34cd",
                    "name": "Notion Business OS",
                    "thumbnail": "665f1c2a9b3e4a0012ab9911"
                  },
                  "owner": null,
                  "collaboratorEmail": "maya@studio.co",
                  "collaborator": {
                    "name": "Maya Chen"
                  },
                  "sharePercent": 30,
                  "shareBps": 3000,
                  "status": "invited",
                  "invitedAt": "2026-10-01T09:12:00.000Z",
                  "inviteExpiresAt": "2026-10-15T09:12:00.000Z",
                  "acceptedAt": null,
                  "declinedAt": null,
                  "removedAt": null,
                  "totals": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — inviting a co-creator needs the Growth plan or higher (feature `revenueSplits`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "revenueSplits",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The invite is not pending (`NOT_PENDING`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/testimonials": {
      "get": {
        "operationId": "listTestimonials",
        "tags": [
          "Testimonials"
        ],
        "summary": "List testimonials to moderate",
        "description": "Your testimonials (and reviews with words, which can go on the wall too), newest first, in the key's mode. Filter by `status`; `counts` gives every tab's total. Reading is never plan-gated.",
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "approved",
                "featured",
                "hidden",
                "all"
              ],
              "default": "pending"
            }
          },
          {
            "in": "query",
            "name": "productId",
            "schema": {
              "type": "string"
            },
            "description": "Only this product's testimonials"
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Testimonials and per-status counts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "testimonials": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Testimonial"
                      }
                    },
                    "counts": {
                      "type": "object",
                      "properties": {
                        "pending": {
                          "type": "integer"
                        },
                        "approved": {
                          "type": "integer"
                        },
                        "featured": {
                          "type": "integer"
                        },
                        "hidden": {
                          "type": "integer"
                        },
                        "all": {
                          "type": "integer"
                        }
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "testimonials": [
                    {
                      "id": "6712b0c49e3f1a0012cd9911",
                      "productId": "665f1c2a9b3e4a0012ab34cd",
                      "productName": "Notion Creator Kit",
                      "saleId": "6711a3d59e3f1a0012cd7799",
                      "buyerId": "",
                      "buyerEmail": "jordan@example.com",
                      "displayName": "Jordan S.",
                      "rating": 5,
                      "text": "Saved me hours every week.",
                      "status": "pending",
                      "featured": false,
                      "source": "request",
                      "consent": true,
                      "video": {
                        "url": "https://d372ebr33sbljd.cloudfront.net/testimonials/auth0%7C123/9f2c1e.webm",
                        "contentType": "video/webm",
                        "durationSeconds": 41.2
                      },
                      "testMode": false,
                      "createdAt": "2026-10-12T09:30:00.000Z",
                      "submittedAt": "2026-10-12T09:30:00.000Z",
                      "moderatedAt": null
                    }
                  ],
                  "counts": {
                    "pending": 1,
                    "approved": 6,
                    "featured": 2,
                    "hidden": 1,
                    "all": 8
                  },
                  "page": 1,
                  "pageSize": 30,
                  "total": 1
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/testimonials/{id}": {
      "get": {
        "operationId": "getTestimonial",
        "tags": [
          "Testimonials"
        ],
        "summary": "Get one testimonial",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6712b0c49e3f1a0012cd9911"
            },
            "description": "Testimonial (review) id"
          }
        ],
        "responses": {
          "200": {
            "description": "The testimonial",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Testimonial"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "moderateTestimonial",
        "tags": [
          "Testimonials"
        ],
        "summary": "Approve, feature or hide a testimonial",
        "description": "`status: approved` puts it on the wall; `featured: true` approves it and shows it first (and on checkout); `status: hidden` takes it off the wall (its stars stay in the product's reviews). Approving and featuring need the Growth plan; hiding never does.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6712b0c49e3f1a0012cd9911"
            },
            "description": "Testimonial (review) id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "pending",
                      "approved",
                      "hidden"
                    ]
                  },
                  "featured": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The moderated testimonial",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Testimonial"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — approving or featuring a testimonial needs the Growth plan or higher (feature `testimonials`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "testimonials",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/testimonials/settings": {
      "get": {
        "operationId": "getTestimonialSettings",
        "tags": [
          "Testimonials"
        ],
        "summary": "Get testimonial settings",
        "responses": {
          "200": {
            "description": "Your settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/TestimonialSettings"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateTestimonialSettings",
        "tags": [
          "Testimonials"
        ],
        "summary": "Update testimonial settings",
        "description": "Fields left out keep their value. Saving with any switch on (`requestEnabled`, `wallEnabled`, `checkoutStrip`) needs the Growth plan; switching everything off never does.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "requestEnabled": {
                    "type": "boolean"
                  },
                  "delayDays": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 60
                  },
                  "askVideo": {
                    "type": "boolean"
                  },
                  "wallEnabled": {
                    "type": "boolean"
                  },
                  "checkoutStrip": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/TestimonialSettings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — turning testimonials on needs the Growth plan or higher (feature `testimonials`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "testimonials",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/testimonials/public/products/{productId}": {
      "get": {
        "operationId": "getTestimonialWall",
        "tags": [
          "Testimonials"
        ],
        "summary": "Get a product's testimonial wall",
        "description": "PUBLIC — no API key. The approved testimonials of one product (live orders only), featured first, at most 24, cached about a minute. Safe to render on your own site: it never includes an email or an order. `enabled: false` (and no testimonials) when the wall and the checkout strip are off, or the store's plan no longer includes testimonials.",
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "productId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The wall",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "wall": {
                      "type": "boolean"
                    },
                    "checkoutStrip": {
                      "type": "boolean"
                    },
                    "count": {
                      "type": "integer",
                      "description": "Approved testimonials in total"
                    },
                    "testimonials": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "rating": {
                            "type": "integer"
                          },
                          "text": {
                            "type": "string"
                          },
                          "displayName": {
                            "type": "string"
                          },
                          "video": {
                            "type": "object",
                            "nullable": true,
                            "description": "The buyer's video, when they sent one",
                            "properties": {
                              "url": {
                                "type": "string",
                                "example": "https://d372ebr33sbljd.cloudfront.net/testimonials/auth0%7C123/9f2c1e.webm"
                              },
                              "contentType": {
                                "type": "string",
                                "enum": [
                                  "video/webm",
                                  "video/mp4",
                                  "video/quicktime"
                                ]
                              },
                              "durationSeconds": {
                                "type": "number",
                                "nullable": true,
                                "example": 41.2
                              }
                            }
                          },
                          "featured": {
                            "type": "boolean"
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "enabled": true,
                  "wall": true,
                  "checkoutStrip": true,
                  "count": 1,
                  "testimonials": [
                    {
                      "id": "6712b0c49e3f1a0012cd9911",
                      "rating": 5,
                      "text": "Saved me hours every week.",
                      "displayName": "Jordan S.",
                      "video": {
                        "url": "https://d372ebr33sbljd.cloudfront.net/testimonials/auth0%7C123/9f2c1e.webm",
                        "contentType": "video/webm",
                        "durationSeconds": 41.2
                      },
                      "featured": true,
                      "createdAt": "2026-10-12T09:30:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/attribution-survey/settings": {
      "get": {
        "operationId": "getAttributionSurvey",
        "tags": [
          "Attribution survey"
        ],
        "summary": "Get the \"How did you hear about us?\" survey",
        "responses": {
          "200": {
            "description": "Your survey",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "survey": {
                      "$ref": "#/components/schemas/AttributionSurvey"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateAttributionSurvey",
        "tags": [
          "Attribution survey"
        ],
        "summary": "Update the survey",
        "description": "Fields left out keep their value. Send an option's `id` to keep its past answers attached after a rename; options without one get an id from their label. `\"other\"` is reserved for the free-text answer. Turning the survey on needs the Growth plan.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "question": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "options": {
                    "type": "array",
                    "minItems": 2,
                    "maxItems": 12,
                    "items": {
                      "type": "object",
                      "required": [
                        "label"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^[a-z0-9][a-z0-9-]{0,39}$"
                        },
                        "label": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 60
                        },
                        "channel": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "instagram",
                            "tiktok",
                            "youtube",
                            "x",
                            "facebook",
                            "email",
                            "ads",
                            "other"
                          ]
                        }
                      }
                    }
                  },
                  "allowOther": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "survey": {
                      "$ref": "#/components/schemas/AttributionSurvey"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — turning the survey on needs the Growth plan or higher (feature `attributionSurvey`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "attributionSurvey",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/attribution-survey/report": {
      "get": {
        "operationId": "getAttributionReport",
        "tags": [
          "Attribution survey"
        ],
        "summary": "Answers and revenue per channel",
        "description": "For live (or the key's test mode), non-refunded orders in the range whose buyers saw the question: how many answered, each answer's share and revenue (in your account currency, like the dashboard's Revenue), the most common \"Other\" answers, and your tracked links' sales per channel over the same range for comparison (`trackedLinks.locked` when your plan has no tracked-link stats). Reading is never plan-gated.",
        "parameters": [
          {
            "in": "query",
            "name": "startDate",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Range start (ISO). Default: 30 days ago. Capped at 366 days."
          },
          {
            "in": "query",
            "name": "endDate",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Range end (ISO). Default: now."
          },
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "minimum": -840,
              "maximum": 840,
              "default": 0
            },
            "description": "Your UTC offset as `Date#getTimezoneOffset()` minutes (UTC − local)."
          }
        ],
        "responses": {
          "200": {
            "description": "The report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "range": {
                      "type": "object",
                      "properties": {
                        "start": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "end": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "survey": {
                      "$ref": "#/components/schemas/AttributionSurvey"
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "shown": {
                          "type": "integer"
                        },
                        "answered": {
                          "type": "integer"
                        },
                        "responseRate": {
                          "type": "number",
                          "description": "Percent of buyers asked who answered"
                        },
                        "revenue": {
                          "type": "number"
                        }
                      }
                    },
                    "options": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "channel": {
                            "type": "string",
                            "nullable": true
                          },
                          "answers": {
                            "type": "integer"
                          },
                          "share": {
                            "type": "number",
                            "description": "Percent of answers"
                          },
                          "revenue": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "other": {
                      "type": "object",
                      "properties": {
                        "answers": {
                          "type": "integer"
                        },
                        "share": {
                          "type": "number"
                        },
                        "revenue": {
                          "type": "number"
                        },
                        "topAnswers": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "text": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        }
                      }
                    },
                    "trackedLinks": {
                      "type": "object",
                      "properties": {
                        "locked": {
                          "type": "boolean"
                        },
                        "channels": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "channel": {
                                "type": "string"
                              },
                              "links": {
                                "type": "integer"
                              },
                              "clicks": {
                                "type": "integer"
                              },
                              "visitors": {
                                "type": "integer"
                              },
                              "sales": {
                                "type": "integer"
                              },
                              "revenue": {
                                "type": "number"
                              }
                            }
                          }
                        },
                        "totals": {
                          "type": "object",
                          "nullable": true,
                          "properties": {
                            "sales": {
                              "type": "integer"
                            },
                            "revenue": {
                              "type": "number"
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "currency": "USD",
                  "totals": {
                    "shown": 120,
                    "answered": 84,
                    "responseRate": 70,
                    "revenue": 3486
                  },
                  "options": [
                    {
                      "id": "podcast",
                      "label": "A podcast",
                      "channel": null,
                      "answers": 31,
                      "share": 36.9,
                      "revenue": 1519
                    },
                    {
                      "id": "instagram",
                      "label": "Instagram",
                      "channel": "instagram",
                      "answers": 22,
                      "share": 26.2,
                      "revenue": 638
                    }
                  ],
                  "other": {
                    "answers": 6,
                    "share": 7.1,
                    "revenue": 174,
                    "topAnswers": [
                      {
                        "text": "Reddit",
                        "count": 3
                      }
                    ]
                  },
                  "trackedLinks": {
                    "locked": false,
                    "channels": [
                      {
                        "channel": "instagram",
                        "links": 3,
                        "clicks": 912,
                        "visitors": 640,
                        "sales": 14,
                        "revenue": 406
                      }
                    ],
                    "totals": {
                      "sales": 14,
                      "revenue": 406
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships": {
      "get": {
        "operationId": "listMemberships",
        "summary": "List memberships",
        "description": "Your memberships (archived last) with their offers, included products and live numbers. Open on every plan.",
        "tags": [
          "Memberships"
        ],
        "responses": {
          "200": {
            "description": "Your memberships",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "memberships": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Membership"
                      }
                    }
                  }
                },
                "example": {
                  "memberships": [
                    {
                      "_id": "6711a0c4e1b2c3d4e5f60a01",
                      "name": "The Studio",
                      "slug": "the-studio",
                      "description": "Weekly breakdowns, a private community and the full course library.",
                      "offerIds": [
                        "665f1c2a9b3e4a0012ab34ce"
                      ],
                      "productIds": [
                        "665f1c2a9b3e4a0012ab34cd"
                      ],
                      "communityEnabled": true,
                      "notifyByEmail": true,
                      "archived": false,
                      "membersUrl": "https://acme.pocketsflow.com/members/the-studio",
                      "offers": [
                        {
                          "id": "665f1c2a9b3e4a0012ab34ce",
                          "name": "Studio membership",
                          "price": 12,
                          "frequency": "monthly",
                          "currency": "USD",
                          "priceLabel": "$12/month",
                          "published": true
                        }
                      ],
                      "products": [
                        {
                          "id": "665f1c2a9b3e4a0012ab34cd",
                          "name": "Pricing course",
                          "thumbnail": null,
                          "productType": "course"
                        }
                      ],
                      "stats": {
                        "activeMembers": 148,
                        "posts": 23,
                        "threads": 61,
                        "joinedLast30Days": 19,
                        "leftLast30Days": 4
                      },
                      "createdAt": "2026-10-01T09:00:00.000Z",
                      "updatedAt": "2026-10-16T09:30:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createMembership",
        "summary": "Create a membership",
        "description": "Create a membership from one or more of your subscription offers. Everyone already subscribed becomes a member right away (`member.joined` fires for each). Pro plan and above.",
        "tags": [
          "Memberships"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "offerIds"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "offerIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 10,
                    "description": "Your subscription offers (at least one)"
                  },
                  "productIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 20,
                    "description": "Your products members can open (courses, files, links)"
                  },
                  "communityEnabled": {
                    "type": "boolean",
                    "default": true
                  },
                  "notifyByEmail": {
                    "type": "boolean",
                    "default": true
                  }
                }
              },
              "example": {
                "name": "The Studio",
                "description": "Weekly breakdowns and a private community.",
                "offerIds": [
                  "665f1c2a9b3e4a0012ab34ce"
                ],
                "productIds": [
                  "665f1c2a9b3e4a0012ab34cd"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Membership"
                },
                "example": {
                  "_id": "6711a0c4e1b2c3d4e5f60a01",
                  "name": "The Studio",
                  "slug": "the-studio",
                  "description": "Weekly breakdowns, a private community and the full course library.",
                  "offerIds": [
                    "665f1c2a9b3e4a0012ab34ce"
                  ],
                  "productIds": [
                    "665f1c2a9b3e4a0012ab34cd"
                  ],
                  "communityEnabled": true,
                  "notifyByEmail": true,
                  "archived": false,
                  "membersUrl": "https://acme.pocketsflow.com/members/the-studio",
                  "offers": [
                    {
                      "id": "665f1c2a9b3e4a0012ab34ce",
                      "name": "Studio membership",
                      "price": 12,
                      "frequency": "monthly",
                      "currency": "USD",
                      "priceLabel": "$12/month",
                      "published": true
                    }
                  ],
                  "products": [
                    {
                      "id": "665f1c2a9b3e4a0012ab34cd",
                      "name": "Pricing course",
                      "thumbnail": null,
                      "productType": "course"
                    }
                  ],
                  "stats": {
                    "activeMembers": 148,
                    "posts": 23,
                    "threads": 61,
                    "joinedLast30Days": 19,
                    "leftLast30Days": 4
                  },
                  "createdAt": "2026-10-01T09:00:00.000Z",
                  "updatedAt": "2026-10-16T09:30:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed: no offer (`OFFER_REQUIRED`), an offer or product that isn't yours (`OFFER_NOT_FOUND`, `PRODUCT_NOT_FOUND`), or too long a name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Pick at least one subscription offer. Its subscribers become members.",
                  "code": "OFFER_REQUIRED"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Creating a membership needs the Pro plan or higher (feature `memberships`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "memberships",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships/members/ban": {
      "post": {
        "operationId": "banMember",
        "summary": "Ban a member from your communities",
        "description": "Ban someone (by email) from every community of your store. They keep reading your posts and included products while subscribed, but can't read or write in the community or react. Optionally hide everything they wrote. Never needs the plan.",
        "tags": [
          "Memberships"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "The member's email"
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 300
                  },
                  "hideContent": {
                    "type": "boolean",
                    "default": false,
                    "description": "Also hide every thread and reply they wrote"
                  }
                }
              },
              "example": {
                "email": "spam@example.com",
                "reason": "Posting links to other stores",
                "hideContent": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The member's ban state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "type": "string"
                    },
                    "banned": {
                      "type": "boolean"
                    },
                    "bannedAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "hiddenItems": {
                      "type": "integer",
                      "description": "Threads and replies hidden"
                    }
                  }
                },
                "example": {
                  "email": "spam@example.com",
                  "banned": true,
                  "bannedAt": "2026-10-16T12:00:00.000Z",
                  "hiddenItems": 3
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships/members/unban": {
      "post": {
        "operationId": "unbanMember",
        "summary": "Lift a community ban",
        "description": "Let them back into the community. Hidden posts stay hidden until you unhide them.",
        "tags": [
          "Memberships"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "The member's email"
                  }
                }
              },
              "example": {
                "email": "spam@example.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The member's ban state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "type": "string"
                    },
                    "banned": {
                      "type": "boolean"
                    },
                    "bannedAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "hiddenItems": {
                      "type": "integer",
                      "description": "Threads and replies hidden"
                    }
                  }
                },
                "example": {
                  "email": "spam@example.com",
                  "banned": true,
                  "bannedAt": "2026-10-16T12:00:00.000Z",
                  "hiddenItems": 3
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships/{id}": {
      "get": {
        "operationId": "getMembership",
        "summary": "Get a membership",
        "tags": [
          "Memberships"
        ],
        "description": "One membership with its offers, included products and live numbers.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          }
        ],
        "responses": {
          "200": {
            "description": "The membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Membership"
                },
                "example": {
                  "_id": "6711a0c4e1b2c3d4e5f60a01",
                  "name": "The Studio",
                  "slug": "the-studio",
                  "description": "Weekly breakdowns, a private community and the full course library.",
                  "offerIds": [
                    "665f1c2a9b3e4a0012ab34ce"
                  ],
                  "productIds": [
                    "665f1c2a9b3e4a0012ab34cd"
                  ],
                  "communityEnabled": true,
                  "notifyByEmail": true,
                  "archived": false,
                  "membersUrl": "https://acme.pocketsflow.com/members/the-studio",
                  "offers": [
                    {
                      "id": "665f1c2a9b3e4a0012ab34ce",
                      "name": "Studio membership",
                      "price": 12,
                      "frequency": "monthly",
                      "currency": "USD",
                      "priceLabel": "$12/month",
                      "published": true
                    }
                  ],
                  "products": [
                    {
                      "id": "665f1c2a9b3e4a0012ab34cd",
                      "name": "Pricing course",
                      "thumbnail": null,
                      "productType": "course"
                    }
                  ],
                  "stats": {
                    "activeMembers": 148,
                    "posts": 23,
                    "threads": 61,
                    "joinedLast30Days": 19,
                    "leftLast30Days": 4
                  },
                  "createdAt": "2026-10-01T09:00:00.000Z",
                  "updatedAt": "2026-10-16T09:30:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateMembership",
        "summary": "Update a membership",
        "description": "Change any field. Changing the offers re-checks every member (`member.joined` / `member.left` with reason `membership_changed`). Archiving closes the members area (`member.left` with reason `membership_closed`). Archiving, reopening and switching the community or post emails off never need the plan; everything else does.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "offerIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 10,
                    "description": "Your subscription offers (at least one)"
                  },
                  "productIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 20,
                    "description": "Your products members can open (courses, files, links)"
                  },
                  "communityEnabled": {
                    "type": "boolean",
                    "default": true
                  },
                  "notifyByEmail": {
                    "type": "boolean",
                    "default": true
                  },
                  "archived": {
                    "type": "boolean",
                    "description": "Archive (close) or reopen the membership. Never needs the plan."
                  }
                }
              },
              "example": {
                "communityEnabled": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Membership"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Editing a membership needs the Pro plan or higher (feature `memberships`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "memberships",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteMembership",
        "summary": "Delete a membership",
        "description": "Delete the membership with its posts, threads, replies and reactions. Members get `member.left` (reason `membership_closed`). Subscriptions are not touched: cancel or reuse the offers separately.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Membership deleted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships/{id}/members": {
      "get": {
        "operationId": "listMembershipMembers",
        "summary": "List members",
        "description": "Everyone who subscribed to the membership's offers, one row per email, active first. `status` is `active` while a subscription is live. 50 per page.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "active",
                "ended",
                "banned"
              ],
              "default": "all"
            }
          },
          {
            "in": "query",
            "name": "q",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Search by email or name"
          },
          {
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "Page number (1-based)"
          }
        ],
        "responses": {
          "200": {
            "description": "Members",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "members": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MembershipMember"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "counts": {
                      "type": "object",
                      "properties": {
                        "all": {
                          "type": "integer"
                        },
                        "active": {
                          "type": "integer"
                        },
                        "ended": {
                          "type": "integer"
                        },
                        "banned": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "members": [
                    {
                      "email": "maya@example.com",
                      "name": "Maya Reyes",
                      "displayName": "Maya R.",
                      "status": "active",
                      "subscriptionStatus": "active",
                      "banned": false,
                      "bannedAt": null,
                      "banReason": null,
                      "emailOptOut": false,
                      "offerIds": [
                        "665f1c2a9b3e4a0012ab34ce"
                      ],
                      "joinedAt": "2026-09-02T10:00:00.000Z",
                      "leftAt": null
                    }
                  ],
                  "page": 1,
                  "total": 1,
                  "hasMore": false,
                  "counts": {
                    "all": 1,
                    "active": 1,
                    "ended": 0,
                    "banned": 0
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships/{id}/posts": {
      "get": {
        "operationId": "listMemberPosts",
        "summary": "List members-only posts",
        "description": "Your posts in this membership (drafts too), newest first, without the body. 20 per page.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "Page number (1-based)"
          }
        ],
        "responses": {
          "200": {
            "description": "Posts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "posts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MemberPost"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "posts": [
                    {
                      "_id": "6711a0c4e1b2c3d4e5f60b01",
                      "membershipId": "6711a0c4e1b2c3d4e5f60a01",
                      "title": "October teardown: three pricing pages",
                      "excerpt": "This month we look at three pricing pages that doubled conversion.",
                      "status": "published",
                      "pinned": false,
                      "publishedAt": "2026-10-16T09:30:00.000Z",
                      "notify": true,
                      "notifyState": "sent",
                      "notifiedAt": "2026-10-16T09:31:12.000Z",
                      "notifiedCount": 142,
                      "reactions": {
                        "like": 12,
                        "love": 5,
                        "insightful": 9
                      },
                      "createdAt": "2026-10-16T09:00:00.000Z",
                      "updatedAt": "2026-10-16T09:31:12.000Z"
                    }
                  ],
                  "page": 1,
                  "total": 1,
                  "hasMore": false
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createMemberPost",
        "summary": "Write a members-only post",
        "description": "Save a draft or publish straight away. Publishing with `notify` emails every live member once (skipping anyone who opted out, unsubscribed or bounced). Pro plan and above.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "html": {
                    "type": "string",
                    "description": "The post as HTML. Sanitised on save: scripts, styles, iframes and every attribute but link `href` (http, https, mailto) and image `src` (https) / `alt` are removed."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "published"
                    ],
                    "description": "`published` puts it on the feed (and emails members when `notify`)"
                  },
                  "pinned": {
                    "type": "boolean"
                  },
                  "notify": {
                    "type": "boolean",
                    "default": true,
                    "description": "Email members when it is published. Sent once."
                  }
                }
              },
              "example": {
                "title": "October teardown: three pricing pages",
                "html": "<p>This month we look at three pricing pages.</p>",
                "status": "published",
                "notify": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemberPost"
                },
                "example": {
                  "_id": "6711a0c4e1b2c3d4e5f60b01",
                  "membershipId": "6711a0c4e1b2c3d4e5f60a01",
                  "title": "October teardown: three pricing pages",
                  "html": "<p>This month we look at three pricing pages that doubled conversion.</p>",
                  "excerpt": "This month we look at three pricing pages that doubled conversion.",
                  "status": "published",
                  "pinned": false,
                  "publishedAt": "2026-10-16T09:30:00.000Z",
                  "notify": true,
                  "notifyState": "pending",
                  "notifiedAt": null,
                  "notifiedCount": 0,
                  "reactions": {
                    "like": 12,
                    "love": 5,
                    "insightful": 9
                  },
                  "createdAt": "2026-10-16T09:00:00.000Z",
                  "updatedAt": "2026-10-16T09:31:12.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Writing a members-only post needs the Pro plan or higher (feature `memberships`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "memberships",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships/{id}/posts/{postId}": {
      "get": {
        "operationId": "getMemberPost",
        "summary": "Get a members-only post",
        "tags": [
          "Memberships"
        ],
        "description": "One post with its HTML.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "path",
            "name": "postId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60b01"
            },
            "description": "Post id"
          }
        ],
        "responses": {
          "200": {
            "description": "The post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemberPost"
                },
                "example": {
                  "_id": "6711a0c4e1b2c3d4e5f60b01",
                  "membershipId": "6711a0c4e1b2c3d4e5f60a01",
                  "title": "October teardown: three pricing pages",
                  "html": "<p>This month we look at three pricing pages that doubled conversion.</p>",
                  "excerpt": "This month we look at three pricing pages that doubled conversion.",
                  "status": "published",
                  "pinned": false,
                  "publishedAt": "2026-10-16T09:30:00.000Z",
                  "notify": true,
                  "notifyState": "sent",
                  "notifiedAt": "2026-10-16T09:31:12.000Z",
                  "notifiedCount": 142,
                  "reactions": {
                    "like": 12,
                    "love": 5,
                    "insightful": 9
                  },
                  "createdAt": "2026-10-16T09:00:00.000Z",
                  "updatedAt": "2026-10-16T09:31:12.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateMemberPost",
        "summary": "Edit, publish or pin a post",
        "description": "Edit the title or HTML, publish a draft (`status: published`), move it back to draft, or pin it. Pinning, unpinning and unpublishing never need the plan. Editing a published post never re-sends the email.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "path",
            "name": "postId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60b01"
            },
            "description": "Post id"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "html": {
                    "type": "string",
                    "description": "The post as HTML. Sanitised on save: scripts, styles, iframes and every attribute but link `href` (http, https, mailto) and image `src` (https) / `alt` are removed."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "draft",
                      "published"
                    ],
                    "description": "`published` puts it on the feed (and emails members when `notify`)"
                  },
                  "pinned": {
                    "type": "boolean"
                  },
                  "notify": {
                    "type": "boolean",
                    "default": true,
                    "description": "Email members when it is published. Sent once."
                  }
                }
              },
              "example": {
                "pinned": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The post",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemberPost"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Editing or publishing a post needs the Pro plan or higher (feature `memberships`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "memberships",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteMemberPost",
        "summary": "Delete a post",
        "tags": [
          "Memberships"
        ],
        "description": "Delete the post and its reactions.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "path",
            "name": "postId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60b01"
            },
            "description": "Post id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Post deleted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships/{id}/threads": {
      "get": {
        "operationId": "listCommunityThreads",
        "summary": "List community threads",
        "description": "The community for moderation: pinned first, then latest activity, hidden threads included (`filter` narrows it). Authors' emails are included here, never on the storefront. 20 per page.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "query",
            "name": "filter",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "visible",
                "hidden"
              ],
              "default": "all"
            }
          },
          {
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "Page number (1-based)"
          }
        ],
        "responses": {
          "200": {
            "description": "Threads",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "threads": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CommunityThread"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "hiddenReplies": {
                      "type": "integer",
                      "description": "Hidden replies in this community"
                    }
                  }
                },
                "example": {
                  "threads": [
                    {
                      "_id": "6711a0c4e1b2c3d4e5f60c01",
                      "membershipId": "6711a0c4e1b2c3d4e5f60a01",
                      "title": "How do you price a first course?",
                      "body": "I'm launching in November and can't decide between $49 and $99.",
                      "author": {
                        "type": "member",
                        "name": "Maya R.",
                        "email": "maya@example.com",
                        "banned": false
                      },
                      "pinned": false,
                      "locked": false,
                      "hidden": false,
                      "replyCount": 4,
                      "reactions": {
                        "like": 3,
                        "love": 0,
                        "insightful": 2
                      },
                      "lastActivityAt": "2026-10-16T11:02:00.000Z",
                      "createdAt": "2026-10-15T18:20:00.000Z"
                    }
                  ],
                  "page": 1,
                  "total": 1,
                  "hasMore": false,
                  "hiddenReplies": 0
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createCommunityThread",
        "summary": "Start a thread as the creator",
        "description": "Post in your own community (plain text). Pro plan and above.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 140
                  },
                  "body": {
                    "type": "string",
                    "maxLength": 10000
                  },
                  "pinned": {
                    "type": "boolean",
                    "default": false
                  }
                }
              },
              "example": {
                "title": "Welcome! Introduce yourself",
                "body": "Say hi and tell us what you're building.",
                "pinned": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The thread",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommunityThread"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Posting in the community needs the Pro plan or higher (feature `memberships`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "memberships",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships/{id}/threads/{threadId}": {
      "get": {
        "operationId": "getCommunityThread",
        "summary": "Get a thread with its replies",
        "description": "The thread and its replies (oldest first, hidden ones included), 50 per page.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "path",
            "name": "threadId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60c01"
            },
            "description": "Thread id"
          },
          {
            "in": "query",
            "name": "page",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "Page number (1-based)"
          }
        ],
        "responses": {
          "200": {
            "description": "The thread",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "thread": {
                      "$ref": "#/components/schemas/CommunityThread"
                    },
                    "replies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CommunityReply"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "thread": {
                    "_id": "6711a0c4e1b2c3d4e5f60c01",
                    "membershipId": "6711a0c4e1b2c3d4e5f60a01",
                    "title": "How do you price a first course?",
                    "body": "I'm launching in November and can't decide between $49 and $99.",
                    "author": {
                      "type": "member",
                      "name": "Maya R.",
                      "email": "maya@example.com",
                      "banned": false
                    },
                    "pinned": false,
                    "locked": false,
                    "hidden": false,
                    "replyCount": 4,
                    "reactions": {
                      "like": 3,
                      "love": 0,
                      "insightful": 2
                    },
                    "lastActivityAt": "2026-10-16T11:02:00.000Z",
                    "createdAt": "2026-10-15T18:20:00.000Z"
                  },
                  "replies": [
                    {
                      "_id": "6711a0c4e1b2c3d4e5f60d01",
                      "threadId": "6711a0c4e1b2c3d4e5f60c01",
                      "body": "Start at $99 with an early-bird price. You can always add a cheaper tier.",
                      "author": {
                        "type": "creator",
                        "name": "Acme Studio",
                        "email": null,
                        "banned": false
                      },
                      "hidden": false,
                      "reactions": {
                        "like": 6,
                        "love": 1,
                        "insightful": 4
                      },
                      "createdAt": "2026-10-16T11:02:00.000Z"
                    }
                  ],
                  "page": 1,
                  "hasMore": false
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "moderateCommunityThread",
        "summary": "Pin, lock or hide a thread",
        "description": "Moderation. Never needs the plan.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "path",
            "name": "threadId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60c01"
            },
            "description": "Thread id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "pinned": {
                    "type": "boolean"
                  },
                  "locked": {
                    "type": "boolean",
                    "description": "Stop new replies"
                  },
                  "hidden": {
                    "type": "boolean",
                    "description": "Hide from members"
                  }
                }
              },
              "example": {
                "hidden": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The thread",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommunityThread"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteCommunityThread",
        "summary": "Delete a thread",
        "description": "Delete the thread with its replies and reactions. Never needs the plan.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "path",
            "name": "threadId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60c01"
            },
            "description": "Thread id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Thread deleted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships/{id}/threads/{threadId}/replies": {
      "post": {
        "operationId": "replyToCommunityThread",
        "summary": "Reply as the creator",
        "description": "Reply in a thread (plain text). Pro plan and above.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "path",
            "name": "threadId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60c01"
            },
            "description": "Thread id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "body"
                ],
                "properties": {
                  "body": {
                    "type": "string",
                    "maxLength": 5000
                  }
                }
              },
              "example": {
                "body": "Start at $99 with an early-bird price."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The reply",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommunityReply"
                },
                "example": {
                  "_id": "6711a0c4e1b2c3d4e5f60d01",
                  "threadId": "6711a0c4e1b2c3d4e5f60c01",
                  "body": "Start at $99 with an early-bird price. You can always add a cheaper tier.",
                  "author": {
                    "type": "creator",
                    "name": "Acme Studio",
                    "email": null,
                    "banned": false
                  },
                  "hidden": false,
                  "reactions": {
                    "like": 6,
                    "love": 1,
                    "insightful": 4
                  },
                  "createdAt": "2026-10-16T11:02:00.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Replying in the community needs the Pro plan or higher (feature `memberships`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "memberships",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/memberships/{id}/replies/{replyId}": {
      "patch": {
        "operationId": "moderateCommunityReply",
        "summary": "Hide or unhide a reply",
        "description": "Moderation. Never needs the plan.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "path",
            "name": "replyId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60d01"
            },
            "description": "Reply id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "hidden"
                ],
                "properties": {
                  "hidden": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "hidden": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The reply",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommunityReply"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteCommunityReply",
        "summary": "Delete a reply",
        "description": "Delete the reply and its reactions. Never needs the plan.",
        "tags": [
          "Memberships"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60a01"
            },
            "description": "Membership id"
          },
          {
            "in": "path",
            "name": "replyId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711a0c4e1b2c3d4e5f60d01"
            },
            "description": "Reply id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Reply deleted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/checkout-offers": {
      "get": {
        "operationId": "getProductCheckoutOffers",
        "summary": "Get a product's payment plan and exit offers",
        "description": "The payment plan, exit offer and downsell settings of one of your products, as saved.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The product's checkout offer settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutOfferSettings"
                }
              }
            }
          },
          "400": {
            "description": "The settings are invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Invalid product id"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No product with this id in your account (current mode)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/payment-plan": {
      "patch": {
        "operationId": "updateProductPaymentPlan",
        "summary": "Set a product's payment plan",
        "description": "Let buyers split the price into 2–6 payments, 30 days apart (“3 payments of $99”). The buyer gets the product on the first payment; every payment is its own order with `installment: { index, of }`, and the `installment.paid` / `installment.failed` webhooks fire. Partial update: fields you leave out keep their value. Turning it on needs the Growth plan.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "Offer “Pay in N” on this product's checkout. Needs a fixed price (not pay what you want)."
                  },
                  "installments": {
                    "type": "integer",
                    "minimum": 2,
                    "maximum": 6,
                    "description": "Number of payments, 30 days apart (default 3)"
                  },
                  "surchargePercent": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 25,
                    "description": "Optional payment plan fee on the whole price, in percent (up to two decimals, default 0)"
                  }
                }
              },
              "example": {
                "enabled": true,
                "installments": 3,
                "surchargePercent": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved payment plan",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "paymentPlan": {
                      "$ref": "#/components/schemas/PaymentPlanSettings"
                    }
                  }
                },
                "example": {
                  "paymentPlan": {
                    "enabled": true,
                    "installments": 3,
                    "surchargePercent": 0
                  }
                }
              }
            }
          },
          "400": {
            "description": "The settings are invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Choose between 2 and 6 payments."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Turning this on needs the Growth plan or higher (`paymentPlans`). Turning it off never does. Body: `code: FEATURE_LOCKED`, `details.feature`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "description": "No product with this id in your account (current mode)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/exit-offer": {
      "patch": {
        "operationId": "updateProductExitOffer",
        "summary": "Set a product's exit offer and downsell",
        "description": "The exit offer shows a one-time discount (once per buyer session) when a buyer is about to leave the checkout; turning it on creates a discount with its own code, kept in step with the amount. The downsell offers a cheaper product when a buyer removes an order bump. Send `exitOffer`, `downsell` or both; fields left out keep their value. Turning either on needs the Growth plan.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "exitOffer": {
                    "type": "object",
                    "properties": {
                      "enabled": {
                        "type": "boolean",
                        "description": "Show a one-time discount when a buyer is about to leave the checkout"
                      },
                      "valueType": {
                        "type": "string",
                        "enum": [
                          "percentage",
                          "fixed"
                        ]
                      },
                      "value": {
                        "type": "number",
                        "description": "Whole percent (1–90) for `percentage`, or an amount in your currency for `fixed`"
                      }
                    }
                  },
                  "downsell": {
                    "type": "object",
                    "properties": {
                      "enabled": {
                        "type": "boolean",
                        "description": "Offer a cheaper product when a buyer removes an order bump at checkout"
                      },
                      "productId": {
                        "type": "string",
                        "description": "Another of your products, in the same mode"
                      },
                      "price": {
                        "type": "number",
                        "description": "Its price on this offer; 0 sells it at its own price"
                      }
                    }
                  }
                }
              },
              "example": {
                "exitOffer": {
                  "enabled": true,
                  "valueType": "percentage",
                  "value": 10
                },
                "downsell": {
                  "enabled": true,
                  "productId": "665f1c2a9b3e4a0012ab34ce",
                  "price": 19
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exitOffer": {
                      "$ref": "#/components/schemas/ExitOfferSettings"
                    },
                    "downsell": {
                      "$ref": "#/components/schemas/DownsellSettings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The settings are invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "The exit discount must be between 1% and 90%."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Turning this on needs the Growth plan or higher (`exitOffers`). Turning it off never does. Body: `code: FEATURE_LOCKED`, `details.feature`, `details.requiredPlan`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "description": "No product with this id in your account (current mode)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/checkout-offers/{productId}": {
      "get": {
        "operationId": "getCheckoutOffers",
        "summary": "Get what a checkout offers",
        "description": "PUBLIC — no API key: what a product's checkout offers right now (payment plan, exit offer, downsell), with the seller's plan applied. Use it to build your own checkout page.",
        "tags": [
          "Checkout"
        ],
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "productId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The offers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutOffers"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/flash-sales": {
      "get": {
        "operationId": "listFlashSales",
        "summary": "List flash sales",
        "description": "Your flash sales in the current mode, newest first, each with its status (computed from the clock) and the sales and revenue it brought in.",
        "tags": [
          "Launch pricing"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "scheduled",
                "ended"
              ]
            },
            "description": "Only sales in this state"
          }
        ],
        "responses": {
          "200": {
            "description": "Your flash sales",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "flashSales": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FlashSale"
                      }
                    },
                    "currency": {
                      "type": "string"
                    },
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createFlashSale",
        "summary": "Create a flash sale",
        "description": "A sale window with a countdown on the product page and checkout. It starts and ends on time by itself. Several live sales never stack: the buyer gets the deepest one (or a better code / fair price).",
        "tags": [
          "Launch pricing"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "startsAt",
                  "endsAt",
                  "value"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80,
                    "example": "Black Friday"
                  },
                  "startsAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the sale starts (ISO 8601)"
                  },
                  "endsAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it ends. Up to 90 days after the start. The sale ends on time by itself: prices are computed from the clock."
                  },
                  "valueType": {
                    "type": "string",
                    "enum": [
                      "percentage",
                      "fixed"
                    ]
                  },
                  "value": {
                    "type": "number",
                    "description": "Whole percent (1–90) for `percentage`, or an amount in your currency (up to two decimals) for `fixed`"
                  },
                  "productIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Your products on sale (up to 200). Empty or left out on create: the whole store."
                  },
                  "allProducts": {
                    "type": "boolean",
                    "description": "True puts the whole store on sale (clears `productIds`)"
                  }
                }
              },
              "example": {
                "name": "Black Friday",
                "startsAt": "2026-11-27T08:00:00.000Z",
                "endsAt": "2026-11-30T23:59:00.000Z",
                "valueType": "percentage",
                "value": 30
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The flash sale",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlashSale"
                }
              }
            }
          },
          "400": {
            "description": "The sale is invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "The sale must end after it starts"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Creating a flash sale needs the Growth plan or higher (feature `flashSales`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "flashSales",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/flash-sales/{id}": {
      "get": {
        "operationId": "getFlashSale",
        "summary": "Get a flash sale",
        "description": "One flash sale with its status and results.",
        "tags": [
          "Launch pricing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711d4e59e3f1a0012cd8b01"
            },
            "description": "Flash sale id"
          }
        ],
        "responses": {
          "200": {
            "description": "The flash sale",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlashSale"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No flash sale with this id in your account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Flash sale not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateFlashSale",
        "summary": "Update a flash sale",
        "description": "Change any field; the rest keep their value. Move `endsAt` to now to end a live sale early.",
        "tags": [
          "Launch pricing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711d4e59e3f1a0012cd8b01"
            },
            "description": "Flash sale id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80,
                    "example": "Black Friday"
                  },
                  "startsAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the sale starts (ISO 8601)"
                  },
                  "endsAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it ends. Up to 90 days after the start. The sale ends on time by itself: prices are computed from the clock."
                  },
                  "valueType": {
                    "type": "string",
                    "enum": [
                      "percentage",
                      "fixed"
                    ]
                  },
                  "value": {
                    "type": "number",
                    "description": "Whole percent (1–90) for `percentage`, or an amount in your currency (up to two decimals) for `fixed`"
                  },
                  "productIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Your products on sale (up to 200). Empty or left out on create: the whole store."
                  },
                  "allProducts": {
                    "type": "boolean",
                    "description": "True puts the whole store on sale (clears `productIds`)"
                  }
                }
              },
              "example": {
                "value": 40
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated flash sale",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlashSale"
                }
              }
            }
          },
          "400": {
            "description": "The change is invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "The end must be in the future"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Editing a flash sale needs the Growth plan or higher (feature `flashSales`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "flashSales",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "No flash sale with this id in your account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Flash sale not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteFlashSale",
        "summary": "Delete a flash sale",
        "description": "Removes the sale (prices go back at once). Orders already made keep their record. Never needs the plan.",
        "tags": [
          "Launch pricing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711d4e59e3f1a0012cd8b01"
            },
            "description": "Flash sale id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "id": "6711d4e59e3f1a0012cd8b01",
                  "deleted": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Flash sale not found"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No flash sale with this id in your account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Flash sale not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/flash-sales/{id}/announcement": {
      "post": {
        "operationId": "createFlashSaleAnnouncement",
        "summary": "Draft the flash sale announcement email",
        "description": "Creates (once) a draft email to your whole audience announcing the sale, with a button to your store. Nothing is sent: review, then send or schedule it like any email (`POST /newsletters/posts/{id}/schedule` for the sale's start), so your sender, send allowance and unsubscribe rules apply. Calling again returns the same draft.",
        "tags": [
          "Launch pricing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711d4e59e3f1a0012cd8b01"
            },
            "description": "Flash sale id"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "Where the button goes (default: your store's home page)"
                  }
                }
              },
              "example": {
                "url": "https://yourstore.pocketsflow.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The existing draft",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "postId": {
                      "type": "string"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "The new draft",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "postId": {
                      "type": "string"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The sale has ended, or the url is invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This sale has ended. There is nothing to announce."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Announcing a flash sale needs the Growth plan or higher (feature `flashSales`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "flashSales",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "No flash sale with this id in your account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Flash sale not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/fair-pricing": {
      "get": {
        "operationId": "getFairPricing",
        "summary": "Get fair pricing settings",
        "description": "Your store's fair pricing (purchasing-power discounts by buyer country): on/off, the percent per tier, country exceptions, and the tier table with every country in it.",
        "tags": [
          "Launch pricing"
        ],
        "responses": {
          "200": {
            "description": "The settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FairPricingSettings"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateFairPricing",
        "summary": "Update fair pricing settings",
        "description": "Fields you leave out keep their value. The buyer's country is the country of their IP, checked on our server against their browser time zone and any billing country they type (a VPN gets full price); after payment the billing country collected is compared again and a mismatch is flagged on the order (`pricing.fairPricing.verified: false`). Products follow these settings unless set otherwise (`PATCH /products/{id}/fair-pricing`). Turning it on needs the Growth plan.",
        "tags": [
          "Launch pricing"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "tiers": {
                    "type": "object",
                    "description": "Whole percent off per tier (0–80). `t1` = price level 0.58–0.72 of the US (e.g. Spain, Japan), `t2` = 0.45–0.58 (Poland, Brazil, Mexico), `t3` = 0.33–0.45 (Romania, Colombia, Kenya), `t4` = under 0.33 (India, Indonesia, Nigeria).",
                    "properties": {
                      "t1": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 80,
                        "example": 20
                      },
                      "t2": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 80,
                        "example": 35
                      },
                      "t3": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 80,
                        "example": 50
                      },
                      "t4": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 80,
                        "example": 60
                      }
                    }
                  },
                  "countryOverrides": {
                    "type": "object",
                    "description": "Country exceptions: ISO country code → whole percent off (0–80). They win over the country's tier; 0 means full price. Sending this replaces the whole list.",
                    "additionalProperties": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 80
                    },
                    "example": {
                      "BR": 40,
                      "PT": 0
                    }
                  }
                }
              },
              "example": {
                "enabled": true,
                "tiers": {
                  "t4": 50
                },
                "countryOverrides": {
                  "BR": 40
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FairPricingSettings"
                }
              }
            }
          },
          "400": {
            "description": "The settings are invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Tier t1 must be a whole percent between 0 and 80"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Turning on fair pricing needs the Growth plan or higher (feature `fairPricing`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "fairPricing",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/fair-pricing/analytics": {
      "get": {
        "operationId": "getFairPricingAnalytics",
        "summary": "Fair pricing results by country",
        "description": "Orders that got fair pricing in the last `days` days (default 30, up to 365), by country: orders, revenue and discount given (main product price, your currency), and billing-country mismatches.",
        "tags": [
          "Launch pricing"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "days",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "example": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "days": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "sales": {
                          "type": "integer"
                        },
                        "revenue": {
                          "type": "number"
                        },
                        "discountGiven": {
                          "type": "number"
                        },
                        "countries": {
                          "type": "integer"
                        },
                        "mismatches": {
                          "type": "integer"
                        }
                      }
                    },
                    "byCountry": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "country": {
                            "type": "string"
                          },
                          "countryName": {
                            "type": "string"
                          },
                          "sales": {
                            "type": "integer"
                          },
                          "revenue": {
                            "type": "number"
                          },
                          "discountGiven": {
                            "type": "number"
                          },
                          "averagePercentOff": {
                            "type": "integer"
                          },
                          "mismatches": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "daily": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "example": "2026-10-16"
                          },
                          "sales": {
                            "type": "integer"
                          },
                          "revenue": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/pricing/early-bird": {
      "get": {
        "operationId": "listPriceSteps",
        "summary": "List products with early-bird price steps",
        "description": "Every product of yours (current mode) with price steps, with seats sold and the step in effect.",
        "tags": [
          "Launch pricing"
        ],
        "responses": {
          "200": {
            "description": "Products with price steps",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string"
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "productId": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "price": {
                            "type": "number"
                          },
                          "thumbnail": {
                            "type": "string",
                            "nullable": true
                          },
                          "priceSteps": {
                            "$ref": "#/components/schemas/PriceSteps"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/launch-pricing": {
      "get": {
        "operationId": "getProductLaunchPricing",
        "summary": "Get a product's price steps and fair pricing mode",
        "description": "The product's early-bird price steps (with seats sold and the step in effect) and its fair pricing mode.",
        "tags": [
          "Launch pricing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "responses": {
          "200": {
            "description": "The product's launch pricing",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "productId": {
                      "type": "string"
                    },
                    "price": {
                      "type": "number"
                    },
                    "priceSteps": {
                      "$ref": "#/components/schemas/PriceSteps"
                    },
                    "fairPricing": {
                      "type": "object",
                      "properties": {
                        "mode": {
                          "type": "string",
                          "enum": [
                            "store",
                            "off",
                            "custom"
                          ]
                        },
                        "tiers": {
                          "type": "object",
                          "description": "Whole percent off per tier (0–80). `t1` = price level 0.58–0.72 of the US (e.g. Spain, Japan), `t2` = 0.45–0.58 (Poland, Brazil, Mexico), `t3` = 0.33–0.45 (Romania, Colombia, Kenya), `t4` = under 0.33 (India, Indonesia, Nigeria).",
                          "properties": {
                            "t1": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 80,
                              "example": 20
                            },
                            "t2": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 80,
                              "example": 35
                            },
                            "t3": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 80,
                              "example": 50
                            },
                            "t4": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 80,
                              "example": 60
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid product id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Invalid product id"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No product with this id in your account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/price-steps": {
      "put": {
        "operationId": "setProductPriceSteps",
        "summary": "Set a product's early-bird price steps",
        "description": "Up to 5 steps, each a price below the product's price with a number of buyers (`maxSales`), an end date (`endsAt`), or both; prices go up from step to step. The first open step is the price in effect, then the product's own price. Seats are taken atomically when a checkout starts and held 20 minutes, so concurrent buyers can never oversell a step (a payment that arrives after its hold expired is honoured and counted). Send the step `id`s you got back to keep their counts when you edit. Needs a fixed price and no variants. Turning it on needs the Growth plan.",
        "tags": [
          "Launch pricing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "steps": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "An existing step's id (keeps its counts)"
                        },
                        "price": {
                          "type": "number"
                        },
                        "maxSales": {
                          "type": "integer",
                          "nullable": true
                        },
                        "endsAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "enabled": true,
                "steps": [
                  {
                    "price": 49,
                    "maxSales": 50
                  },
                  {
                    "price": 59,
                    "endsAt": "2026-11-01T00:00:00.000Z"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved steps",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "productId": {
                      "type": "string"
                    },
                    "price": {
                      "type": "number"
                    },
                    "priceSteps": {
                      "$ref": "#/components/schemas/PriceSteps"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The steps are invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Step 1: the price must be lower than the product's price"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Turning on price steps needs the Growth plan or higher (feature `earlyBirdPricing`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "earlyBirdPricing",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "No product with this id in your account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/products/{id}/fair-pricing": {
      "patch": {
        "operationId": "setProductFairPricing",
        "summary": "Set a product's fair pricing mode",
        "description": "`store` follows your store settings (default), `off` sells this product at full price everywhere, `custom` uses your store's countries with this product's own percent per tier. `custom` needs the Growth plan.",
        "tags": [
          "Launch pricing"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "store",
                      "off",
                      "custom"
                    ]
                  },
                  "tiers": {
                    "type": "object",
                    "description": "Whole percent off per tier (0–80). `t1` = price level 0.58–0.72 of the US (e.g. Spain, Japan), `t2` = 0.45–0.58 (Poland, Brazil, Mexico), `t3` = 0.33–0.45 (Romania, Colombia, Kenya), `t4` = under 0.33 (India, Indonesia, Nigeria).",
                    "properties": {
                      "t1": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 80,
                        "example": 20
                      },
                      "t2": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 80,
                        "example": 35
                      },
                      "t3": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 80,
                        "example": 50
                      },
                      "t4": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 80,
                        "example": 60
                      }
                    }
                  }
                }
              },
              "example": {
                "mode": "custom",
                "tiers": {
                  "t3": 40,
                  "t4": 50
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved mode",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "productId": {
                      "type": "string"
                    },
                    "fairPricing": {
                      "type": "object",
                      "properties": {
                        "mode": {
                          "type": "string"
                        },
                        "tiers": {
                          "type": "object",
                          "description": "Whole percent off per tier (0–80). `t1` = price level 0.58–0.72 of the US (e.g. Spain, Japan), `t2` = 0.45–0.58 (Poland, Brazil, Mexico), `t3` = 0.33–0.45 (Romania, Colombia, Kenya), `t4` = under 0.33 (India, Indonesia, Nigeria).",
                          "properties": {
                            "t1": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 80,
                              "example": 20
                            },
                            "t2": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 80,
                              "example": 35
                            },
                            "t3": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 80,
                              "example": 50
                            },
                            "t4": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 80,
                              "example": 60
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The mode is invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "mode must be \"store\", \"off\" or \"custom\""
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — A custom fair pricing table needs the Growth plan or higher (feature `fairPricing`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "fairPricing",
                    "currentPlan": "free",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "No product with this id in your account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/pricing/quote/{productId}": {
      "get": {
        "operationId": "getPriceQuote",
        "summary": "Get the price a buyer pays now",
        "description": "PUBLIC — no API key: the price this buyer pays for a product right now and why — early-bird seats left, the flash sale countdown, fair pricing for their country (from their IP, checked against `tz`). Never cached. Use it on your own product pages; the checkout charges the same price.",
        "tags": [
          "Launch pricing"
        ],
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "productId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Product id"
          },
          {
            "in": "query",
            "name": "variantId",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "discountId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A discount the buyer applied (best price wins)"
          },
          {
            "in": "query",
            "name": "tz",
            "required": false,
            "schema": {
              "type": "string",
              "example": "Asia/Kolkata"
            },
            "description": "The buyer's browser time zone (IANA). Without it, fair pricing does not apply."
          },
          {
            "in": "query",
            "name": "billingCountry",
            "required": false,
            "schema": {
              "type": "string",
              "example": "IN"
            }
          },
          {
            "in": "query",
            "name": "vid",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The A/B test visitor id (`POST /ab-tests/public/assign`). With `abv`, in a running price test the quote starts from that visitor's tested list price, as at checkout."
          },
          {
            "in": "query",
            "name": "abv",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The A/B variant id the page rendered. The tested price applies only when it matches the visitor's assignment."
          },
          {
            "in": "query",
            "name": "orderBumpIds",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated order bump ids the buyer ticked. Adds `cart`: each item's price, the \"Buy more, save more\" tier and the total the checkout charges (before tax)."
          }
        ],
        "responses": {
          "200": {
            "description": "The quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PriceQuote"
                }
              }
            }
          },
          "404": {
            "description": "No such product",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/bundles": {
      "get": {
        "operationId": "listBundles",
        "summary": "List your bundles",
        "description": "Every product of yours that is a bundle (current live / test mode), newest first, with what each includes and the buyer's saving.",
        "tags": [
          "Bundles"
        ],
        "responses": {
          "200": {
            "description": "Your bundles",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bundles": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Bundle"
                      }
                    }
                  }
                },
                "example": {
                  "bundles": [
                    {
                      "productId": "665f1c2a9b3e4a0012ab34cd",
                      "name": "Creator Starter Kit",
                      "price": 76,
                      "published": true,
                      "productIds": [
                        "665f1c2a9b3e4a0012ab34ce",
                        "665f1c2a9b3e4a0012ab34cf",
                        "665f1c2a9b3e4a0012ab34d0"
                      ],
                      "items": [
                        {
                          "_id": "665f1c2a9b3e4a0012ab34ce",
                          "name": "Notion Content OS",
                          "thumbnail": "https://cdn.example.com/os.png",
                          "price": 49,
                          "productType": "file"
                        },
                        {
                          "_id": "665f1c2a9b3e4a0012ab34cf",
                          "name": "Hook Library",
                          "thumbnail": "",
                          "price": 29,
                          "productType": "file"
                        },
                        {
                          "_id": "665f1c2a9b3e4a0012ab34d0",
                          "name": "Launch Week course",
                          "thumbnail": "",
                          "price": 36,
                          "productType": "course"
                        }
                      ],
                      "value": 114,
                      "savings": 38,
                      "savingsPercent": 33
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "User not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/bundles/{id}": {
      "get": {
        "operationId": "getBundle",
        "summary": "Get a product's bundle",
        "description": "What a product includes. A product that is not a bundle answers with empty `productIds` and `items`.",
        "tags": [
          "Bundles"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The bundle product's id"
          }
        ],
        "responses": {
          "200": {
            "description": "The bundle",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bundle": {
                      "$ref": "#/components/schemas/Bundle"
                    }
                  }
                },
                "example": {
                  "bundle": {
                    "productId": "665f1c2a9b3e4a0012ab34cd",
                    "name": "Creator Starter Kit",
                    "price": 76,
                    "published": true,
                    "productIds": [
                      "665f1c2a9b3e4a0012ab34ce",
                      "665f1c2a9b3e4a0012ab34cf",
                      "665f1c2a9b3e4a0012ab34d0"
                    ],
                    "items": [
                      {
                        "_id": "665f1c2a9b3e4a0012ab34ce",
                        "name": "Notion Content OS",
                        "thumbnail": "https://cdn.example.com/os.png",
                        "price": 49,
                        "productType": "file"
                      },
                      {
                        "_id": "665f1c2a9b3e4a0012ab34cf",
                        "name": "Hook Library",
                        "thumbnail": "",
                        "price": 29,
                        "productType": "file"
                      },
                      {
                        "_id": "665f1c2a9b3e4a0012ab34d0",
                        "name": "Launch Week course",
                        "thumbnail": "",
                        "price": 36,
                        "productType": "course"
                      }
                    ],
                    "value": 114,
                    "savings": 38,
                    "savingsPercent": 33
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Product not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateBundle",
        "summary": "Make a product a bundle (or change what it includes)",
        "description": "Sets the products this product delivers. Create the bundle product first (POST /products, its price is the bundle price), then call this. The product itself must have a fixed price.",
        "tags": [
          "Bundles"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The bundle product's id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productIds"
                ],
                "properties": {
                  "productIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 0,
                    "maxItems": 10,
                    "description": "2–10 of your other one-time products, in display order. Not pay-what-you-want, not credit packs, not waitlist products, not another bundle, same mode (live / test). An empty list turns the product back into a normal one.",
                    "example": [
                      "665f1c2a9b3e4a0012ab34ce",
                      "665f1c2a9b3e4a0012ab34cf",
                      "665f1c2a9b3e4a0012ab34d0"
                    ]
                  }
                }
              },
              "example": {
                "productIds": [
                  "665f1c2a9b3e4a0012ab34ce",
                  "665f1c2a9b3e4a0012ab34cf",
                  "665f1c2a9b3e4a0012ab34d0"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved bundle",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bundle": {
                      "$ref": "#/components/schemas/Bundle"
                    }
                  }
                },
                "example": {
                  "bundle": {
                    "productId": "665f1c2a9b3e4a0012ab34cd",
                    "name": "Creator Starter Kit",
                    "price": 76,
                    "published": true,
                    "productIds": [
                      "665f1c2a9b3e4a0012ab34ce",
                      "665f1c2a9b3e4a0012ab34cf",
                      "665f1c2a9b3e4a0012ab34d0"
                    ],
                    "items": [
                      {
                        "_id": "665f1c2a9b3e4a0012ab34ce",
                        "name": "Notion Content OS",
                        "thumbnail": "https://cdn.example.com/os.png",
                        "price": 49,
                        "productType": "file"
                      },
                      {
                        "_id": "665f1c2a9b3e4a0012ab34cf",
                        "name": "Hook Library",
                        "thumbnail": "",
                        "price": 29,
                        "productType": "file"
                      },
                      {
                        "_id": "665f1c2a9b3e4a0012ab34d0",
                        "name": "Launch Week course",
                        "thumbnail": "",
                        "price": 36,
                        "productType": "course"
                      }
                    ],
                    "value": 114,
                    "savings": 38,
                    "savingsPercent": 33
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid list, or a product that can't be bundled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Pay-what-you-want products can't be in a bundle"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FEATURE_LOCKED — turning a product into a bundle needs the Growth plan (or higher). Upgrade at https://app.pocketsflow.com/profile/billing. Reading and removing are never gated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "bundles",
                    "currentPlan": "tier1",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "Product not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteBundle",
        "summary": "Turn a bundle back into a normal product",
        "description": "Removes the included products. Buyers who already bought the bundle keep every product they got. Never gated.",
        "tags": [
          "Bundles"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The bundle product's id"
          }
        ],
        "responses": {
          "200": {
            "description": "The product, no longer a bundle",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bundle": {
                      "$ref": "#/components/schemas/Bundle"
                    }
                  }
                },
                "example": {
                  "bundle": {
                    "productId": "665f1c2a9b3e4a0012ab34cd",
                    "name": "Creator Starter Kit",
                    "price": 76,
                    "published": true,
                    "productIds": [
                      "665f1c2a9b3e4a0012ab34ce",
                      "665f1c2a9b3e4a0012ab34cf",
                      "665f1c2a9b3e4a0012ab34d0"
                    ],
                    "items": [
                      {
                        "_id": "665f1c2a9b3e4a0012ab34ce",
                        "name": "Notion Content OS",
                        "thumbnail": "https://cdn.example.com/os.png",
                        "price": 49,
                        "productType": "file"
                      },
                      {
                        "_id": "665f1c2a9b3e4a0012ab34cf",
                        "name": "Hook Library",
                        "thumbnail": "",
                        "price": 29,
                        "productType": "file"
                      },
                      {
                        "_id": "665f1c2a9b3e4a0012ab34d0",
                        "name": "Launch Week course",
                        "thumbnail": "",
                        "price": 36,
                        "productType": "course"
                      }
                    ],
                    "value": 114,
                    "savings": 38,
                    "savingsPercent": 33
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Invalid product id"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Product not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Product not found"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/tiered-discounts": {
      "get": {
        "operationId": "getTieredDiscounts",
        "summary": "Get your buy more, save more tiers",
        "description": "The store-wide tiers. Reading is never gated.",
        "tags": [
          "Buy more, save more"
        ],
        "responses": {
          "200": {
            "description": "The tiers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tieredDiscounts": {
                      "$ref": "#/components/schemas/TieredDiscounts"
                    }
                  }
                },
                "example": {
                  "tieredDiscounts": {
                    "enabled": true,
                    "tiers": [
                      {
                        "minItems": 2,
                        "percentOff": 10
                      },
                      {
                        "minItems": 3,
                        "percentOff": 20
                      }
                    ],
                    "combineWithCodes": false
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "updateTieredDiscounts",
        "summary": "Save your buy more, save more tiers",
        "description": "Every field is optional; omitted fields keep their saved value. Turning it on (or saving while on) needs the Growth plan.",
        "tags": [
          "Buy more, save more"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "example": true
                  },
                  "tiers": {
                    "type": "array",
                    "maxItems": 5,
                    "description": "Up to 5 tiers: whole `minItems` 2–20 (all different) and whole `percentOff` 1–90, a bigger cart always saving more",
                    "items": {
                      "type": "object",
                      "required": [
                        "minItems",
                        "percentOff"
                      ],
                      "properties": {
                        "minItems": {
                          "type": "integer",
                          "example": 2
                        },
                        "percentOff": {
                          "type": "integer",
                          "example": 10
                        }
                      }
                    }
                  },
                  "combineWithCodes": {
                    "type": "boolean",
                    "example": false
                  }
                }
              },
              "example": {
                "enabled": true,
                "tiers": [
                  {
                    "minItems": 2,
                    "percentOff": 10
                  },
                  {
                    "minItems": 3,
                    "percentOff": 20
                  }
                ],
                "combineWithCodes": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved tiers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tieredDiscounts": {
                      "$ref": "#/components/schemas/TieredDiscounts"
                    }
                  }
                },
                "example": {
                  "tieredDiscounts": {
                    "enabled": true,
                    "tiers": [
                      {
                        "minItems": 2,
                        "percentOff": 10
                      },
                      {
                        "minItems": 3,
                        "percentOff": 20
                      }
                    ],
                    "combineWithCodes": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid tiers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "A tier with more items must give a bigger discount"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FEATURE_LOCKED — turning tiers on needs the Growth plan (or higher). Upgrade at https://app.pocketsflow.com/profile/billing. Reading and removing are never gated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "tieredDiscounts",
                    "currentPlan": "tier1",
                    "requiredPlan": "tier2",
                    "requiredPlanName": "Growth",
                    "requiredPlanPrice": 49,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteTieredDiscounts",
        "summary": "Remove your buy more, save more tiers",
        "description": "Turns tiers off and removes them. Never gated.",
        "tags": [
          "Buy more, save more"
        ],
        "responses": {
          "200": {
            "description": "Tiers removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tieredDiscounts": {
                      "$ref": "#/components/schemas/TieredDiscounts"
                    }
                  }
                },
                "example": {
                  "tieredDiscounts": {
                    "enabled": false,
                    "tiers": [],
                    "combineWithCodes": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Invalid request"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/forms": {
      "get": {
        "operationId": "listSignupForms",
        "tags": [
          "Signup forms"
        ],
        "summary": "List signup forms with stats",
        "description": "Your signup forms, newest first, each with views, signups, conversion and its embed code. Archived forms only with `archived=true`.",
        "parameters": [
          {
            "in": "query",
            "name": "archived",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "false"
            },
            "description": "`true` lists archived forms instead"
          }
        ],
        "responses": {
          "200": {
            "description": "Your forms",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "forms": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SignupForm"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createSignupForm",
        "tags": [
          "Signup forms"
        ],
        "summary": "Create a signup form",
        "description": "Creates an inline form, slide-in or popup. Needs the Growth plan or above. Headline, text and button default to sensible copy for the type. Up to 100 forms per account.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80,
                    "description": "Internal name, shown only in your dashboard",
                    "example": "Blog footer"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "inline",
                      "slide_in",
                      "popup"
                    ],
                    "description": "`inline` renders where the script tag is; `slide_in` slides into a bottom corner; `popup` opens a centred dialog (on exit intent on desktop, after the delay on touch screens).",
                    "example": "inline"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused"
                    ],
                    "description": "A paused form renders nothing and refuses signups."
                  },
                  "archived": {
                    "type": "boolean",
                    "description": "Hide from the list (implies it stops showing)"
                  },
                  "headline": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Heading of the form",
                    "example": "Get the newsletter"
                  },
                  "body": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Text under the heading",
                    "example": "One useful email a week."
                  },
                  "buttonLabel": {
                    "type": "string",
                    "maxLength": 40,
                    "description": "Submit button text",
                    "example": "Subscribe"
                  },
                  "placeholder": {
                    "type": "string",
                    "maxLength": 60,
                    "description": "Email field placeholder",
                    "example": "you@example.com"
                  },
                  "collectName": {
                    "type": "boolean",
                    "description": "Also ask for a first name"
                  },
                  "successHeadline": {
                    "type": "string",
                    "maxLength": 80,
                    "description": "Heading after a signup",
                    "example": "You're in"
                  },
                  "successMessage": {
                    "type": "string",
                    "maxLength": 300,
                    "description": "Text after a signup"
                  },
                  "leadMagnetProductId": {
                    "type": "string",
                    "nullable": true,
                    "description": "One of your products to hand over after the signup: the success state links to its page (after confirmation, with double opt-in). `null` removes it."
                  },
                  "tags": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "string",
                      "maxLength": 40
                    },
                    "description": "Tags every signup gets in your audience (send to them by tag)",
                    "example": [
                      "blog",
                      "lead-magnet"
                    ]
                  },
                  "doubleOptIn": {
                    "type": "boolean",
                    "description": "Email a confirmation link first; the person joins your audience only after clicking it (links expire after 7 days)."
                  },
                  "accentColor": {
                    "type": "string",
                    "pattern": "^#[0-9a-fA-F]{6}$",
                    "description": "Button colour on your site. The button text turns dark on light colours.",
                    "example": "#2563eb"
                  },
                  "position": {
                    "type": "string",
                    "enum": [
                      "bottom_right",
                      "bottom_left"
                    ],
                    "description": "Slide-in corner"
                  },
                  "trigger": {
                    "type": "object",
                    "description": "When a slide-in or popup shows. A popup with `exitIntent` waits for the pointer to leave the page (desktop) and uses `delaySeconds` on touch screens; otherwise `scrollPercent` (when above 0) or `delaySeconds` decides.",
                    "properties": {
                      "delaySeconds": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 120,
                        "example": 5
                      },
                      "scrollPercent": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100,
                        "example": 0
                      },
                      "exitIntent": {
                        "type": "boolean",
                        "example": true
                      }
                    }
                  },
                  "frequencyDays": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 365,
                    "description": "After a visitor closes a slide-in or popup, hide it from them for this many days (0 = show on every page view). People who signed up never see it again.",
                    "example": 7
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created form",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignupForm"
                }
              }
            }
          },
          "400": {
            "description": "A field is invalid, the lead magnet is not your product, or you have 100 forms",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "accentColor must be a hex colour like #2563eb"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include signup forms (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`. Upgrade at https://app.pocketsflow.com/profile/billing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/forms/{id}": {
      "get": {
        "operationId": "getSignupForm",
        "tags": [
          "Signup forms"
        ],
        "summary": "Get a signup form with daily stats",
        "description": "The form, its embed code and views / signups per UTC day for the last 30 days.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711b0c39e3f1a0012cd9911"
            },
            "description": "Signup form id"
          }
        ],
        "responses": {
          "200": {
            "description": "The form",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SignupForm"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "daily": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": "string",
                                "format": "date",
                                "example": "2026-10-15"
                              },
                              "views": {
                                "type": "integer",
                                "example": 64
                              },
                              "signups": {
                                "type": "integer",
                                "example": 4
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateSignupForm",
        "tags": [
          "Signup forms"
        ],
        "summary": "Update, pause or archive a signup form",
        "description": "Send only the fields to change. Editing needs the Growth plan; pausing (`status: \"paused\"`) and archiving always work. Changes show on your site within a minute.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711b0c39e3f1a0012cd9911"
            },
            "description": "Signup form id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80,
                    "description": "Internal name, shown only in your dashboard",
                    "example": "Blog footer"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "inline",
                      "slide_in",
                      "popup"
                    ],
                    "description": "`inline` renders where the script tag is; `slide_in` slides into a bottom corner; `popup` opens a centred dialog (on exit intent on desktop, after the delay on touch screens).",
                    "example": "inline"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused"
                    ],
                    "description": "A paused form renders nothing and refuses signups."
                  },
                  "archived": {
                    "type": "boolean",
                    "description": "Hide from the list (implies it stops showing)"
                  },
                  "headline": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Heading of the form",
                    "example": "Get the newsletter"
                  },
                  "body": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Text under the heading",
                    "example": "One useful email a week."
                  },
                  "buttonLabel": {
                    "type": "string",
                    "maxLength": 40,
                    "description": "Submit button text",
                    "example": "Subscribe"
                  },
                  "placeholder": {
                    "type": "string",
                    "maxLength": 60,
                    "description": "Email field placeholder",
                    "example": "you@example.com"
                  },
                  "collectName": {
                    "type": "boolean",
                    "description": "Also ask for a first name"
                  },
                  "successHeadline": {
                    "type": "string",
                    "maxLength": 80,
                    "description": "Heading after a signup",
                    "example": "You're in"
                  },
                  "successMessage": {
                    "type": "string",
                    "maxLength": 300,
                    "description": "Text after a signup"
                  },
                  "leadMagnetProductId": {
                    "type": "string",
                    "nullable": true,
                    "description": "One of your products to hand over after the signup: the success state links to its page (after confirmation, with double opt-in). `null` removes it."
                  },
                  "tags": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "string",
                      "maxLength": 40
                    },
                    "description": "Tags every signup gets in your audience (send to them by tag)",
                    "example": [
                      "blog",
                      "lead-magnet"
                    ]
                  },
                  "doubleOptIn": {
                    "type": "boolean",
                    "description": "Email a confirmation link first; the person joins your audience only after clicking it (links expire after 7 days)."
                  },
                  "accentColor": {
                    "type": "string",
                    "pattern": "^#[0-9a-fA-F]{6}$",
                    "description": "Button colour on your site. The button text turns dark on light colours.",
                    "example": "#2563eb"
                  },
                  "position": {
                    "type": "string",
                    "enum": [
                      "bottom_right",
                      "bottom_left"
                    ],
                    "description": "Slide-in corner"
                  },
                  "trigger": {
                    "type": "object",
                    "description": "When a slide-in or popup shows. A popup with `exitIntent` waits for the pointer to leave the page (desktop) and uses `delaySeconds` on touch screens; otherwise `scrollPercent` (when above 0) or `delaySeconds` decides.",
                    "properties": {
                      "delaySeconds": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 120,
                        "example": 5
                      },
                      "scrollPercent": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100,
                        "example": 0
                      },
                      "exitIntent": {
                        "type": "boolean",
                        "example": true
                      }
                    }
                  },
                  "frequencyDays": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 365,
                    "description": "After a visitor closes a slide-in or popup, hide it from them for this many days (0 = show on every page view). People who signed up never see it again.",
                    "example": 7
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated form",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignupForm"
                }
              }
            }
          },
          "400": {
            "description": "A field is invalid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include signup forms (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`. Upgrade at https://app.pocketsflow.com/profile/billing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteSignupForm",
        "tags": [
          "Signup forms"
        ],
        "summary": "Delete a signup form",
        "description": "Deletes the form and its stats. Embeds of it render nothing from then on. People who signed up stay in your audience.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711b0c39e3f1a0012cd9911"
            },
            "description": "Signup form id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean",
                      "example": true
                    },
                    "_id": {
                      "type": "string",
                      "example": "6711b0c39e3f1a0012cd9911"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/forms/public/{id}": {
      "get": {
        "operationId": "getPublicSignupForm",
        "tags": [
          "Signup forms"
        ],
        "summary": "Public form config (embed)",
        "description": "PUBLIC (no API key, any origin) — what `forms.js` renders: copy, type, trigger, frequency, colour and the lead magnet's name and link. `active: false` (and nothing else) when the form is paused or the account's plan no longer includes forms. Cached for 60 seconds. Never includes stats.",
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711b0c39e3f1a0012cd9911"
            },
            "description": "Signup form id"
          }
        ],
        "responses": {
          "200": {
            "description": "The form config",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "example": "6711b0c39e3f1a0012cd9911"
                    },
                    "active": {
                      "type": "boolean",
                      "example": true
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "inline",
                        "slide_in",
                        "popup"
                      ]
                    },
                    "headline": {
                      "type": "string"
                    },
                    "body": {
                      "type": "string"
                    },
                    "buttonLabel": {
                      "type": "string"
                    },
                    "placeholder": {
                      "type": "string"
                    },
                    "collectName": {
                      "type": "boolean"
                    },
                    "successHeadline": {
                      "type": "string"
                    },
                    "successMessage": {
                      "type": "string"
                    },
                    "doubleOptIn": {
                      "type": "boolean"
                    },
                    "accentColor": {
                      "type": "string"
                    },
                    "position": {
                      "type": "string",
                      "enum": [
                        "bottom_right",
                        "bottom_left"
                      ]
                    },
                    "trigger": {
                      "type": "object",
                      "description": "When a slide-in or popup shows. A popup with `exitIntent` waits for the pointer to leave the page (desktop) and uses `delaySeconds` on touch screens; otherwise `scrollPercent` (when above 0) or `delaySeconds` decides.",
                      "properties": {
                        "delaySeconds": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 120,
                          "example": 5
                        },
                        "scrollPercent": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 100,
                          "example": 0
                        },
                        "exitIntent": {
                          "type": "boolean",
                          "example": true
                        }
                      }
                    },
                    "frequencyDays": {
                      "type": "integer"
                    },
                    "leadMagnet": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "name": {
                          "type": "string",
                          "example": "The pricing playbook"
                        },
                        "url": {
                          "type": "string",
                          "example": "https://redmug.pocketsflow.com/pricing-playbook"
                        }
                      }
                    },
                    "showPoweredBy": {
                      "type": "boolean"
                    },
                    "poweredByUrl": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/forms/public/{id}/subscribe": {
      "post": {
        "operationId": "subscribeWithSignupForm",
        "tags": [
          "Signup forms"
        ],
        "summary": "Sign up through a form (public)",
        "description": "PUBLIC (no API key, any origin) — what `forms.js` posts; use it to build your own form markup. Adds the email to the form owner's audience with the form's tags, or emails a confirmation link when the form uses double opt-in (`status: \"confirm_email\"`). Never says whether the address was already subscribed. Limited to 10 signups per 15 minutes per IP (429). Spam protection: a non-empty `company` (honeypot) or an `elapsedMs` under 1.5 s is answered with a success and ignored.",
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711b0c39e3f1a0012cd9911"
            },
            "description": "Signup form id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "ada@example.com"
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 80,
                    "example": "Ada"
                  },
                  "company": {
                    "type": "string",
                    "description": "Honeypot: leave empty (hidden from people)"
                  },
                  "elapsedMs": {
                    "type": "integer",
                    "description": "Milliseconds the form was on screen before submitting",
                    "example": 8200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signed up, or a confirmation email is on its way",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "subscribed",
                        "confirm_email"
                      ]
                    },
                    "leadMagnet": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "url": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The email is not valid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Enter a valid email address"
                }
              }
            }
          },
          "404": {
            "description": "Unknown form, paused, or the account's plan no longer includes forms",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This form is not accepting signups right now"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/posts/{id}/resend": {
      "get": {
        "operationId": "getPostResend",
        "tags": [
          "Posts"
        ],
        "summary": "Get non-openers and the resend of a post",
        "description": "Who received the post, who opened it, how many have not (who a resend would go to now), and the resend with its own results once scheduled. Opens are unique per person (a tracking image in every campaign email; clients that block images count as not opened).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Post id"
          }
        ],
        "responses": {
          "200": {
            "description": "Resend status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostResend"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "scheduleResendToNonOpeners",
        "tags": [
          "Posts"
        ],
        "summary": "Resend a post to non-openers",
        "description": "Schedules the same email with a new subject for `delayHours` after the original send (default 48; never sooner than 10 minutes from now), to everyone who received it — or one of its subject-test arms — and has not opened it by then and is still subscribed. Needs the Growth plan. The resend is its own post (`resend.postId`), counts against your monthly sends like any campaign, and goes through the same send checks. One resend per post (a failed one can be scheduled again).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Post id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subject"
                ],
                "properties": {
                  "subject": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "The new subject line",
                    "example": "In case you missed it: the spring drop"
                  },
                  "delayHours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 168,
                    "default": 48,
                    "description": "Hours after the original send"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Scheduled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostResend"
                }
              }
            }
          },
          "400": {
            "description": "Not a sent post, nobody left to resend to, the sender is not verified, or the subject is missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Everyone who received this post has opened it"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The send would exceed your monthly send allowance (`code: SEND_ALLOWANCE_EXCEEDED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your plan does not include resends and subject tests (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`. Upgrade at https://app.pocketsflow.com/profile/billing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "A resend of this post is already scheduled or was sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "cancelResendToNonOpeners",
        "tags": [
          "Posts"
        ],
        "summary": "Cancel a scheduled resend",
        "description": "Cancels a resend that has not started sending (its post is deleted). Always allowed.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Post id"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostResend"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "The post has no resend",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "This post has no scheduled resend"
                }
              }
            }
          },
          "409": {
            "description": "The resend already started sending",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/newsletters/posts/{id}/subject-test": {
      "get": {
        "operationId": "getSubjectTest",
        "tags": [
          "Posts"
        ],
        "summary": "Get a post's subject test",
        "description": "The test settings, each arm's live results (unique opens) and the winner's send to the rest. `subjectTest` is null for a post without a test.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Post id"
          }
        ],
        "responses": {
          "200": {
            "description": "Subject test",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubjectTest"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "startSubjectTest",
        "tags": [
          "Posts"
        ],
        "summary": "Send a post with a subject test",
        "description": "Instead of sending a draft to everyone at once: sends the post's subject (A) and `subjectB` to two random halves of a `testPercent` slice of its audience now (default 20%), waits `waitHours` (default 4), then picks the subject with the higher unique open rate (a tie keeps A) and sends it to everyone else automatically. Needs the Growth plan and an audience of at least 50 subscribers. Gated and metered like Send now: the whole audience must fit in your monthly sends. The post shows `scheduled` until the winner goes out.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Post id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subjectB"
                ],
                "properties": {
                  "subjectB": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "The second subject line (must differ from the post's)",
                    "example": "Your spring drop is here (20% off inside)"
                  },
                  "testPercent": {
                    "type": "integer",
                    "minimum": 10,
                    "maximum": 50,
                    "default": 20,
                    "description": "Share of the audience in the test, both arms together"
                  },
                  "waitHours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 72,
                    "default": 4,
                    "description": "Hours before the winner is picked"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The test started",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubjectTest"
                }
              }
            }
          },
          "400": {
            "description": "Not a draft, missing content, sender not verified, same subject twice, or an audience under the minimum",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "A subject test needs at least 50 subscribers in the audience",
                  "code": "AUDIENCE_TOO_SMALL"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The send would exceed your monthly send allowance (`code: SEND_ALLOWANCE_EXCEEDED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Your plan does not include resends and subject tests (Growth and above). Body: `code: FEATURE_LOCKED`, `details.requiredPlan`. Upgrade at https://app.pocketsflow.com/profile/billing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The post already has a subject test",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/creator-services": {
      "get": {
        "operationId": "getCreatorServices",
        "tags": [
          "Creator services"
        ],
        "summary": "Get your creator services",
        "description": "What your plan gives you: priority support and its reply promise, your account manager and Slack channel (Volume), and dispute cover with this month's usage (Volume). Never gated.",
        "responses": {
          "200": {
            "description": "Your services",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatorServices"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/creator-services/account-manager": {
      "get": {
        "operationId": "getAccountManager",
        "tags": [
          "Creator services"
        ],
        "summary": "Get your account manager",
        "description": "Your dedicated account manager (name, photo, booking link) and the shared Slack channel state. `manager` is null while our team assigns one.",
        "responses": {
          "200": {
            "description": "Your account manager",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "manager": {
                      "type": "object",
                      "nullable": true,
                      "description": "Your account manager, or null while one is being assigned",
                      "properties": {
                        "name": {
                          "type": "string",
                          "example": "Maya Chen"
                        },
                        "title": {
                          "type": "string",
                          "nullable": true,
                          "example": "Account manager"
                        },
                        "email": {
                          "type": "string",
                          "nullable": true,
                          "example": "maya@pocketsflow.com"
                        },
                        "photoUrl": {
                          "type": "string",
                          "nullable": true,
                          "example": "https://cdn.pocketsflow.com/team/maya.jpg"
                        },
                        "bookingUrl": {
                          "type": "string",
                          "nullable": true,
                          "example": "https://cal.com/maya-pocketsflow/30min"
                        },
                        "assignedAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    },
                    "slackChannel": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "none",
                            "requested",
                            "ready"
                          ],
                          "description": "`requested` while our team sets it up; `ready` with the link"
                        },
                        "url": {
                          "type": "string",
                          "nullable": true,
                          "example": "https://pocketsflow.slack.com/archives/C07ABCDEF"
                        },
                        "requestedAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Reading your account manager needs the Volume plan or higher (feature `accountManager`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "accountManager",
                    "currentPlan": "free",
                    "requiredPlan": "volume",
                    "requiredPlanName": "Volume",
                    "requiredPlanPrice": 500,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/creator-services/account-manager/slack-channel": {
      "post": {
        "operationId": "requestSlackChannel",
        "tags": [
          "Creator services"
        ],
        "summary": "Request a shared Slack channel",
        "description": "Asks our team for a shared Slack channel. Answers 201 the first time and 200 (unchanged) while a request is open or the channel is ready; the link appears in `slackChannel.url` once it is set up.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "note": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Who should be in the channel, or anything we should know",
                    "example": "Please add ops@redmug.co too."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already requested (or ready)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "manager": {
                      "type": "object",
                      "nullable": true,
                      "description": "Your account manager, or null while one is being assigned",
                      "properties": {
                        "name": {
                          "type": "string",
                          "example": "Maya Chen"
                        },
                        "title": {
                          "type": "string",
                          "nullable": true,
                          "example": "Account manager"
                        },
                        "email": {
                          "type": "string",
                          "nullable": true,
                          "example": "maya@pocketsflow.com"
                        },
                        "photoUrl": {
                          "type": "string",
                          "nullable": true,
                          "example": "https://cdn.pocketsflow.com/team/maya.jpg"
                        },
                        "bookingUrl": {
                          "type": "string",
                          "nullable": true,
                          "example": "https://cal.com/maya-pocketsflow/30min"
                        },
                        "assignedAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    },
                    "slackChannel": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "none",
                            "requested",
                            "ready"
                          ],
                          "description": "`requested` while our team sets it up; `ready` with the link"
                        },
                        "url": {
                          "type": "string",
                          "nullable": true,
                          "example": "https://pocketsflow.slack.com/archives/C07ABCDEF"
                        },
                        "requestedAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Requested",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "manager": {
                      "type": "object",
                      "nullable": true,
                      "description": "Your account manager, or null while one is being assigned",
                      "properties": {
                        "name": {
                          "type": "string",
                          "example": "Maya Chen"
                        },
                        "title": {
                          "type": "string",
                          "nullable": true,
                          "example": "Account manager"
                        },
                        "email": {
                          "type": "string",
                          "nullable": true,
                          "example": "maya@pocketsflow.com"
                        },
                        "photoUrl": {
                          "type": "string",
                          "nullable": true,
                          "example": "https://cdn.pocketsflow.com/team/maya.jpg"
                        },
                        "bookingUrl": {
                          "type": "string",
                          "nullable": true,
                          "example": "https://cal.com/maya-pocketsflow/30min"
                        },
                        "assignedAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    },
                    "slackChannel": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "none",
                            "requested",
                            "ready"
                          ],
                          "description": "`requested` while our team sets it up; `ready` with the link"
                        },
                        "url": {
                          "type": "string",
                          "nullable": true,
                          "example": "https://pocketsflow.slack.com/archives/C07ABCDEF"
                        },
                        "requestedAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Requesting a shared Slack channel needs the Volume plan or higher (feature `accountManager`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "accountManager",
                    "currentPlan": "free",
                    "requiredPlan": "volume",
                    "requiredPlanName": "Volume",
                    "requiredPlanPrice": 500,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/creator-services/dispute-help": {
      "get": {
        "operationId": "listDisputeHelpRequests",
        "tags": [
          "Creator services"
        ],
        "summary": "List your dispute help requests",
        "description": "Your requests for help with a dispute, newest first (50 max).",
        "parameters": [
          {
            "in": "query",
            "name": "saleId",
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "Only the requests about this order"
          }
        ],
        "responses": {
          "200": {
            "description": "Your requests",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "requests": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DisputeHelpRequest"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createDisputeHelpRequest",
        "tags": [
          "Creator services"
        ],
        "summary": "Ask for help with a dispute",
        "description": "Sends our team the order (amount, product, buyer, status) and your notes. It opens a priority support thread where we reply. One open request per order (409 while one is open).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "saleId",
                  "message"
                ],
                "properties": {
                  "saleId": {
                    "type": "string",
                    "description": "The order id (one of your sales)",
                    "example": "665f1c2a9b3e4a0012ab34cd"
                  },
                  "message": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 5000,
                    "description": "What happened, and what the buyer received",
                    "example": "The buyer downloaded the course on day one and opened a dispute 40 days later as 'not received'."
                  },
                  "dueAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the response is due, if you know"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Request sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request": {
                      "$ref": "#/components/schemas/DisputeHelpRequest"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Asking for dispute help needs the Pro plan or higher (feature `prioritySupport`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "prioritySupport",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "description": "The order is not one of yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request for this order is already open",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "ALREADY_REQUESTED",
                  "message": "You already asked for help with this order. We will reply on the same thread."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/creator-services/dispute-covers": {
      "get": {
        "operationId": "listDisputeCovers",
        "tags": [
          "Creator services"
        ],
        "summary": "List your dispute covers",
        "description": "Every dispute on your sales that dispute cover looked at (covered, or excluded with the reason), newest first.",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "How many to return"
          }
        ],
        "responses": {
          "200": {
            "description": "Your dispute covers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "covers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DisputeCover"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/creator-services/dispute-covers/{saleId}": {
      "get": {
        "operationId": "getDisputeCover",
        "tags": [
          "Creator services"
        ],
        "summary": "Get the dispute cover of an order",
        "description": "The cover for one order's dispute. 404 when the order has none.",
        "parameters": [
          {
            "in": "path",
            "name": "saleId",
            "required": true,
            "schema": {
              "type": "string",
              "example": "665f1c2a9b3e4a0012ab34cd"
            },
            "description": "The order id"
          }
        ],
        "responses": {
          "200": {
            "description": "The cover",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cover": {
                      "$ref": "#/components/schemas/DisputeCover"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/sheets-sync": {
      "get": {
        "operationId": "getSheetsSync",
        "tags": [
          "Google Sheets sync"
        ],
        "summary": "Get Google Sheets sync",
        "description": "Whether the server can connect to Google (`configured`), whether your plan includes it, and your connection: the spreadsheet, what is synced, the last sync and the row counts. Never gated.",
        "responses": {
          "200": {
            "description": "Sync status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SheetsSync"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateSheetsSync",
        "tags": [
          "Google Sheets sync"
        ],
        "summary": "Choose what to sync",
        "description": "Turn the Sales and Customers tabs on or off. Rows already written stay.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "syncSales": {
                    "type": "boolean",
                    "description": "Append every new sale"
                  },
                  "syncCustomers": {
                    "type": "boolean",
                    "description": "Append every new buyer (one row per email)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "connection": {
                      "type": "object",
                      "description": "The connection, as `connection` in `GET /sheets-sync`"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Changing the sync needs the Pro plan or higher (feature `sheetsSync`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "sheetsSync",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "409": {
            "description": "Not connected, Google access was removed, or no spreadsheet chosen yet. `code`: `SHEETS_NOT_CONNECTED`, `SHEETS_RECONNECT` or `SHEETS_NO_SPREADSHEET`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "SHEETS_NOT_CONNECTED",
                  "message": "Connect your Google account first."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "disconnectSheetsSync",
        "tags": [
          "Google Sheets sync"
        ],
        "summary": "Disconnect Google Sheets",
        "description": "Stops the sync, revokes Pocketsflow's Google access and deletes the stored tokens. The spreadsheet and its rows stay in your Drive. Never gated.",
        "responses": {
          "200": {
            "description": "Disconnected",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "disconnected": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/sheets-sync/connect": {
      "post": {
        "operationId": "connectSheetsSync",
        "tags": [
          "Google Sheets sync"
        ],
        "summary": "Start connecting a Google account",
        "description": "Returns Google's consent page URL. Open it in a browser signed in to the Google account that should own the spreadsheet; Google then sends the creator back to the dashboard. The link works once, for 15 minutes.",
        "responses": {
          "200": {
            "description": "Consent URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "example": "https://accounts.google.com/o/oauth2/v2/auth?client_id=…&scope=https%3A%2F%2Fround-lake.dustinice.workers.dev%3A443%2Fhttps%2Fwww.googleapis.com%2Fauth%2Fdrive.file"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Connecting Google Sheets needs the Pro plan or higher (feature `sheetsSync`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "sheetsSync",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "description": "Google Sheets sync is not configured on this server (`code: SHEETS_NOT_CONFIGURED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "SHEETS_NOT_CONFIGURED",
                  "message": "Google Sheets sync is not available on this server yet."
                }
              }
            }
          }
        }
      }
    },
    "/sheets-sync/spreadsheet": {
      "put": {
        "operationId": "setSheetsSpreadsheet",
        "tags": [
          "Google Sheets sync"
        ],
        "summary": "Create or choose the spreadsheet",
        "description": "`create` makes a new spreadsheet with `Sales` and `Customers` tabs. `existing` reuses a spreadsheet Pocketsflow created before (Google's `drive.file` permission only lets Pocketsflow open its own files). Syncing starts from now, plus `backfillDays` of past sales.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "mode"
                ],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "create",
                      "existing"
                    ]
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Name of the new spreadsheet (create)",
                    "example": "Pocketsflow sales"
                  },
                  "spreadsheet": {
                    "type": "string",
                    "description": "The spreadsheet link or id (existing)"
                  },
                  "backfillDays": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 365,
                    "description": "Also add the sales of the last N days",
                    "example": 30
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Spreadsheet set; the first rows are on their way",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "connection": {
                      "type": "object",
                      "description": "The connection, as `connection` in `GET /sheets-sync`"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Choosing the spreadsheet needs the Pro plan or higher (feature `sheetsSync`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "sheetsSync",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "409": {
            "description": "Not connected, Google access was removed, or no spreadsheet chosen yet. `code`: `SHEETS_NOT_CONNECTED`, `SHEETS_RECONNECT` or `SHEETS_NO_SPREADSHEET`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "SHEETS_NOT_CONNECTED",
                  "message": "Connect your Google account first."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "502": {
            "description": "Google Sheets did not answer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Google Sheets sync is not configured on this server (`code: SHEETS_NOT_CONFIGURED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "SHEETS_NOT_CONFIGURED",
                  "message": "Google Sheets sync is not available on this server yet."
                }
              }
            }
          }
        }
      }
    },
    "/sheets-sync/backfill": {
      "post": {
        "operationId": "backfillSheetsSync",
        "tags": [
          "Google Sheets sync"
        ],
        "summary": "Add past sales",
        "description": "Queues the sales of the last `days` days. Sales already in the spreadsheet are skipped (each sale is written once); new rows are appended at the bottom.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "days"
                ],
                "properties": {
                  "days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365,
                    "example": 90
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Backfill queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "connection": {
                      "type": "object",
                      "description": "The connection, as `connection` in `GET /sheets-sync`"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Backfilling needs the Pro plan or higher (feature `sheetsSync`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "sheetsSync",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "409": {
            "description": "Not connected, Google access was removed, or no spreadsheet chosen yet. `code`: `SHEETS_NOT_CONNECTED`, `SHEETS_RECONNECT` or `SHEETS_NO_SPREADSHEET`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "SHEETS_NOT_CONNECTED",
                  "message": "Connect your Google account first."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/sheets-sync/sync": {
      "post": {
        "operationId": "syncSheetsNow",
        "tags": [
          "Google Sheets sync"
        ],
        "summary": "Sync now",
        "description": "Runs the sync right away instead of waiting for the next run (every 2 minutes). Once a minute.",
        "responses": {
          "200": {
            "description": "Synced",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "properties": {
                        "queued": {
                          "type": "object",
                          "properties": {
                            "sales": {
                              "type": "integer"
                            },
                            "customers": {
                              "type": "integer"
                            }
                          }
                        },
                        "flushed": {
                          "type": "object",
                          "properties": {
                            "synced": {
                              "type": "integer"
                            },
                            "failed": {
                              "type": "integer"
                            },
                            "retrying": {
                              "type": "integer"
                            },
                            "parked": {
                              "type": "boolean"
                            }
                          }
                        }
                      }
                    },
                    "connection": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Syncing needs the Pro plan or higher (feature `sheetsSync`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "sheetsSync",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "409": {
            "description": "Not connected, Google access was removed, or no spreadsheet chosen yet. `code`: `SHEETS_NOT_CONNECTED`, `SHEETS_RECONNECT` or `SHEETS_NO_SPREADSHEET`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "SHEETS_NOT_CONNECTED",
                  "message": "Connect your Google account first."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "description": "Google Sheets sync is not configured on this server (`code: SHEETS_NOT_CONFIGURED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "SHEETS_NOT_CONFIGURED",
                  "message": "Google Sheets sync is not available on this server yet."
                }
              }
            }
          }
        }
      }
    },
    "/sheets-sync/retry": {
      "post": {
        "operationId": "retrySheetsSync",
        "tags": [
          "Google Sheets sync"
        ],
        "summary": "Retry failed rows",
        "description": "Puts the rows that gave up after repeated errors back in the queue.",
        "responses": {
          "200": {
            "description": "Requeued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "requeued": {
                      "type": "integer",
                      "example": 2
                    },
                    "connection": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — Retrying needs the Pro plan or higher (feature `sheetsSync`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "sheetsSync",
                    "currentPlan": "free",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "409": {
            "description": "Not connected, Google access was removed, or no spreadsheet chosen yet. `code`: `SHEETS_NOT_CONNECTED`, `SHEETS_RECONNECT` or `SHEETS_NO_SPREADSHEET`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "SHEETS_NOT_CONNECTED",
                  "message": "Connect your Google account first."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/discounts/{id}/unique-codes": {
      "get": {
        "operationId": "listUniqueCodes",
        "summary": "List a discount's one-time codes",
        "description": "The issued / redeemed / expired counts of a unique-per-recipient discount and a page of its codes, newest first. Not plan-gated (you can always read what was issued).",
        "tags": [
          "Discounts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Discount id"
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "redeemed",
                "expired"
              ]
            },
            "description": "Only rows in this state"
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            },
            "description": "Page size (max 200)"
          },
          {
            "in": "query",
            "name": "before",
            "schema": {
              "type": "string"
            },
            "description": "Cursor: the `nextCursor` of the previous page"
          }
        ],
        "responses": {
          "200": {
            "description": "Counts and a page of codes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "discountId": {
                      "type": "string"
                    },
                    "uniquePerRecipient": {
                      "type": "boolean"
                    },
                    "expiresInHours": {
                      "type": "integer"
                    },
                    "mergeTag": {
                      "type": "string",
                      "example": "{{unique_coupon:6710b2c49e3f1a0012cd56ef}}"
                    },
                    "stats": {
                      "$ref": "#/components/schemas/UniqueCodeStats"
                    },
                    "codes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/UniqueCode"
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createUniqueCodes",
        "summary": "Issue one-time codes in a batch",
        "description": "Issue codes for another tool or a CSV: one code per email in `emails` (bound to that email), or `count` unbound single-use codes. The discount must have `uniquePerRecipient: true`. At most 5,000 per call. Growth plan and above.",
        "tags": [
          "Discounts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Discount id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "emails": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    },
                    "description": "One bound code per email (duplicates are merged)"
                  },
                  "count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 5000,
                    "description": "Unbound single-use codes to create (when `emails` is absent)"
                  },
                  "expiresInHours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 720,
                    "description": "Overrides the discount's `uniqueExpiryHours` for this batch"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The issued codes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "batchId": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "codes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string",
                            "nullable": true
                          },
                          "expiresAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this feature (Growth and above). Body: `code: FEATURE_LOCKED`, `details.feature: uniqueCoupons`, `details.requiredPlan: tier2`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/discounts/{id}/unique-codes/export": {
      "get": {
        "operationId": "exportUniqueCodes",
        "summary": "Export a discount's one-time codes (CSV)",
        "description": "Every code of the discount as CSV: `code,email,status,expires_at,redeemed_at`. Exports are never plan-gated.",
        "tags": [
          "Discounts"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Discount id"
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "redeemed",
                "expired"
              ]
            },
            "description": "Only rows in this state"
          }
        ],
        "responses": {
          "200": {
            "description": "CSV file",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/personal-deadlines": {
      "get": {
        "operationId": "listPersonalDeadlines",
        "summary": "List personal deadlines",
        "description": "Your personal deadlines in the key's mode, newest first, each with `stats` (people started / active / redeemed / expired). Not plan-gated.",
        "tags": [
          "Personal deadlines"
        ],
        "responses": {
          "200": {
            "description": "Deadlines",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PersonalDeadline"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createPersonalDeadline",
        "summary": "Create a personal deadline",
        "description": "Put a per-person offer window on one of your fixed-price products. Pro plan and above.",
        "tags": [
          "Personal deadlines"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "productId",
                  "offer"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "72-hour launch offer"
                  },
                  "productId": {
                    "type": "string",
                    "description": "Your fixed-price one-time product the offer is on"
                  },
                  "durationHours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 720,
                    "default": 72,
                    "description": "Length of each person's window in hours. Changing it only affects people who start after the change."
                  },
                  "offer": {
                    "type": "object",
                    "required": [
                      "type",
                      "value"
                    ],
                    "description": "The personal offer during the window. `percentage` / `fixed` off the product price, or a set `price` (your account currency; not applied to variants)",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "percentage",
                          "fixed",
                          "price"
                        ]
                      },
                      "value": {
                        "type": "number",
                        "minimum": 0,
                        "example": 30
                      }
                    }
                  },
                  "fallback": {
                    "type": "object",
                    "description": "What the person pays after their window ends (or once they used the offer): full price, or a smaller lasting discount",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "full_price",
                          "percentage",
                          "fixed"
                        ],
                        "default": "full_price"
                      },
                      "value": {
                        "type": "number",
                        "minimum": 0,
                        "example": 10
                      }
                    }
                  },
                  "trigger": {
                    "type": "object",
                    "required": [
                      "type"
                    ],
                    "description": "What starts a person's clock. `audience_joined`: they join your email audience. `lead_magnet`: they claim the free product `productId`. `automation_entered`: they enter the automation `automationId`. People already on your list start on the first email that carries a deadline merge tag; you can also start a clock with `POST /personal-deadlines/{id}/start`.",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "audience_joined",
                          "lead_magnet",
                          "automation_entered"
                        ]
                      },
                      "productId": {
                        "type": "string",
                        "description": "lead_magnet only"
                      },
                      "automationId": {
                        "type": "string",
                        "description": "automation_entered only"
                      }
                    }
                  },
                  "active": {
                    "type": "boolean",
                    "default": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created deadline",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PersonalDeadline"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this feature (Pro and above). Body: `code: FEATURE_LOCKED`, `details.feature: personalDeadlines`, `details.requiredPlan: tier3`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/personal-deadlines/public/offer": {
      "get": {
        "operationId": "getPersonalOffer",
        "summary": "Get a visitor's personal offer on a product",
        "description": "PUBLIC (no API key) — used by the product page and checkout. With the signed visitor token from a personal link (`pf_sid`), returns that person's window: `state` (`active`, `expired`, `redeemed` or `none`), `expiresAt`, `msLeft`, `endsIn`, `offerPrice`, `listPrice` and the fallback. Never returns the email. `none` for anonymous visitors.",
        "tags": [
          "Personal deadlines"
        ],
        "security": [],
        "parameters": [
          {
            "in": "query",
            "name": "productId",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product page being viewed"
          },
          {
            "in": "query",
            "name": "sid",
            "schema": {
              "type": "string"
            },
            "description": "The `pf_sid` visitor token"
          },
          {
            "in": "query",
            "name": "vid",
            "schema": {
              "type": "string"
            },
            "description": "The A/B test visitor id (`POST /ab-tests/public/assign`). With `abv`, in a running price test the offer is priced from that visitor's tested list price, as at checkout."
          },
          {
            "in": "query",
            "name": "abv",
            "schema": {
              "type": "string"
            },
            "description": "The A/B variant id the page rendered. The tested price applies only when it matches the visitor's assignment."
          }
        ],
        "responses": {
          "200": {
            "description": "The visitor's offer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "state": {
                      "type": "string",
                      "enum": [
                        "none",
                        "active",
                        "expired",
                        "redeemed"
                      ]
                    },
                    "deadlineId": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "msLeft": {
                      "type": "integer"
                    },
                    "endsIn": {
                      "type": "string",
                      "example": "ends in 47 hours"
                    },
                    "offer": {
                      "type": "object",
                      "required": [
                        "type",
                        "value"
                      ],
                      "description": "The personal offer during the window. `percentage` / `fixed` off the product price, or a set `price` (your account currency; not applied to variants)",
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "percentage",
                            "fixed",
                            "price"
                          ]
                        },
                        "value": {
                          "type": "number",
                          "minimum": 0,
                          "example": 30
                        }
                      }
                    },
                    "listPrice": {
                      "type": "number"
                    },
                    "offerPrice": {
                      "type": "number",
                      "nullable": true
                    },
                    "fallback": {
                      "type": "object",
                      "description": "What the person pays after their window ends (or once they used the offer): full price, or a smaller lasting discount",
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "full_price",
                            "percentage",
                            "fixed"
                          ],
                          "default": "full_price"
                        },
                        "value": {
                          "type": "number",
                          "minimum": 0,
                          "example": 10
                        }
                      }
                    },
                    "fallbackPrice": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed product id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/personal-deadlines/{id}": {
      "get": {
        "operationId": "getPersonalDeadline",
        "summary": "Get a personal deadline",
        "description": "One of your deadlines with its `stats` and merge tags",
        "tags": [
          "Personal deadlines"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Deadline id"
          }
        ],
        "responses": {
          "200": {
            "description": "The deadline",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PersonalDeadline"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "updatePersonalDeadline",
        "summary": "Update a personal deadline",
        "description": "Partial update — only the fields you send change. Clocks already running keep their end time. Pro plan and above.",
        "tags": [
          "Personal deadlines"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Deadline id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "72-hour launch offer"
                  },
                  "productId": {
                    "type": "string",
                    "description": "Your fixed-price one-time product the offer is on"
                  },
                  "durationHours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 720,
                    "default": 72,
                    "description": "Length of each person's window in hours. Changing it only affects people who start after the change."
                  },
                  "offer": {
                    "type": "object",
                    "required": [
                      "type",
                      "value"
                    ],
                    "description": "The personal offer during the window. `percentage` / `fixed` off the product price, or a set `price` (your account currency; not applied to variants)",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "percentage",
                          "fixed",
                          "price"
                        ]
                      },
                      "value": {
                        "type": "number",
                        "minimum": 0,
                        "example": 30
                      }
                    }
                  },
                  "fallback": {
                    "type": "object",
                    "description": "What the person pays after their window ends (or once they used the offer): full price, or a smaller lasting discount",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "full_price",
                          "percentage",
                          "fixed"
                        ],
                        "default": "full_price"
                      },
                      "value": {
                        "type": "number",
                        "minimum": 0,
                        "example": 10
                      }
                    }
                  },
                  "trigger": {
                    "type": "object",
                    "required": [
                      "type"
                    ],
                    "description": "What starts a person's clock. `audience_joined`: they join your email audience. `lead_magnet`: they claim the free product `productId`. `automation_entered`: they enter the automation `automationId`. People already on your list start on the first email that carries a deadline merge tag; you can also start a clock with `POST /personal-deadlines/{id}/start`.",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "audience_joined",
                          "lead_magnet",
                          "automation_entered"
                        ]
                      },
                      "productId": {
                        "type": "string",
                        "description": "lead_magnet only"
                      },
                      "automationId": {
                        "type": "string",
                        "description": "automation_entered only"
                      }
                    }
                  },
                  "active": {
                    "type": "boolean",
                    "default": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated deadline",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PersonalDeadline"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this feature (Pro and above). Body: `code: FEATURE_LOCKED`, `details.feature: personalDeadlines`, `details.requiredPlan: tier3`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deletePersonalDeadline",
        "summary": "Delete a personal deadline",
        "description": "Delete the deadline and everyone's clock on it. Not plan-gated.",
        "tags": [
          "Personal deadlines"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Deadline id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "Deadline removed"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/personal-deadlines/{id}/entries": {
      "get": {
        "operationId": "listPersonalDeadlineEntries",
        "summary": "List the people on a deadline",
        "description": "Everyone whose clock started on this deadline, newest first, with their state.",
        "tags": [
          "Personal deadlines"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Deadline id"
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "redeemed",
                "expired"
              ]
            },
            "description": "Only rows in this state"
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            },
            "description": "Page size (max 200)"
          },
          {
            "in": "query",
            "name": "before",
            "schema": {
              "type": "string"
            },
            "description": "Cursor: the `nextCursor` of the previous page"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of people",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PersonalDeadlineEntry"
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/personal-deadlines/{id}/start": {
      "post": {
        "operationId": "startPersonalDeadline",
        "summary": "Start someone's clock",
        "description": "Start the window for `email` now (your own signup form, Zapier, an import) and get their personal link. Idempotent: an existing clock is returned, never restarted. Pro plan and above.",
        "tags": [
          "Personal deadlines"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Deadline id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Their clock and personal link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PersonalDeadlineLink"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this feature (Pro and above). Body: `code: FEATURE_LOCKED`, `details.feature: personalDeadlines`, `details.requiredPlan: tier3`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/personal-deadlines/{id}/link": {
      "get": {
        "operationId": "getPersonalDeadlineLink",
        "summary": "Get someone's personal link",
        "description": "The product link that identifies `email` on your store (it does not start their clock). Pro plan and above.",
        "tags": [
          "Personal deadlines"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6710b2c49e3f1a0012cd56ef"
            },
            "description": "Deadline id"
          },
          {
            "in": "query",
            "name": "email",
            "required": true,
            "schema": {
              "type": "string",
              "format": "email"
            },
            "description": "The person"
          }
        ],
        "responses": {
          "200": {
            "description": "Their personal link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PersonalDeadlineLink"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Your plan does not include this feature (Pro and above). Body: `code: FEATURE_LOCKED`, `details.feature: personalDeadlines`, `details.requiredPlan: tier3`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/ab-tests": {
      "get": {
        "operationId": "listAbTests",
        "summary": "List A/B tests",
        "description": "Your A/B tests in the key's mode, newest first (200 max), each with a `summary` of visitors and conversions. Not plan-gated.",
        "tags": [
          "A/B tests"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "running",
                "completed",
                "stopped"
              ]
            },
            "description": "Only tests in this state"
          },
          {
            "in": "query",
            "name": "productId",
            "schema": {
              "type": "string"
            },
            "description": "Only this product's tests"
          }
        ],
        "responses": {
          "200": {
            "description": "Tests",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AbTest"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createAbTest",
        "summary": "Start an A/B test",
        "description": "Start a test on one of your paid products: `variants` are the 1–3 challengers (B, C, D); variant A is the product as it is now. Starts immediately. One running test per product. Price tests need a fixed-price product without variants. Editing the tested field of the product later stops the test. Pro plan and above.",
        "tags": [
          "A/B tests"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productId",
                  "type",
                  "variants"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Defaults to `<Type> test · <product name>`"
                  },
                  "productId": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "price",
                      "headline",
                      "cover",
                      "checkout_layout"
                    ],
                    "description": "`price`, `headline` (name + optional description), `cover` (cover image) or `checkout_layout`"
                  },
                  "variants": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 3,
                    "description": "The challengers. Set the field of the test's type on each; each must differ from A and from each other.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "label": {
                          "type": "string",
                          "maxLength": 60,
                          "description": "Your name for the variant (defaults to `Variant B`, …)",
                          "example": "Lower price"
                        },
                        "price": {
                          "type": "number",
                          "minimum": 1,
                          "maximum": 1000000,
                          "description": "`price` tests: the list price in your account currency (major units, e.g. `39`). Discounts and personal offers apply on top of it at checkout, exactly as on the product's own price.",
                          "example": 39
                        },
                        "headline": {
                          "type": "string",
                          "maxLength": 200,
                          "description": "`headline` tests: the product name shown as the headline",
                          "example": "Ship your first app this weekend"
                        },
                        "description": {
                          "type": "string",
                          "description": "`headline` tests (optional): the product description (HTML, like the product's own). Left out = the product's description."
                        },
                        "coverImage": {
                          "type": "string",
                          "format": "uri",
                          "description": "`cover` tests: an https image URL (upload one with `POST /images` first)"
                        },
                        "checkoutLayout": {
                          "type": "string",
                          "enum": [
                            "split",
                            "flipped",
                            "focus",
                            "banner",
                            "card",
                            "hero",
                            "sidebar",
                            "gallery",
                            "sheet",
                            "frame"
                          ],
                          "description": "`checkout_layout` tests: the checkout layout"
                        }
                      }
                    }
                  },
                  "weights": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "minimum": 5,
                      "maximum": 95
                    },
                    "description": "Optional traffic split, percent per variant with A first, adding up to 100. Default: equal.",
                    "example": [
                      50,
                      50
                    ]
                  }
                }
              },
              "example": {
                "productId": "665f1c2a9b3e4a0012ab34cd",
                "type": "price",
                "variants": [
                  {
                    "label": "Lower price",
                    "price": 39
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The started test",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AbTest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — starting an A/B test needs the Pro plan or higher (feature `abTests`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "abTests",
                    "currentPlan": "tier2",
                    "requiredPlan": "tier3",
                    "requiredPlanName": "Pro",
                    "requiredPlanPrice": 119,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "409": {
            "description": "The product already has a running test (`code: AB_TEST_RUNNING`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "AB_TEST_RUNNING",
                  "message": "This product already has a test running. Stop it or declare a winner first."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/ab-tests/public/assign": {
      "post": {
        "operationId": "assignAbTestVariant",
        "summary": "Get a visitor's variant of a product",
        "description": "PUBLIC (no API key) — called by the product page and checkout. With the visitor's own random id (16–64 of `A-Z a-z 0-9 _ -`, kept in a first-party cookie), returns that visitor's variant of the product's running test and logs the visit once. The same id always gets the same variant. `test: null` when nothing is running, for bots, or when the store's plan no longer includes A/B tests. Never lists the other variants.",
        "tags": [
          "A/B tests"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productId",
                  "visitorId"
                ],
                "properties": {
                  "productId": {
                    "type": "string"
                  },
                  "visitorId": {
                    "type": "string",
                    "example": "3f9c2a7be14d4c0a9e6b5d21c8f0a7e4"
                  },
                  "currency": {
                    "type": "string",
                    "example": "EUR",
                    "description": "Optional: the currency the page shows. A tested price then also comes back as `displayPrice`, converted exactly as the checkout will charge it."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The visitor's variant",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "test": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "testId": {
                          "type": "string"
                        },
                        "variantId": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "price",
                            "headline",
                            "cover",
                            "checkout_layout"
                          ]
                        },
                        "basePrice": {
                          "type": "number",
                          "nullable": true,
                          "description": "The product's own list price"
                        },
                        "overrides": {
                          "type": "object",
                          "description": "What to show instead of the product's own value (empty for variant A)",
                          "properties": {
                            "price": {
                              "type": "number"
                            },
                            "headline": {
                              "type": "string"
                            },
                            "description": {
                              "type": "string"
                            },
                            "coverImage": {
                              "type": "string"
                            },
                            "checkoutLayout": {
                              "type": "string"
                            }
                          }
                        },
                        "displayPrice": {
                          "type": "object",
                          "description": "Price tests with `currency`: the tested price in the charge currency",
                          "properties": {
                            "currency": {
                              "type": "string",
                              "example": "EUR"
                            },
                            "amount": {
                              "type": "number",
                              "example": 35.99
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed product id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Product not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/ab-tests/{id}": {
      "get": {
        "operationId": "getAbTest",
        "summary": "Get an A/B test with its results",
        "description": "The test and its live `results`: per variant visitors, paid conversions, conversion rate, revenue per visitor, the z-test p-value and the Bayesian chance to beat A, plus a daily series and a recommendation. Not plan-gated.",
        "tags": [
          "A/B tests"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6712c4d59e3f1a0012cd9911"
            },
            "description": "A/B test id"
          },
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "example": -120
            },
            "description": "Your browser's `getTimezoneOffset()` in minutes (UTC − local), so the daily chart groups by your local day. Default UTC."
          }
        ],
        "responses": {
          "200": {
            "description": "The test",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AbTest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteAbTest",
        "summary": "Delete an ended A/B test",
        "description": "Removes an ended test and its visitor log. A running test answers 409 — stop it or declare a winner first. Sales keep their order history.",
        "tags": [
          "A/B tests"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6712c4d59e3f1a0012cd9911"
            },
            "description": "A/B test id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The test is still running (`code: AB_TEST_RUNNING`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "AB_TEST_RUNNING",
                  "message": "Stop the test or declare a winner before deleting it."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/ab-tests/{id}/results": {
      "get": {
        "operationId": "getAbTestResults",
        "summary": "Get an A/B test's results",
        "description": "The same `results` as `GET /ab-tests/{id}`, alone. Not plan-gated.",
        "tags": [
          "A/B tests"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6712c4d59e3f1a0012cd9911"
            },
            "description": "A/B test id"
          },
          {
            "in": "query",
            "name": "timezoneOffsetMinutes",
            "schema": {
              "type": "integer",
              "example": -120
            },
            "description": "Your browser's `getTimezoneOffset()` in minutes (UTC − local), so the daily chart groups by your local day. Default UTC."
          }
        ],
        "responses": {
          "200": {
            "description": "Results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AbTestResults"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/ab-tests/{id}/stop": {
      "post": {
        "operationId": "stopAbTest",
        "summary": "Stop an A/B test",
        "description": "Ends the test without a winner: every visitor sees the product as it is (variant A) again. Fires `ab_test.completed`. Never plan-gated.",
        "tags": [
          "A/B tests"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6712c4d59e3f1a0012cd9911"
            },
            "description": "A/B test id"
          }
        ],
        "responses": {
          "200": {
            "description": "The stopped test",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AbTest"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The test already ended (`code: AB_TEST_NOT_RUNNING`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "AB_TEST_NOT_RUNNING",
                  "message": "This test has already ended."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/ab-tests/{id}/winner": {
      "post": {
        "operationId": "declareAbTestWinner",
        "summary": "Declare an A/B test's winner",
        "description": "Ends the test and applies the winning variant to the product permanently (its price, name + description, cover image or checkout layout). Declaring A changes nothing. Fires `product.updated` (when the product changed) and `ab_test.completed`. Never plan-gated.",
        "tags": [
          "A/B tests"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6712c4d59e3f1a0012cd9911"
            },
            "description": "A/B test id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "variantId"
                ],
                "properties": {
                  "variantId": {
                    "type": "string",
                    "description": "The `_id` of one of the test's variants"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The completed test, with the product fields it changed",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/AbTest"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "applied": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "example": [
                            "price"
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The test already ended (`code: AB_TEST_NOT_RUNNING`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "AB_TEST_NOT_RUNNING",
                  "message": "This test has already ended."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/platforms": {
      "get": {
        "operationId": "listMigrationPlatforms",
        "tags": [
          "Migrations"
        ],
        "summary": "Platforms you can import from",
        "description": "Every source with the CSV exports we recognise (per kind, the column names that map automatically), where to find each export, and which kinds the API import (`connect`) brings over.",
        "responses": {
          "200": {
            "description": "The platforms",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "platforms": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "source": {
                            "type": "string",
                            "enum": [
                              "gumroad",
                              "lemonsqueezy",
                              "stan",
                              "csv"
                            ],
                            "description": "Where you are moving from: `gumroad`, `lemonsqueezy`, `stan` (Stan Store) or `csv` (any other platform). It decides which export columns are recognised and whether `connect` is available (Gumroad, Lemon Squeezy).",
                            "example": "gumroad"
                          },
                          "name": {
                            "type": "string",
                            "example": "Gumroad"
                          },
                          "csvKinds": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "products",
                                "customers",
                                "orders",
                                "subscribers"
                              ],
                              "description": "`products` (catalog), `customers`, `orders` (purchase history — saved as imported records, never as sales) or `subscribers` (email list).",
                              "example": "orders"
                            }
                          },
                          "apiKinds": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "products",
                                "customers",
                                "orders",
                                "subscribers"
                              ],
                              "description": "`products` (catalog), `customers`, `orders` (purchase history — saved as imported records, never as sales) or `subscribers` (email list).",
                              "example": "orders"
                            }
                          },
                          "exportHints": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "string"
                            }
                          },
                          "tokenHint": {
                            "type": "string",
                            "nullable": true
                          },
                          "columns": {
                            "type": "object",
                            "description": "Per kind: field, label, required, headers we recognise"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations": {
      "get": {
        "operationId": "listMigrations",
        "tags": [
          "Migrations"
        ],
        "summary": "List your imports",
        "description": "Your imports, newest first, including those our team ran for you (`createdBy.type: admin`).",
        "parameters": [
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of imports",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MigrationJob"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createMigration",
        "tags": [
          "Migrations"
        ],
        "summary": "Create an import",
        "description": "Starts a draft import from `source`. You can attach the CSV exports right away with `files` (one per kind, up to 10 MB / 50,000 rows each); columns are mapped automatically from the platform's export format. Nothing is written to your store until you `start` it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "enum": [
                      "gumroad",
                      "lemonsqueezy",
                      "stan",
                      "csv"
                    ],
                    "description": "Where you are moving from: `gumroad`, `lemonsqueezy`, `stan` (Stan Store) or `csv` (any other platform). It decides which export columns are recognised and whether `connect` is available (Gumroad, Lemon Squeezy).",
                    "example": "gumroad"
                  },
                  "downloadFiles": {
                    "type": "boolean",
                    "default": true,
                    "description": "Download product files from the export's links into your storage (size and type checked). Off: products are marked \"file needed\"."
                  },
                  "files": {
                    "type": "array",
                    "maxItems": 4,
                    "items": {
                      "type": "object",
                      "required": [
                        "kind",
                        "csv"
                      ],
                      "properties": {
                        "kind": {
                          "type": "string",
                          "enum": [
                            "products",
                            "customers",
                            "orders",
                            "subscribers"
                          ],
                          "description": "`products` (catalog), `customers`, `orders` (purchase history — saved as imported records, never as sales) or `subscribers` (email list).",
                          "example": "orders"
                        },
                        "filename": {
                          "type": "string",
                          "example": "sales.csv"
                        },
                        "csv": {
                          "type": "string",
                          "description": "The CSV file's text"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The draft import",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MigrationJob"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — importing needs the Volume plan or higher (feature `migration`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this. Reading an import, its report and cancelling are never locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "migration",
                    "currentPlan": "tier2",
                    "requiredPlan": "volume",
                    "requiredPlanName": "Volume",
                    "requiredPlanPrice": 500,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}": {
      "get": {
        "operationId": "getMigration",
        "tags": [
          "Migrations"
        ],
        "summary": "Get an import (status and progress)",
        "description": "The import with its files, mapping, latest dry run and, while it runs, per-kind progress. Poll it after `start`.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          }
        ],
        "responses": {
          "200": {
            "description": "The import",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MigrationJob"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}/files": {
      "post": {
        "operationId": "addMigrationFile",
        "tags": [
          "Migrations"
        ],
        "summary": "Add a CSV export",
        "description": "Uploads one platform export (replacing an earlier file of the same kind). Every row is validated; read them with `GET /migrations/{id}/rows`. Draft imports only (409 `MIGRATION_NOT_DRAFT` after start).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "kind",
                  "csv"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "products",
                      "customers",
                      "orders",
                      "subscribers"
                    ],
                    "description": "`products` (catalog), `customers`, `orders` (purchase history — saved as imported records, never as sales) or `subscribers` (email list).",
                    "example": "orders"
                  },
                  "filename": {
                    "type": "string",
                    "example": "gumroad-sales.csv"
                  },
                  "csv": {
                    "type": "string",
                    "description": "The CSV file's text (up to 10 MB)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The file and the import",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "file": {
                      "$ref": "#/components/schemas/MigrationFile"
                    },
                    "job": {
                      "$ref": "#/components/schemas/MigrationJob"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — importing needs the Volume plan or higher (feature `migration`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this. Reading an import, its report and cancelling are never locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "migration",
                    "currentPlan": "tier2",
                    "requiredPlan": "volume",
                    "requiredPlanName": "Volume",
                    "requiredPlanPrice": 500,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The import has started; its files can no longer change (`code: MIGRATION_NOT_DRAFT`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The file is larger than 10 MB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}/files/{kind}/mapping": {
      "put": {
        "operationId": "updateMigrationMapping",
        "tags": [
          "Migrations"
        ],
        "summary": "Fix the column mapping",
        "description": "Sets which CSV column feeds each canonical field and re-validates every row. Fields left out are unmapped. Required fields must be mapped (400 `INVALID_MAPPING`).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          },
          {
            "in": "path",
            "name": "kind",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "products",
                "customers",
                "orders",
                "subscribers"
              ]
            },
            "description": "Which data the file holds"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "mapping"
                ],
                "properties": {
                  "mapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string",
                      "nullable": true
                    },
                    "example": {
                      "email": "Purchase Email",
                      "productName": "Item Name",
                      "amount": "Sale Price ($)"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The re-validated file",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "file": {
                      "$ref": "#/components/schemas/MigrationFile"
                    },
                    "job": {
                      "$ref": "#/components/schemas/MigrationJob"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — importing needs the Volume plan or higher (feature `migration`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this. Reading an import, its report and cancelling are never locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "migration",
                    "currentPlan": "tier2",
                    "requiredPlan": "volume",
                    "requiredPlanName": "Volume",
                    "requiredPlanPrice": 500,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}/files/{kind}": {
      "delete": {
        "operationId": "removeMigrationFile",
        "tags": [
          "Migrations"
        ],
        "summary": "Remove a file",
        "description": "Drops a kind's file and its rows from a draft import.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          },
          {
            "in": "path",
            "name": "kind",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "products",
                "customers",
                "orders",
                "subscribers"
              ]
            },
            "description": "Which data the file holds"
          }
        ],
        "responses": {
          "200": {
            "description": "The import",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MigrationJob"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — importing needs the Volume plan or higher (feature `migration`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this. Reading an import, its report and cancelling are never locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "migration",
                    "currentPlan": "tier2",
                    "requiredPlan": "volume",
                    "requiredPlanName": "Volume",
                    "requiredPlanPrice": 500,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}/rows": {
      "get": {
        "operationId": "listMigrationRows",
        "tags": [
          "Migrations"
        ],
        "summary": "Preview rows and their errors",
        "description": "The validated rows of one kind, in file order, with each row's errors and warnings — and after the run, what happened to it.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          },
          {
            "in": "query",
            "name": "kind",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "products",
                "customers",
                "orders",
                "subscribers"
              ]
            }
          },
          {
            "in": "query",
            "name": "filter",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "problems",
                "valid"
              ],
              "default": "all"
            },
            "description": "`problems` = rows with errors (or that failed)"
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "kind": {
                      "type": "string",
                      "enum": [
                        "products",
                        "customers",
                        "orders",
                        "subscribers"
                      ],
                      "description": "`products` (catalog), `customers`, `orders` (purchase history — saved as imported records, never as sales) or `subscribers` (email list).",
                      "example": "orders"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MigrationRow"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}/connect": {
      "post": {
        "operationId": "connectMigrationAccount",
        "tags": [
          "Migrations"
        ],
        "summary": "Pull from Gumroad or Lemon Squeezy with your token",
        "description": "Reads products, sales / orders, customers and (Lemon Squeezy) marketing opt-ins with a personal token you create on the platform. The token is used for this one pull and is never stored or logged. Gumroad: Settings → Advanced → Applications → access token. Lemon Squeezy: Settings → API.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "writeOnly": true,
                    "description": "Your platform API token (never stored)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "What the pull brought over",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "files": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MigrationFile"
                      }
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "job": {
                      "$ref": "#/components/schemas/MigrationJob"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — importing needs the Volume plan or higher (feature `migration`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this. Reading an import, its report and cancelling are never locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "migration",
                    "currentPlan": "tier2",
                    "requiredPlan": "volume",
                    "requiredPlanName": "Volume",
                    "requiredPlanPrice": 500,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}/dry-run": {
      "post": {
        "operationId": "dryRunMigration",
        "tags": [
          "Migrations"
        ],
        "summary": "Dry run",
        "description": "Counts what the run would create, skip (already there) and leave out (invalid, no consent, on the suppression list, over the subscriber limit), against your store as it is now. Writes nothing.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          }
        ],
        "responses": {
          "200": {
            "description": "The dry run",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MigrationDryRun"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — importing needs the Volume plan or higher (feature `migration`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this. Reading an import, its report and cancelling are never locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "migration",
                    "currentPlan": "tier2",
                    "requiredPlan": "volume",
                    "requiredPlanName": "Volume",
                    "requiredPlanPrice": 500,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}/start": {
      "post": {
        "operationId": "startMigration",
        "tags": [
          "Migrations"
        ],
        "summary": "Start the import",
        "description": "Queues the background run (202). It imports products, then customers, then purchase history, then subscribers, and resumes by itself after an interruption. Starting a `failed` or `canceled` import again picks up the rows not imported yet.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          }
        ],
        "responses": {
          "202": {
            "description": "The queued import",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MigrationJob"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "`FEATURE_LOCKED` — importing needs the Volume plan or higher (feature `migration`). Nothing was saved. The creator upgrades at https://app.pocketsflow.com/profile/billing; check `GET /users/entitlements` first to avoid this. Reading an import, its report and cancelling are never locked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLocked"
                },
                "example": {
                  "error": "FEATURE_LOCKED",
                  "code": "FEATURE_LOCKED",
                  "upgradeRequired": true,
                  "message": "This feature is part of a higher plan. Please upgrade your plan to use it.",
                  "details": {
                    "feature": "migration",
                    "currentPlan": "tier2",
                    "requiredPlan": "volume",
                    "requiredPlanName": "Volume",
                    "requiredPlanPrice": 500,
                    "upgradeRequired": true
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The import is already running or finished (`code: MIGRATION_ALREADY_STARTED`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}/cancel": {
      "post": {
        "operationId": "cancelMigration",
        "tags": [
          "Migrations"
        ],
        "summary": "Cancel an import",
        "description": "Stops a draft or running import after the current batch. What was already imported stays. Never plan-locked.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          }
        ],
        "responses": {
          "200": {
            "description": "The import",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MigrationJob"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The import has already finished",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}/report": {
      "get": {
        "operationId": "getMigrationReport",
        "tags": [
          "Migrations"
        ],
        "summary": "Import report",
        "description": "Per kind: imported, skipped, invalid, failed and pending rows; the products created, files downloaded, and the products that still need their file.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          }
        ],
        "responses": {
          "200": {
            "description": "The report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job": {
                      "$ref": "#/components/schemas/MigrationJob"
                    },
                    "kinds": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "integer"
                        }
                      }
                    },
                    "products": {
                      "type": "object",
                      "properties": {
                        "created": {
                          "type": "integer"
                        },
                        "filesDownloaded": {
                          "type": "integer"
                        },
                        "filesNeeded": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "row": {
                                "type": "integer"
                              },
                              "productId": {
                                "type": "string",
                                "nullable": true
                              },
                              "name": {
                                "type": "string"
                              },
                              "reason": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        }
                      }
                    },
                    "problems": {
                      "type": "integer",
                      "description": "Rows not imported because of an error"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/migrations/{id}/errors.csv": {
      "get": {
        "operationId": "downloadMigrationErrors",
        "tags": [
          "Migrations"
        ],
        "summary": "Download row problems (CSV)",
        "description": "Every row that was invalid, skipped, failed or imported with a warning: type, row number, status, the problem and the source row. Spreadsheet formulas in source cells are neutralised.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "example": "6711c0d59e3f1a0012cd9911"
            },
            "description": "Import id"
          }
        ],
        "responses": {
          "200": {
            "description": "A CSV file",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    }
  }
}