Open the interactive API reference
Browse every endpoint, see request/response schemas, and try calls live at
api.pocketsflow.com/docs.
Browse every public endpoint
See all public paths, methods, parameters, request bodies, and response codes
generated from the OpenAPI contract.
https://api.pocketsflow.com/docs.json —
import it into Postman/Insomnia or generate a typed client from it.
Base URL
Every request is made over HTTPS to:GET /orders). See Versioning for how the API changes.
Authentication
Every endpoint requires authentication via theAuthorization header using the
Bearer scheme, except the few marked public below (they are used by the
hosted checkout). Two credential types are accepted:
Create an API key in the dashboard under Developers → API keys, then send it
on every request:
All data is automatically scoped to the account that owns the key — you never
pass a user id. The key 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 and searches 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.
Keys are stored hashed, can be given an expiry, and can be archived/revoked at
any time; a revoked or expired key returns 401. Accounts in a country the API
does not serve receive 403 with "code": "COUNTRY_RESTRICTED".
Authentication errors
Missing or malformed credentials return401 with a JSON body:
Authorization header, a header that doesn’t start with
Bearer , an empty token, and an inactive/archived/expired key.
Making requests
- Send JSON bodies with
Content-Type: application/json.POST /imagestakesmultipart/form-data, and the product and Link in Bio write endpoints also acceptmultipart/form-datawhen you upload files. - Responses are JSON. IDs are Mongo ObjectId strings (24 hex characters) unless
noted (payment-processor ids are
plan_…,pay_…,mem_…). - Timestamps are ISO 8601 UTC strings (
2026-01-24T05:35:38.103Z). - Money in API requests and responses is a decimal amount in major currency
units (e.g.
12.1= $12.10) — product prices, orders, payments, subscribers, discounts, upsells. Only fields whose name ends inCentsare integer cents. Webhook payloads have their own rules — see Webhook events.
Pagination
List endpoints paginate in one of the following ways.
Keep requesting
page + 1 while hasMore is true. The page-based
pagination object looks like:
Errors
Errors use standard HTTP status codes and a JSON body. Most endpoints return amessage:
error and message, and some errors add
a machine-readable code (for example COUNTRY_RESTRICTED, ACCOUNT_BLOCKED,
ACCOUNT_DELETED, SEND_ALLOWANCE_EXCEEDED):
message first, fall back to error, and branch on code when it is
present. A few older endpoints answer validation errors with a plain-text body.
Rate limits
Requests authenticated with a valid API key get 600 requests per minute per account. Unauthenticated traffic (public endpoints, or a request whose key is rejected) shares 200 requests per minute per IP address, so always send your key. Responses carryRateLimit-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 a JSON body:
5xx, and cache where you can. Some endpoints have their own caps —
for example POST /newsletters/send accepts at
most 100 recipients per request.
Versioning
The 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 and never depend on field order. Breaking changes are announced in the changelog before they ship.Endpoint catalog
Everything below is available with an API key (a few endpoints are public and say so). Full request/response schemas live in the interactive reference and the endpoint reference; the most important response shapes are shown here.Products
POST /products accepts (among others) name, price, compareAtPrice,
description, subtitle, published (new products are always created published; send false on update to unpublish), slug,
thumbnail, images[], hasProductPage ("yes" or "no"), payWant,
minPrice, maxPrice, showSales, showReviews, refundPolicy,
hasFirstName, hasLastName, and productType.
- Prices are decimal amounts in your account currency (
currencyonGET /users/me), not cents.compareAtPriceis the crossed-out “was” price — display only, it never changes what the buyer pays. refundPolicyis the id of one of your refund policies (fromPOST /refunds), not the policy text.- Pay-what-you-want products need a product page: a request that sets
payWant: truemust also sendhasProductPage: "yes", otherwise it fails with400. - Subscription (recurring) offers are a separate resource: fetch them with
GET /subscriptions/{id}and delete them withDELETE /subscriptions/{id}. Updating a subscription offer is not available with an API key yet.
POST /products/copy/{id} duplicates one of your catalog items. 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, so nothing can be charged before you review it. A copied one-time
product fires the product.created webhook against the copy’s own mode.
Checkout
productId (a product or subscription offer id). Optional:
successUrl and cancelUrl (absolute http(s) URLs), customerEmail,
lockEmail, clientReferenceId (max 256 characters), discountCode, and
metadata (an object, echoed back on the resulting webhooks). Invalid values
return 400; a productId that is not yours returns 404. Your account needs
a store subdomain first — the checkout link lives on it.
Orders
Payments
The unified payment ledger — one-time product purchases and subscription charges (initial + every renewal) in a single resource. Every payment is aSale.
A single Payment object:
boolean
true for subscription payments (initial + renewals); false for one-time
product purchases.string
Present only on subscription payments:
initial for the first charge that
activates the membership, renewal for every recurring charge after it.string
Set on one-time purchases; empty on subscription payments (use
subscriptionId instead).string
The payments partner’s receipt, plan, and membership ids backing the sale.
membershipId is present on subscription payments only and matches the
subscriber’s membershipId.Some responses also carry older duplicate keys for these ids and for the
paymentsPartner, paymentsPartnerLive and membership blocks below, with
the same values. They are deprecated and will only be removed after notice —
read the names documented here.GET /payments returns { payments: [Payment], pagination }. GET /payments/{id}?live=true returns a PaymentDetail:
net = amountBeforeTax − affiliateCut − processingFee; fee is the processor fee.
paymentsPartnerLive is null if the live lookup failed — the stored paymentsPartner block still
applies, so ?live=true never fails the request.
The total transaction cost on a sale is itemized — currently 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 order detail is authoritative: it uses the settled payment and returns
the platform fee, processing fee, total transaction cost, effective fee rate,
and net amount. See Pricing & fees.
Subscriptions and subscribers
Two distinct concepts: a subscription offer is the recurring product you sell; a subscriber is a buyer’s membership in one of those offers.Each subscriber carries a
portalUrl — the hosted self-service portal where
the buyer manages, cancels, or resumes their subscription. The same link is
delivered as portalUrl in the customer.subscription.created / .updated
webhooks. Send it to that buyer exactly as returned: its ?token=… is
what allows cancel/resume — a portal link built by hand from the two ids
opens read-only.Subscription offer creation and
/subscriptions/{id} accept both an API key
and an Auth0 JWT. In SDK v1.2.0 and later, Node.js and TypeScript users can
call pocketsflow.subscriptionOffers.create(). See the SDK
guide for installation and usage; use the REST request
above if your installed SDK predates v1.2.0.status
(incomplete, incomplete_expired, trialing, active, past_due,
canceled, unpaid, succeeded, refunded, paused) and the joined offer:
GET /subscriptions/subscribers/{id}?live=true:
Discounts
valueType is percentage (default, value at most 100) or fixed (a
decimal amount off in your account currency); value must be greater than 0.
Optional active and expiration (an ISO 8601 date after which checkout
rejects the code; null removes it). mainProductIds must all be your own
products or subscription offers in the key’s mode, otherwise 400.
Upsells
Optional:
name, offer, compareAtPrice, upsellDescription,
primaryButtonText, secondaryButtonText, active, and the colour/font
overrides. upsellProductId must be one of your products and
mainProductIds your own products in the key’s mode. On update,
upsellProductId, upsellPrice, and mainProductIds change only when sent;
the other fields are written as sent. GET /upsells returns
{ upsells, salesWithUpsells }.
Refunds
These endpoints manage refund policies — the refund terms shown to buyers at
checkout, which products reference by id (
refundPolicy). They 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 as
isRefunded: true on the payment.
Reviews
Webhooks
Deliveries are signed (
X-Pocketsflow-Signature-V2: t=<unix>,v1=<hex HMAC-SHA256>
plus the legacy X-Pocketsflow-Signature), carry the event id in
X-Pocketsflow-Event-Id and the body’s id, and are retried up to 3 attempts
on 5xx, 429, and connections that never reached your endpoint. See Webhook events
for the full event list, payloads, and headers, and
Authentication & security for
signature verification.
Newsletters
A post has a
name (internal, required), subject, preview
(inbox preview text), contentHtml (the email body; contentJson is the
optional editor document), and an audience: sendToAll, subscriberIds,
productIds (everyone who bought those products), and/or tags. The sender
(fromName, replyToEmail) defaults to your newsletter settings, which must be
configured first. status is draft (default), scheduled, sending,
sent, failed, or automation; you can set draft, scheduled (with
scheduledAt and optional scheduledTimezone), or automation. Any status
other than draft/automation needs subject, preview, fromName, and
contentHtml.
POST /newsletters/posts/{id}/send, which answers
{ "message": "Post queued for sending", "post": { … } }. Sending requires a
verified sender email (400 otherwise); exceeding your monthly send allowance
returns 402 (code: "SEND_ALLOWANCE_EXCEEDED"), and a restricted account or
an over-limit subscriber count returns 403.
Link in Bio (creator pages)
Page fields:
creatorName, creatorBio, username (3–30 letters, numbers,
- or _; it is also your store subdomain), socialLinks and regularLinks
(arrays of { name, url, customName? }, at most 20 each — the whole array is
replaced on update), profilePicture, coverImage, images, template,
themeV2, gaMeasurementId, selectedProductIds / selectedSubscriptionIds
(which of your products the page shows), subscriberForm, bookingEnabled,
showFirstName, and showLastName. There are no per-link endpoints: to add,
edit, or remove a link, send the full regularLinks array with
PUT /creator-pages.
Images
Customers
Account
subdomain to build product/checkout URLs
(https://{subdomain}.pocketsflow.com/...). testMode reflects the key you
called with (true for a pk_test_… key). canSell is false while selling
is paused on the account. Other fields may appear in the response; they are
dashboard-internal and can change without notice. A suspended account answers
403 (code: "ACCOUNT_BLOCKED") and a deleted one 410
(code: "ACCOUNT_DELETED").
Partners
Connect an AI agent
Prefer to drive Pocketsflow from an AI assistant? The MCP server exposes this entire API as Model Context Protocol tools — addhttps://api.pocketsflow.com/mcp to Claude,
ChatGPT, Cursor, or your own agent, sign in, and click Allow access, and it
can manage your account in natural language. Connecting over MCP needs no API
key; the REST API and SDK keep using the API keys described above.