Skip to main content
These endpoints are live in production today, served by the FloKit payments gateway. They are separate from the v1 REST API, which is still in design-partner preview.
The paywall API submits store receipts for verification and logs funnel events for paywall analytics. Paywalls themselves are Flow-owned and served with the Flow page — there is no remote paywall configuration endpoint. Base URL: https://payments-gateway.flokitai.com

Authentication

These endpoints use request-scoped credentials instead of server API keys:
  • x-app-key header — your app’s publishable key (pk_...), created per app in the FloKit dashboard. It scopes the request to your app and is safe to embed in your app build (it is not a secret and does not authenticate the user). Required on entitlement reads and funnel event writes.
  • x-app-token header — an optional short-lived app-session token minted from the publishable key via POST /api/paywall/token. Anywhere x-app-key is accepted, x-app-token works instead — send one or the other.
  • x-user-id header — the user’s identity. userId or anonymousId query parameters are accepted as a fallback where custom headers are inconvenient (the header takes precedence).
Tenant resolution happens server-side from the app key and user identity — clients never send a company or tenant ID.
x-app-key enforcement is on in production. Requests without a valid app credential receive 401 on every endpoint marked as requiring it — there is no grace period. @flokitai/subscriptions-sdk v2.0+ sends it automatically — set appKey in initSubscriptionsSdk() and every call carries x-app-key (or x-app-token) for you.

GET /api/paywall/config

Removed. GET /api/paywall/config returns 410 with {"code": "PAYWALL_CONFIG_REMOVED"}. Remote paywall configuration no longer exists — paywalls are Flow-owned and served with the Flow page. The gateway still proxies the path so older SDK builds receive an honest 410 rather than a 404. Because the path is proxied, two non-410 outcomes are possible and are transport failures rather than statements about the paywall: 503 with {"error": "Flows Gateway is not configured."} when the gateway has no Flows upstream configured, and 502 when that upstream call fails. Treat both as retryable; only 410 is the durable answer.

POST /api/iap/receipts

Renamed from POST /api/paywall/receipt. The body schema is unchanged.
Submits an in-app purchase receipt for server-side verification. Send the Apple StoreKit 2 signed transaction (JWS) or the Google Play package name + purchase token; the gateway forwards it to the verification service and returns the verification result.

Headers

Receipt verification is protected by the store signature on the receipt itself, so x-app-key is not enforced here — but send it anyway for uniformity.

Request body

string
required
apple or google.
string
Apple StoreKit 2 signed transaction (JWS). Required for apple.
string
Android package name. Required for google.
string
Google Play purchase token. Required for google.

Example

Response

200 returns the same canonical entitlement shape as GET /api/entitlements/current:
When the receipt verified but no canonical entitlement has been projected yet, source is provider_verified and the response carries active, provider, state, expires_at, and source only.

Errors


POST /api/paywall/events

Logs paywall funnel events — impressions, offer taps, purchases, conversions, cancellations — for paywall analytics. Accepts 1–50 events per request. The gateway resolves the request’s country at the edge; a server-resolved country takes precedence over any client-supplied country value.

Headers

Every event’s app_id must belong to the app the key was issued for — mismatches are rejected with 403.

Request body

array
required
1–50 event objects.
Each event:
string
required
A name from the canonical event vocabulary. Unknown names are rejected with a non-retriable 400 that discards the whole batch, so validate before sending (the SDK does this on-device).
string
required
Your application identifier.
string
The placement the event happened at, e.g. onboarding_complete. Optional; slug format ^[a-z0-9_]{1,64}$. Lets funnels be sliced by placement in reporting.
object
required
Must include paywall_id and variant_id for the six render-scoped events: impression, offer_tap, purchase_start, trial_start, convert, and cancel. purchase_failed and paywall_dismiss deliberately do not require them, since purchase() can fire those with no paywall in context. All other events may carry arbitrary properties.
string
Client-minted idempotency key. The SDK’s retry queue stamps one on every event so a re-delivered batch does not double-count; forwarded as-is.
string
ISO 3166-1 alpha-2 country code. Used only if server-side resolution fails.
string
Client platform, e.g. ios or android.
string
ISO 8601 timestamp of when the event occurred.

Example

Response

Errors


POST /api/paywall/token

Exchanges the publishable key for a short-lived app-session token bound to the requesting app and user. Use it when you’d rather not attach the raw pk_... key to every request: mint once at SDK init, send the token as x-app-token on subsequent calls, and re-mint when it expires.

Headers

Example

Response

The token is opaque to clients, expires after 15 minutes, and is only valid for the same x-user-id it was minted for — a token leaked from one user cannot read another user’s entitlements.

Errors


POST /api/paywall/checkout

Removed. POST /api/paywall/checkout was deleted along with the Offer/Offering entities. Web checkout is created from a published Flow’s paywall and checkout configuration and driven same-origin by @flokitai/checkout-js on the Flow page. The React Native SDK’s startWebCheckout() always throws CheckoutUnavailableError with code: 'WEB_CHECKOUT_MIGRATED'.

Other live gateway endpoints

The gateway serves the endpoints below in production today. They are listed here so the surface is complete; the SDK calls them for you, so most integrations never build these requests by hand. All of them resolve company, app, and environment server-side from the presented app credential (x-app-key or x-app-token) — a request body can never select the tenant.

Provider webhook passthrough

The gateway also exposes webhook receiver endpoints for subscription and attribution providers. Bodies are forwarded as raw bytes — provider signature headers are preserved and verified downstream, so configure these URLs directly in each provider’s dashboard.
Removed: the RevenueCat, Adapty, and generic-subscription webhook receivers (POST /api/providers/revenuecat/webhook, POST /api/providers/adapty/webhook, and POST /api/providers/generic-subscription/webhook) no longer exist on the gateway — there is no RevenueCat or Adapty adapter in the platform, and the generic receiver was deleted. If any of these URLs is still configured in a provider dashboard, its deliveries are being dropped. Third-party subscription events must arrive through a per-connection provider receiver (/webhooks/paddle/:connectionId, /webhooks/stripe/:connectionId, or /webhooks/lemon-squeezy/:connectionId).
If the downstream service cannot be reached, the gateway responds 502; if it times out (10 seconds), 504 — providers treat both as delivery failures and retry.