Endpoint reference

Every route the Offer API serves, grouped by resource. Paths are relative to the API's base URL, http://localhost:6767 when you run it locally.

Under each route is the credential it takes. The admin key also works on every /apps/:appId route. Authentication explains each credential and the error format. To send these requests, import the Postman collection: it has every route below with an example body.

LabelMeaning
Secret keyThe app's key_… key, from your server.
Secret or publishable keyThe pub_… key works too, so the route can be called from a browser.
…or the account’s own tokenAn act_… token works, for its own account only.
Admin key onlyThe API's ADMIN_API_KEY. App keys get 401.
No credentialOpen to anyone.

Ids you choose for plans, entitlements, add-ons, incentives, offers and cancel flows are normalized: lowercased, spaces turned into underscores, and anything other than a-z, 0-9 and _ removed. Use underscores in ids, since hyphens are dropped. Account ids are kept as you send them. Most PATCH routes merge the fields you send into the record, and none of them change its id.

Service

  • GET/
    Plain-text banner, to check the service is up.No credential
  • GET/health
    Runs a query against Postgres and returns { ok: true }. Render's health check.No credential
  • GET/openapi.json
    The OpenAPI 3.1 document. Covers the core routes only.No credential
  • GET/docs
    Interactive API reference rendered from /openapi.json.No credential
  • GET/event-types
    The webhook event catalog, with a sample data payload for each type.No credential

Apps

An app is one product you sell. Creating one returns its secret and publishable keys.

  • POST/apps
    Creates an app. Body: { name, plan? }. Returns the app with api_key and public_key.Admin key only
  • GET/apps/:appId
    The app record, including its keys and checkout_url.Secret key
  • PATCH/apps/:appId
    Merges fields into the app, such as name or checkout_url. Key fields are ignored.Secret key
  • DELETE/apps/:appId
    Deletes the app and every plan, entitlement, add-on, incentive, account and counter in it.Secret key
  • POST/apps/:appId/keys/regenerate
    Replaces the secret key. Also revokes every account token.Secret key
  • POST/apps/:appId/public-key/regenerate
    Replaces the publishable key.Secret key

Accounts

Accounts are your customers, keyed by an id you choose. In paths they are called namespaces.

  • GET/apps/:appId/namespaces
    Searches accounts. Query: q, plan, incentive, offer, has_incentive, page, per_page.Secret key
  • GET/apps/:appId/namespaces/count
    Number of accounts in the app.Secret key
  • GET/apps/:appId/namespaces/with-incentive
    Accounts that have an incentive. Query: incentive, page, per_page.Secret key
  • POST/apps/:appId/namespaces
    Creates an account. Body: { id, name?, plan, incentive? }. 409 if the id exists.Secret key
  • GET/apps/:appId/namespaces/:accountId
    The account record, with its subscription and own add-ons.Secret key
  • PATCH/apps/:appId/namespaces/:accountId
    Merges fields into the account: name, plan, incentive, incentive_expires_at. Changes the plan without payment.Secret key
  • DELETE/apps/:appId/namespaces/:accountId
    Deletes the account and its usage counters. Usage history stays for analytics.Secret key
  • DELETE/apps/:appId/namespaces/:accountId/incentive
    Removes the account's incentive.Secret key
  • POST/apps/:appId/namespaces/:accountId/token
    Mints an account token (act_…). Body: { ttl_seconds? }, 60 to 86,400.Secret key
  • GET/apps/:appId/namespaces/:accountId/plan
    Effective access: plan, offer extras and incentive merged, with usage, max, left and can per entitlement. Private meta removed.Secret key, publishable key, or the account's own token
  • GET/apps/:appId/namespaces/:accountId/full-plan
    Same as /plan, with private meta included.Secret key, or the account's own token
  • POST/apps/:appId/namespaces/:accountId/addons
    Gives the account an add-on of its own. Body: { id }.Secret key
  • DELETE/apps/:appId/namespaces/:accountId/addons/:addonId
    Removes an add-on the account owns.Secret key

Usage

Counters for usage entitlements. Calls on a boolean entitlement return 400.

  • GET/apps/:appId/namespaces/:accountId/usage
    Counts for every entitlement on the account's plan, as { [entitlementId]: count }.Secret or publishable key
  • GET/apps/:appId/namespaces/:accountId/usage/:entitlementId
    One counter: { entitlement, count }.Secret or publishable key
  • POST/apps/:appId/namespaces/:accountId/usage/:entitlementId/add
    Adds 1. Returns 402 with an upgrade offer when a blocking limit would be passed.Secret or publishable key
  • POST/apps/:appId/namespaces/:accountId/usage/:entitlementId/remove
    Subtracts 1.Secret key
  • POST/apps/:appId/namespaces/:accountId/usage/:entitlementId/amount
    Adds { amount } (an integer, negative to subtract). Positive amounts can return 402. A negative amount with the publishable key returns 403.Secret or publishable key

Plans

  • GET/apps/:appId/plans
    Every plan, in creation order.Secret key
  • GET/apps/:appId/plans/pricing
    Pricing cards of the plans that have one, for a pricing table.Secret or publishable key
  • POST/apps/:appId/plans
    Creates a plan. Body: { id, name, description?, note?, isFree?, pricingCard? }.Secret key
  • GET/apps/:appId/plans/:planId
    One plan.Secret key
  • PATCH/apps/:appId/plans/:planId
    Merges fields into the plan, including privateMetaKeys.Secret key
  • DELETE/apps/:appId/plans/:planId
    Deletes the plan.Secret key
  • GET/apps/:appId/plans/:planId/pricing
    One plan's pricing card. 404 when it has none.Secret or publishable key
  • PATCH/apps/:appId/plans/:planId/meta
    Merges keys into the plan's meta.Secret key
  • GET/apps/:appId/plans/:planId/namespaces
    Accounts on the plan. Query: page, per_page.Secret key
  • POST/apps/:appId/plans/:planId/entitlements
    Attaches an entitlement. Body: { id, max? }. max: null means unlimited.Secret key
  • PATCH/apps/:appId/plans/:planId/entitlements/:entitlementId
    Changes an attached entitlement's limit. Body: { max }.Secret key
  • DELETE/apps/:appId/plans/:planId/entitlements/:entitlementId
    Detaches an entitlement.Secret key
  • POST/apps/:appId/plans/:planId/addons
    Attaches an add-on. Body: { id }.Secret key
  • DELETE/apps/:appId/plans/:planId/addons/:addonId
    Detaches an add-on.Secret key
  • POST/apps/:appId/plans/:planId/checkout
    Starts a PayPal checkout for the plan at its regular price. Body: { interval?, account? | email?, ref?, return_url?, cancel_url? }.Secret or publishable key

Entitlements

  • GET/apps/:appId/entitlements
    Every entitlement.Secret key
  • POST/apps/:appId/entitlements
    Creates an entitlement. Body: { id, type: usage | boolean, name, description? }.Secret key
  • GET/apps/:appId/entitlements/:entitlementId
    One entitlement.Secret key
  • PATCH/apps/:appId/entitlements/:entitlementId
    Updates it. overage: { mode: allow | block, offer_id? } makes the API refuse usage past the limit with a 402.Secret key
  • DELETE/apps/:appId/entitlements/:entitlementId
    Deletes the entitlement.Secret key

Add-ons

  • GET/apps/:appId/addons
    Every add-on.Secret key
  • POST/apps/:appId/addons
    Creates an add-on. Body: { id, name, description? }.Secret key
  • GET/apps/:appId/addons/:addonId
    One add-on.Secret key
  • PATCH/apps/:appId/addons/:addonId
    Updates the add-on.Secret key
  • DELETE/apps/:appId/addons/:addonId
    Deletes the add-on.Secret key

Incentives

An incentive overrides limits and adds add-ons on top of an account's plan, such as a promo code or a partner deal.

  • GET/apps/:appId/incentives
    Every incentive.Secret key
  • POST/apps/:appId/incentives
    Creates an incentive. Body: { id, name, description?, entitlements?, addons? }.Secret key
  • GET/apps/:appId/incentives/:incentiveId
    One incentive.Secret key
  • PATCH/apps/:appId/incentives/:incentiveId
    Updates the incentive.Secret key
  • DELETE/apps/:appId/incentives/:incentiveId
    Deletes the incentive.Secret key
  • POST/apps/:appId/incentives/:incentiveId/entitlements
    Adds an entitlement override. Body: { id, max? }.Secret key
  • PATCH/apps/:appId/incentives/:incentiveId/entitlements/:entitlementId
    Changes an override's limit. Body: { max }.Secret key
  • DELETE/apps/:appId/incentives/:incentiveId/entitlements/:entitlementId
    Removes an override.Secret key
  • POST/apps/:appId/incentives/:incentiveId/addons
    Adds an add-on. Body: { id }.Secret key
  • DELETE/apps/:appId/incentives/:incentiveId/addons/:addonId
    Removes an add-on.Secret key

Offers

Offers sell plans at a price, with extras and order bumps. Publishing needs PayPal connected.

  • GET/apps/:appId/offers
    Every offer, with live status and completed redemptions.Secret key
  • POST/apps/:appId/offers
    Creates a draft offer. Shareable offers need an id (their slug); targeted offers take an account_id and get an unguessable id.Secret key
  • GET/apps/:appId/offers/:offerId
    One offer as the dashboard sees it.Secret key
  • PATCH/apps/:appId/offers/:offerId
    Edits the offer. Applies to future purchases only.Secret key
  • DELETE/apps/:appId/offers/:offerId
    Deletes the offer. Buyers keep their price and extras.Secret key
  • POST/apps/:appId/offers/draft-preview
    Validates unsaved offer fields and returns the public view a checkout page would show. Stores nothing.Secret key
  • POST/apps/:appId/offers/:offerId/publish
    Creates the PayPal billing plans and makes the offer active.Secret key
  • POST/apps/:appId/offers/:offerId/archive
    Stops new purchases. Existing buyers keep their terms.Secret key
  • POST/apps/:appId/offers/:offerId/preview
    What each plan in the offer grants. Body: { account? } adds what would change for that account.Secret key
  • GET/apps/:appId/offers/:offerId/stats
    Views, checkouts, conversion and revenue, by plan, by ref and by bump.Secret key
  • GET/apps/:appId/offers/:offerId/public
    The offer a checkout page should show, falling back to fallback, then regular prices. Query: fallback, account, ref. Never 404.Secret or publishable key
  • POST/apps/:appId/offers/:offerId/checkout
    Prices the selection on the server and creates the PayPal subscription or order. Body: { plan, interval, bumps?, account? | email?, ref?, return_url?, cancel_url? }.Secret or publishable key

Checkouts

  • GET/apps/:appId/checkouts
    Recent checkouts, newest first. Query: offer, status, limit.Secret key
  • GET/apps/:appId/checkouts/:checkoutId
    One checkout.Secret or publishable key
  • POST/apps/:appId/checkouts/:checkoutId/complete
    Checks the payment with PayPal and applies it. Returns status created while PayPal is still activating; call again shortly.Secret or publishable key

Subscriptions

An account's paid PayPal subscription. Changes need the customer's approval on PayPal and apply once PayPal reports them.

  • GET/apps/:appId/namespaces/:accountId/subscription
    The subscription: offer, plan, price, renewal date and status. 404 when there is none.Secret key or the account's own token
  • POST/apps/:appId/namespaces/:accountId/subscription/change
    Moves to another plan or interval. Body: { plan, interval?, return_url?, cancel_url? }. Returns PayPal's approval link.Secret key
  • POST/apps/:appId/namespaces/:accountId/subscription/sync
    Re-reads the subscription from PayPal. Webhooks do this automatically.Secret key
  • POST/apps/:appId/namespaces/:accountId/subscription/cancel
    Cancels in PayPal and moves the account to the free plan. Body: { reason? }.Secret key
  • POST/apps/:appId/namespaces/:accountId/subscription/pause
    Suspends billing for 1 to 12 months. Body: { months, reason? }.Secret key
  • POST/apps/:appId/namespaces/:accountId/subscription/resume
    Ends a pause now: billing restarts and the plan comes back.Secret key

Cancel flows

The steps, questions and save offers a customer sees when they cancel.

  • GET/apps/:appId/cancel-flows
    Every flow, with sessions and saves in the last 30 days.Secret key
  • GET/apps/:appId/cancel-flows/capabilities
    Whether dynamic offers (AI) and workflow pauses (Render) are configured on this API.Secret key
  • POST/apps/:appId/cancel-flows
    Creates a flow. Body: { id?, name?, status?, steps? }. Without steps, starts from a template.Secret key
  • GET/apps/:appId/cancel-flows/:flowId
    One flow.Secret key
  • PATCH/apps/:appId/cancel-flows/:flowId
    Updates name, status (active or draft) or steps. steps replaces the whole list.Secret key
  • DELETE/apps/:appId/cancel-flows/:flowId
    Deletes the flow.Secret key
  • GET/apps/:appId/cancel-flows/:flowId/stats
    Save rate, revenue kept, reasons and offer results. Query: days.Secret key
  • POST/apps/:appId/cancel-flows/:flowId/preview-offer
    The offer a customer would get for a set of answers, without starting a session.Secret key

Cancel sessions

One customer going through a flow. The SDK calls these with an account token; every call returns the session view.

  • POST/apps/:appId/cancel-sessions
    Starts a session. Body: { flow?, account? }. With a token, the account comes from the token.Secret key or the account's own token
  • GET/apps/:appId/cancel-sessions
    Recent sessions with answers and offers. Query: flow, account, status, limit.Secret key
  • GET/apps/:appId/cancel-sessions/:sessionId
    One session.Secret key or the account's own token
  • POST/apps/:appId/cancel-sessions/:sessionId/answer
    Answers the current step. Body: { step, answer?, text? }.Secret key or the account's own token
  • POST/apps/:appId/cancel-sessions/:sessionId/back
    Goes back one step.Secret key or the account's own token
  • POST/apps/:appId/cancel-sessions/:sessionId/decline
    Turns down the save offer.Secret key or the account's own token
  • POST/apps/:appId/cancel-sessions/:sessionId/accept
    Applies the save offer. Discounts and downgrades return approve_url. Body: { return_url?, cancel_url? }.Secret key or the account's own token
  • POST/apps/:appId/cancel-sessions/:sessionId/cancel
    Cancels the subscription from the confirm step.Secret key or the account's own token
  • POST/apps/:appId/cancel-sessions/:sessionId/close
    Marks an open session abandoned.Secret key or the account's own token

Analytics

Aggregates of recorded usage. interval is one of 7d, 30d, 60d, 6m, year or alltime.

  • GET/apps/:appId/analytics
    Calls and total amount per entitlement. Query: interval (required), namespace.Secret key
  • GET/apps/:appId/analytics/top-namespaces
    Most active accounts. Query: interval, limit, entitlement.Secret key
  • GET/apps/:appId/analytics/timeseries
    Daily buckets up to 60 days, weekly beyond. Query: interval, namespace, entitlement.Secret key
  • GET/apps/:appId/analytics/events
    Recent usage events, newest first. Query: namespace, entitlement, limit.Secret key
  • GET/apps/:appId/analytics/reports
    Saved reports.Secret key
  • POST/apps/:appId/analytics/reports
    Saves a report. Body: { name, entitlements?, interval? }.Secret key
  • GET/apps/:appId/analytics/reports/:id
    One saved report.Secret key
  • PATCH/apps/:appId/analytics/reports/:id
    Updates a saved report.Secret key
  • DELETE/apps/:appId/analytics/reports/:id
    Deletes a saved report.Secret key

Webhooks and events

Endpoints that receive signed events, their delivery logs, and the app's event log.

  • GET/apps/:appId/webhooks
    Every endpoint, with delivery stats for the last 7 days.Secret key
  • POST/apps/:appId/webhooks
    Adds an endpoint. Body: { url, events, description?, source?, enabled? }. Up to 25 per app.Secret key
  • GET/apps/:appId/webhooks/:webhookId
    One endpoint, with its secret.Secret key
  • PATCH/apps/:appId/webhooks/:webhookId
    Updates url, events, description or enabled. Re-enabling clears disabled_reason.Secret key
  • DELETE/apps/:appId/webhooks/:webhookId
    Deletes the endpoint and its deliveries.Secret key
  • POST/apps/:appId/webhooks/:webhookId/secret/regenerate
    Replaces the signing secret.Secret key
  • POST/apps/:appId/webhooks/:webhookId/test
    Sends a sample event once and returns the delivery. Body: { type? }.Secret key
  • GET/apps/:appId/webhooks/:webhookId/deliveries
    Delivery log, newest first. Query: status, limit.Secret key
  • POST/apps/:appId/webhooks/:webhookId/deliveries/:deliveryId/retry
    Sends a delivery again, once, and returns the result.Secret key
  • GET/apps/:appId/events
    Events from the last 30 days, newest first. Query: type, limit.Secret key

PayPal

Each app connects its own PayPal REST app, sandbox or live.

  • GET/apps/:appId/paypal
    Whether PayPal is connected, the environment, client id and webhook registration.Secret key
  • PUT/apps/:appId/paypal
    Connects PayPal. Body: { client_id, client_secret, env: sandbox | live }. Checks the credentials and registers the webhook when the API is public.Secret key
  • DELETE/apps/:appId/paypal
    Disconnects PayPal. Existing subscriptions keep billing.Secret key
  • POST/paypal/webhooks/:appId
    PayPal's notifications for the app, mapped onto checkouts and accounts.No key. Verified with PayPal

MCP server

Each app's MCP server, and the OAuth 2.1 routes MCP clients use to connect without a key. The MCP server docs page covers the protocol and the tools.

  • POST/apps/:appId/mcp
    The app's MCP server: one JSON-RPC message (or a batch) per request, answered with JSON. 401 points to the resource metadata; 403 when the server is turned off.Secret key, or an OAuth access token for this app
  • GET/apps/:appId/mcp
    405. The server is stateless and never streams to the client.No credential
  • DELETE/apps/:appId/mcp
    405. There is no session to end.No credential
  • GET/.well-known/oauth-protected-resource/apps/:appId/mcp
    Protected resource metadata (RFC 9728): the server URL, its authorization server and scopes.No credential
  • GET/.well-known/oauth-protected-resource
    The same, for clients that drop the path. The resource is the API's origin.No credential
  • GET/.well-known/oauth-authorization-server
    Authorization server metadata (RFC 8414). Also served under /.well-known/oauth-authorization-server/* and /.well-known/openid-configuration.No credential
  • POST/oauth/register
    Dynamic client registration (RFC 7591). Body: { redirect_uris, client_name?, token_endpoint_auth_method?, client_uri?, logo_uri? }.No credential
  • GET/oauth/authorize
    Starts an authorization code request with PKCE (S256) and redirects to the dashboard's consent page.No credential
  • POST/oauth/token
    grant_type authorization_code (with code_verifier) or refresh_token. Refresh tokens rotate on every use.Client id, plus its secret for confidential clients
  • POST/oauth/revoke
    Revokes an access or refresh token (RFC 7009), which ends the connection.Client id, plus its secret for confidential clients

Admin only

Routes the dashboard and the Render Workflow call with the admin key. App keys get 401.

  • GET/orgs
    Every dashboard workspace.Admin key only
  • POST/orgs
    Creates a workspace. Body: { id, app_ids? }.Admin key only
  • GET/orgs/:orgId
    One workspace.Admin key only
  • PATCH/orgs/:orgId
    Updates app_ids, the apps the workspace shows.Admin key only
  • DELETE/orgs/:orgId
    Deletes the workspace record. Its apps stay.Admin key only
  • GET/orgs/:orgId/apps
    The workspace's apps, in app_ids order.Admin key only
  • GET/pauses/due
    Pauses due to resume. Query: limit.Admin key only
  • POST/pauses/apply
    Applies a pause. Body: { app_id, account_id, months, session_id?, reason? }.Admin key only
  • POST/pauses/resume
    Resumes a paused subscription. Body: { app_id, account_id }.Admin key only
  • POST/apps/:appId/import
    Imports history: backdated accounts and usage events, up to 10,000 of each. No webhooks fire.Admin key only
  • GET/apps/:appId/agent/threads
    The dashboard agent's chat threads for a user. Query: user_id, limit.Admin key only
  • GET/apps/:appId/agent/threads/:threadId
    One thread, with messages.Admin key only
  • PUT/apps/:appId/agent/threads/:threadId
    Creates a thread or replaces its messages. Body: { user_id, messages, title? }.Admin key only
  • PATCH/apps/:appId/agent/threads/:threadId
    Renames a thread. Body: { title }.Admin key only
  • DELETE/apps/:appId/agent/threads/:threadId
    Deletes a thread.Admin key only
  • GET/apps/:appId/mcp/settings
    The server's settings: enabled, access_level and tool_overrides.Admin key only
  • PATCH/apps/:appId/mcp/settings
    Updates enabled, access_level (read, write or full) or tool_overrides, a map of tool name to true or false that replaces the old one.Admin key only
  • GET/apps/:appId/mcp/connections
    Connected clients, most recently used first, with tool calls in the last 7 days.Admin key only
  • DELETE/apps/:appId/mcp/connections/:connectionId
    Removes a connection. An OAuth connection loses access on its next call.Admin key only
  • GET/apps/:appId/mcp/calls
    Tool calls, newest first, kept 30 days. Query: connection_id, limit.Admin key only
  • GET/oauth/requests/:requestId
    A pending authorization request, for the consent page. 404 once used or expired.Admin key only
  • POST/oauth/requests/:requestId/approve
    Approves a request. Body: { user: { id, name?, email? }, access_level, app_id? }. Returns redirect_url with the code.Admin key only
  • POST/oauth/requests/:requestId/deny
    Denies a request. Returns redirect_url with error=access_denied.Admin key only
  • GET/api/auth/*
    Dashboard sign-in (better-auth), proxied by the dashboard.Admin key only
  • POST/api/auth/*
    Dashboard sign-in (better-auth), proxied by the dashboard.Admin key only