Endpoints

All endpoints are read-only (GET) and live under /api/v1.

Tenant

`GET /me`

Returns the authenticated creator's basic info and current plan.

Leads

`GET /leads`

List leads (users captured via your storefront/forms) ordered by created_at desc. By default, leads that have converted into customers or are archived are excluded.

Query params:

  • limit (default 50, max 200)
  • cursor — opaque, from previous response meta.next_cursor
  • created_after, created_before — ISO 8601 timestamps
  • include_converted — set true to include leads with converted_at != null (former leads who became paying customers). Useful when you sync the full history into a CRM.
  • include_archived — set true to include contacts the creator soft-archived from their dashboard. Use this when syncing the full history to a CRM that needs to delete or unsubscribe the matching record on its side.

Each lead includes:

  • id, source — acquisition surface (e.g. storefront_login, blog_lead_capture, free_signup)
  • created_at, converted_at (null when still a lead), archived_at (null when active)
  • userid, email, name, first_name, last_name, phone. All except id and email can be null when the contact was captured through a flow that didn't collect them. CRMs that expect first/last separately (HubSpot, Mailchimp, ConvertKit) can map directly.
  • marketing_consent — see Marketing consent object below.

`GET /leads/:id`

Retrieve a single lead, including converted_at, archived_at, and marketing_consent. Returns the row even after conversion or archive (so CRM dereferences keep working).

Customers

`GET /customers`

List users with at least one purchase from your store. Archived customers are excluded by default; pass ?include_archived=true to include them.

Each customer includes id, email, name, first_name, last_name, phone, created_at, and the same marketing_consent object as leads. Name fields may be null when not collected at checkout.

`GET /customers/:id`

Retrieve a single customer. Includes:

  • acquisitionsource, acquired_at, converted_at if the customer started as a lead (null otherwise).
  • marketing_consent — see below.

Marketing consent object

Returned on every lead and customer payload. Always present (defaults to { status: "none", ...all-null } for contacts who never interacted with consent).

{
  "marketing_consent": {
    "status": "granted" | "withdrawn" | "none",
    "granted_at": "2026-06-02T10:00:00.000Z" | null,
    "withdrawn_at": "2026-06-15T14:30:00.000Z" | null,
    "source": "auth_modal_storefront" | "...",
    "policy_version": "2026-06-01" | null
  }
}

Status semantics:

  • grantedgranted_at set, withdrawn_at null. Only contacts in this state can receive marketing emails.
  • withdrawnwithdrawn_at set. Do not send marketing communications, even if re-syncing later overwrites your CRM-side flag.
  • none — no consent record. Treat the same as withdrawn for marketing purposes.

Source values (which surface captured the latest action):

SourceWhere the consent was given
auth_modal_storefrontSign-in modal on the storefront landing page
auth_modal_blog_leadSign-in modal opened from a blog-post lead-capture button
auth_modal_lessonSign-in modal on a course lesson page
auth_modal_productSign-in modal on a product page
auth_modal_appSign-in modal on a course/blog app presentation page
auth_modal_checkoutSign-in modal on the checkout init page
blog_lead_sectionInline Subscribe widget rendered inside a blog post (no modal hop — signed-in user)
storefront_lead_sectionInline Subscribe widget rendered as a storefront LEAD_CAPTURE item (signed-in user)
free_signup_checkoutFree-product checkout form
paid_checkoutStripe-hosted paid checkout (recorded by the tenant webhook after checkout.session.completed)
account_settingsThe contact toggled the subscription themselves on /client/account
creator_initiatedThe creator (or team admin) unsubscribed the contact on their behalf — typically after an external request (GDPR Art. 21). Distinguishes proactive opt-outs from user-initiated ones for audit purposes.

`policy_version` — version label of the consent wording shown to the contact at the moment they granted consent. Bumped when the wording changes materially. Stored so you can prove which legal text the contact agreed to.

Products

`GET /products`

List your products with active pricing options.

`GET /products/:id`

Retrieve a single product.

Orders

`GET /orders`

List purchases (one-time + recurring) across all your products.

Query params include the standard pagination/date filters plus:

  • statusACTIVE, CANCELLED, EXPIRED, REFUNDED, FREE_TRIAL, PENDING_CHECKOUT
  • product_id — filter to a specific product

`GET /orders/:id`

Retrieve a single order.

Subscriptions

`GET /subscriptions`

Like /orders but filtered to recurring purchases only. Same query params.

Analytics

`GET /analytics/summary`

Returns aggregated metrics for a period.

Query params:

  • period_start, period_end — ISO 8601 (default: last 30 days)

Response includes:

  • orders_count, paying_orders_count
  • leads_count
  • unique_customers
  • conversion_rate (unique customers / leads, or null)
  • revenue_by_currency — array with currency, gross_revenue_cents, aov_cents per currency

Events

`GET /events`

Unified feed of recent events across your store. Use this to drive webhook-style integrations via polling.

Use the cursor from the previous response and poll every 5–15 minutes. Do not poll more often than once every 5 minutes unless the creator's plan explicitly allows it.

Event types:

  • lead.createddata: lead_id, customer_id, source
  • lead.converted — emitted when a lead becomes a paying customer. occurred_at is the conversion timestamp. data: lead_id, customer_id, source
  • order.completed
  • subscription.cancelled
  • access.granted

Each event has id, type, occurred_at, and a data object specific to the type.