Billing & Subscriptions

Manage subscriptions, invoices, payment methods, checkout sessions, credit packs, pricing plans, API keys, Stripe Connect profiles, and academic verification flows for Backbuild and Backbuild Science.

47 endpoints. Generated from the OpenAPI 3.1 specification.

GET /v1/academic-verifications

List academic verifications (reviewer queue)

Returns the org-scoped reviewer queue of pending and submitted academic verifications. Filtered by optional `status` and paginated by `page`/`page_size`.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
page query integer no 1-based page number.
page_size query integer no Records per page (1–100).
status query string (enum) no Filter by verification status.

Responses

StatusDescription
200 Paginated reviewer queue.
400 400 Bad Request — a request parameter, body field, or the JSON body itself failed validation.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
GET /v1/academic-verifications/{id}

Get an academic verification

Returns a single academic verification by id. Accessible by the subject user or a reviewer.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
id path string<uuid> yes Academic verification identifier (UUIDv7).

Responses

StatusDescription
200 Academic verification record.
400 `INVALID_ID_FORMAT` — malformed `id`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
user_id string<uuid>
org_id string | null
saas_package_id string | null
provider string (enum) manual_reviewsheeridverifyle
status string (enum) pendingsubmittedapprovedrejectedexpired
institution_name string | null
academic_role string | null
rejection_reason string | null
reviewed_by string | null
reviewed_at string | null
expires_at string | null
created_at string<date-time>
updated_at string<date-time>
POST /v1/academic-verifications/{id}/review

Review an academic verification

Approves or rejects an academic verification. Callers must hold the reviewer role. `rejection_reason` is required when `decision=rejected`.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
id path string<uuid> yes Academic verification identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
decision string (enum) approvedrejected yes
rejection_reason string no Required when `decision=rejected`.

Responses

StatusDescription
200 Verification reviewed.
400 `INVALID_ID_FORMAT`, `INVALID_JSON`, `VALIDATION_ERROR` (including missing `rejection_reason` when decision=rejected).
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
user_id string<uuid>
org_id string | null
saas_package_id string | null
provider string (enum) manual_reviewsheeridverifyle
status string (enum) pendingsubmittedapprovedrejectedexpired
institution_name string | null
academic_role string | null
rejection_reason string | null
reviewed_by string | null
reviewed_at string | null
expires_at string | null
created_at string<date-time>
updated_at string<date-time>
POST /v1/academic-verifications/{id}/submit

Submit evidence for academic verification

Attaches institution name, role, and optional evidence URL to an in-progress academic verification.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
id path string<uuid> yes Academic verification identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
institution_name string yes
academic_role string yes
evidence_url string<uri> no

Responses

StatusDescription
200 Evidence submitted.
400 `INVALID_ID_FORMAT`, `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
409 `CONFLICT` — resubmission quota exceeded.
429 429 Too Many Requests — a rate limit or usage quota was exceeded. Retry after the indicated window.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
user_id string<uuid>
org_id string | null
saas_package_id string | null
provider string (enum) manual_reviewsheeridverifyle
status string (enum) pendingsubmittedapprovedrejectedexpired
institution_name string | null
academic_role string | null
rejection_reason string | null
reviewed_by string | null
reviewed_at string | null
expires_at string | null
created_at string<date-time>
updated_at string<date-time>
POST /v1/academic-verifications/start

Start an academic verification

Initiates an academic verification for the authenticated user. Optionally scoped to a specific SaaS package and verification provider.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
saas_package_id string<uuid> no
provider string (enum) manual_reviewsheeridverifyle no

Responses

StatusDescription
201 Academic verification started.
400 `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 `NOT_FOUND` — the specified package or provider not found.
409 `CONFLICT` — concurrent verification submission quota exceeded.
429 `RATE_LIMIT_EXCEEDED`.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
201 response body: data fields
FieldTypeDescription
id string<uuid>
user_id string<uuid>
org_id string | null
saas_package_id string | null
provider string (enum) manual_reviewsheeridverifyle
status string (enum) pendingsubmittedapprovedrejectedexpired
institution_name string | null
academic_role string | null
rejection_reason string | null
reviewed_by string | null
reviewed_at string | null
expires_at string | null
created_at string<date-time>
updated_at string<date-time>
GET /v1/api-keys

List organization API keys

Returns the API keys for the authenticated organization. Raw key values are never returned — only the key prefix, name, scopes, and metadata. The organization and RLS scope are derived from the session.

Auth Session JWT (Bearer)

Responses

StatusDescription
200 List of API keys.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
keys array of object
POST /v1/api-keys

Create an API key

Creates a new organization API key with the specified name, scopes, expiry, and optional rate limit. The raw key (`bb_live_<prefix>_<secret>`) is returned ONCE in the response and never stored — if lost it cannot be retrieved. Requires recent authentication (token issued within 5 minutes). Organization API key cap (100) is enforced before creation.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
name string yes
scopes array of string yes
expires_in_days integer no Expiry in days from now (omit for non-expiring).
rate_limit_rpm integer no
pinned_env_id string<uuid> no

Responses

StatusDescription
201 API key created. The `key` field contains the raw key; store it now as it will not be shown again.
400 `VALIDATION_ERROR` — invalid JSON, invalid `name`, disallowed `scopes`, out-of-range `expires_in_days` or `rate_limit_rpm`, or malformed `pinned_env_id`.
401 `AUTH_REQUIRED`, `REAUTH_REQUIRED` — a recent (within 5 min) session token is required.
403 `FORBIDDEN` — caller lacks permission to create keys or the `pinned_env_id` would escalate privilege.
429 `QUOTA_EXCEEDED` — organization has reached the 100-key limit.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
201 response body: data fields
FieldTypeDescription
id string<uuid>
key string Full raw API key in format `bb_live_{8hexchars}_{64hexchars}`. Store it now — it will not be shown again.
prefix string
name string
scopes array of string
expires_at string | null
created_at string<date-time>
DELETE /v1/api-keys/{id}

Revoke an API key

Immediately revokes an API key, preventing any future use. Requires recent authentication (token issued within 5 minutes).

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
id path string<uuid> yes API key identifier (UUIDv7).

Responses

StatusDescription
200 API key revoked.
400 `VALIDATION_ERROR` — malformed key `id`.
401 `AUTH_REQUIRED`, `REAUTH_REQUIRED` — a recent session token is required.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
revoked boolean (enum) true
POST /v1/api-keys/{id}/rotate

Rotate an API key

Atomically revokes the existing key and creates a replacement with the same name and scopes. The new raw key is returned once. An optional `grace_period_minutes` (0–60, default 5) keeps the old key valid for a short overlap window so in-flight requests are not disrupted. Requires recent authentication (token issued within 5 minutes).

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
id path string<uuid> yes API key identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
grace_period_minutes integer no How long the old key remains valid after rotation (0–60 min).

Responses

StatusDescription
200 API key rotated. The `key` field contains the new raw key; store it now as it will not be shown again.
400 `VALIDATION_ERROR` — malformed key `id`.
401 `AUTH_REQUIRED`, `REAUTH_REQUIRED` — a recent session token is required.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid> New key identifier.
key string New raw API key. Store it now.
prefix string
name string
scopes array of string
old_key_id string<uuid>
old_key_expires_at string | null Grace period expiry for the old key (null = revoked immediately).
created_at string<date-time>
GET /v1/billing

Get billing overview

Returns the billing overview for the authenticated organization, including subscription state, payment methods, and credit balance. Requires owner or admin role within the org; members without that role receive `403 FORBIDDEN`.

Auth Session JWT (Bearer)

Responses

StatusDescription
200 Billing overview for the organization.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 `FORBIDDEN` — caller lacks owner or admin role within the organization.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
subscription object An organization subscription record.
payment_methods array of object Saved payment methods.
credits object AI credit balance for an organization.
GET /v1/billing/client-config

Get Stripe client configuration

Returns the Stripe publishable key required to initialize the Stripe.js client on the frontend. Returns `503 CHECKOUT_FAILED` if Stripe is not configured for the deployment.

Auth Session JWT (Bearer)

Responses

StatusDescription
200 Stripe client configuration.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
503 `CHECKOUT_FAILED` — Stripe is not configured for this deployment.
200 response body: data fields
FieldTypeDescription
publishable_key string Stripe publishable key (pk_live_* or pk_test_*) used to initialize Stripe.js.
POST /v1/billing/create-checkout-session

Create a hosted Stripe Checkout session

Creates a hosted Stripe Checkout session for a subscription plan. Returns a `checkout_url` to redirect the user to Stripe. `success_url` and `cancel_url` must be `https://` URLs targeting an allowlisted application domain; other values return `400 VALIDATION_ERROR`. `trial_period_days` is a client hint that is server-clamped against the plan's configured maximum — the effective value can be less than requested.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
price_id string yes Stripe price id for the plan.
package_id string<uuid> yes SaaS package id (UUIDv7).
success_url string<uri> no HTTPS URL to redirect to after successful checkout. Must target an allowlisted application domain.
cancel_url string<uri> no HTTPS URL to redirect to after checkout cancellation. Must target an allowlisted application domain.
trial_period_days integer no Client-suggested free trial length in days. Server-clamped to the plan's configured maximum; the effective value may be lower than requested.

Responses

StatusDescription
201 Checkout session created. Redirect the user to `checkout_url`.
400 `VALIDATION_ERROR` (invalid fields, disallowed redirect domain, or invalid `trial_period_days`), `STRIPE_CREDENTIALS_MISSING` (Stripe not configured for the package).
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 `CHECKOUT_FAILED` — Stripe session creation failed or server misconfiguration.
201 response body: data fields
FieldTypeDescription
checkout_url string | null Stripe-hosted Checkout URL. Redirect the user here.
POST /v1/billing/create-embedded-checkout-session

Create an embedded Stripe Checkout session

Creates an embedded Stripe Checkout session. Returns a `client_secret` for initializing Stripe.js Embedded Checkout on the page. Supports `mode=subscription` (default, requires `price_id`) and `mode=setup` (card collection without a charge). `return_url` must target an allowlisted application domain; other values return `400 VALIDATION_ERROR`.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
price_id string no Stripe price id. Required for `mode=subscription`; ignored for `mode=setup`.
package_id string<uuid> yes SaaS package id (UUIDv7).
return_url string<uri> no HTTPS URL the embedded checkout redirects to on completion. Must target an allowlisted application domain.
trial_period_days integer no Client-suggested trial length in days. Server-clamped to the plan's maximum.
mode string (enum) subscriptionsetup no Checkout mode. `subscription` (default) charges immediately; `setup` collects a card for later use.

Responses

StatusDescription
201 Embedded checkout session created.
400 `VALIDATION_ERROR` (invalid fields, disallowed redirect domain, or `price_id` missing for `mode=subscription`), `STRIPE_CREDENTIALS_MISSING` (Stripe not configured for the package).
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 `CHECKOUT_FAILED` — Stripe session creation failed or server misconfiguration.
201 response body: data fields
FieldTypeDescription
client_secret string Stripe Checkout client secret for initializing Stripe.js Embedded Checkout.
mode string (enum) subscriptionsetup Mode of the session (`setup` only present when mode=setup was requested).
POST /v1/billing/credit-packs/{planId}/purchase

Purchase a credit pack

Initiates a one-time Stripe Checkout session (`mode=payment`) to purchase an AI credit pack identified by `planId`. Returns `checkout_url` to redirect the user and an `order_id` for status polling. The plan must be of type `credit_pack` with a configured Stripe price. `return_url` and `cancel_url` must be `https://` URLs targeting an allowlisted application domain.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
planId path string<uuid> yes Credit pack pricing plan identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
return_url string<uri> no HTTPS URL to redirect to after successful purchase. Must target an allowlisted application domain.
cancel_url string<uri> no HTTPS URL to redirect to on cancellation. Must target an allowlisted application domain.

Responses

StatusDescription
201 Credit pack checkout session created.
400 `INVALID_ID_FORMAT` (malformed `planId`), `VALIDATION_ERROR` (invalid redirect URLs or plan not eligible for credit-pack purchase).
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 `FORBIDDEN` — caller lacks permission to purchase credit packs for the org.
404 `NOT_FOUND` — plan not found.
500 `CHECKOUT_FAILED` — Stripe session creation failed.
503 `CHECKOUT_FAILED` — credit pack plan not yet synced to Stripe (missing `stripe_price_id`).
201 response body: data fields
FieldTypeDescription
order_id string<uuid> Platform order id for status polling.
checkout_url string | null Stripe Checkout URL to redirect the user to.
checkout_session_id string Stripe Checkout session id.
credit_amount integer Number of AI credits in the pack.
price_cents integer Price in the smallest currency unit (e.g. cents).
currency string ISO 4217 currency code.
GET /v1/billing/credit-packs/orders/{orderId}

Get credit pack order status

Returns the status and metadata of a one-time credit pack order. Used for post-checkout polling to confirm that credits have been provisioned after Stripe payment.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
orderId path string<uuid> yes Credit pack order identifier (UUIDv7).

Responses

StatusDescription
200 Credit pack order record.
400 `INVALID_ID_FORMAT` — malformed `orderId`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 `FORBIDDEN` — order does not belong to this organization.
404 `NOT_FOUND` — order not found.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
org_id string<uuid>
plan_id string<uuid>
status string Order status (e.g. `pending`, `completed`, `failed`).
credit_amount integer
price_cents integer
currency string
credits_applied boolean Whether credits have been applied to the organization balance.
credits_applied_at string | null
stripe_checkout_session_id string | null
failure_reason string | null
created_at string<date-time>
completed_at string | null
GET /v1/billing/credits

Get credit balance

Returns the current AI credit balance for the authenticated organization.

Auth Session JWT (Bearer)

Responses

StatusDescription
200 Credit balance.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
balance number Current available credit balance.
currency string Currency code (e.g. `usd`).
GET /v1/billing/invoices

List invoices

Returns invoices for the authenticated organization with optional status and date-range filters. Default limit 25, max 100.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
status query string (enum) no Filter by invoice status.
from_date query string<date-time> no Inclusive start of the invoice date range (ISO 8601 datetime).
to_date query string<date-time> no Inclusive end of the invoice date range (ISO 8601 datetime).
limit query integer no Maximum number of invoices to return (1–100, default 25).
offset query integer no Number of invoices to skip for pagination.

Responses

StatusDescription
200 Paginated list of invoices.
400 400 Bad Request — a request parameter, body field, or the JSON body itself failed validation.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
GET /v1/billing/payment-methods

List saved payment methods

Returns all saved Stripe payment methods for the authenticated organization.

Auth Session JWT (Bearer)

Responses

StatusDescription
200 List of saved payment methods.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
PUT /v1/billing/payment-methods

Upsert a payment method

Records a Stripe payment method (`pm_*`) for the organization. The API verifies the payment method belongs to this organization's Stripe customer before writing; a cross-customer or unknown `pm_*` returns `400 PAYMENT_METHOD_NOT_OWNED`. All `pm_*` and billing-address string fields are capped at Stripe's documented field lengths.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
payment_method_id string yes Stripe `pm_*` identifier. Must belong to this organization's Stripe customer.
set_as_default boolean no Whether to set this as the default payment method (default true when omitted).
billing_details object no Optional billing detail overrides.

Responses

StatusDescription
200 Payment method upserted.
400 `VALIDATION_ERROR` (invalid body fields), `PAYMENT_METHOD_NOT_OWNED` (the `pm_*` id does not belong to this org's Stripe customer or cannot be found).
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid> Platform-assigned record id.
stripe_payment_method_id string Stripe `pm_*` identifier.
type string Payment method type (e.g. `card`).
is_default boolean Whether this is the default payment method.
created_at string<date-time> When the record was created.
GET /v1/billing/subscription-status

Poll subscription activation status

Returns whether the organization's subscription has been activated. Intended for short-polling after Stripe Checkout redirect to confirm the Stripe webhook has been processed (typically called every 3 s for up to 2 min).

Auth Session JWT (Bearer)

Responses

StatusDescription
200 Current activation state.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
active boolean True once the Stripe webhook has confirmed activation.
status string | null Current subscription status string.
GET /v1/billing/subscriptions

List subscriptions

Returns the organization's subscriptions with optional status filter and offset pagination. Default limit 25, max 100.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
status query string (enum) no Filter by subscription status. Must be one of the canonical values matching the underlying platform subscription state.
limit query integer no Maximum number of records to return (1–100, default 25).
offset query integer no Number of records to skip for pagination.

Responses

StatusDescription
200 Paginated list of subscriptions.
400 400 Bad Request — a request parameter, body field, or the JSON body itself failed validation.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
POST /v1/billing/subscriptions

Create a subscription

Creates a new subscription for the authenticated organization using a Stripe payment method. All Stripe identifier and billing-address fields are bounded at Stripe's documented field lengths.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
plan_id string yes Pricing plan key or ID.
interval string (enum) monthyear yes Billing interval.
payment_method_id string yes Stripe `pm_*` identifier.
billing_details object yes
seats integer no Number of seats (default 1).
metadata object no Optional metadata JSONB.

Responses

StatusDescription
201 Subscription created.
400 400 Bad Request — a request parameter, body field, or the JSON body itself failed validation.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
201 response body: data fields
FieldTypeDescription
id string<uuid> Platform-assigned subscription id.
org_id string<uuid> Owning organization.
plan_id string Pricing plan key.
interval string (enum) monthyear Billing interval.
status string (enum) activetrialingpast_duecanceledincompletepaused Subscription lifecycle status.
seats integer Number of seats.
cancel_at_period_end boolean Whether the subscription is scheduled to cancel at period end.
current_period_end string | null End of the current billing period.
created_at string<date-time>
updated_at string<date-time>
PUT /v1/billing/subscriptions/{subscriptionId}

Update a subscription

Updates an existing subscription's plan, seat count, or cancellation scheduling. At least one body field must be supplied. Returns `404 NOT_FOUND` when the subscription does not exist or belongs to another organization.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
subscriptionId path string<uuid> yes Subscription identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
plan_id string no New pricing plan key.
seats integer no New seat count.
cancel_at_period_end boolean no Schedule cancellation at the current period end.
metadata object no Optional metadata JSONB.

Responses

StatusDescription
200 Updated subscription record.
400 `INVALID_ID_FORMAT` (malformed `subscriptionId`), `VALIDATION_ERROR` (invalid body fields).
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
404 `NOT_FOUND` — subscription not found or belongs to another organization.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid> Platform-assigned subscription id.
org_id string<uuid> Owning organization.
plan_id string Pricing plan key.
interval string (enum) monthyear Billing interval.
status string (enum) activetrialingpast_duecanceledincompletepaused Subscription lifecycle status.
seats integer Number of seats.
cancel_at_period_end boolean Whether the subscription is scheduled to cancel at period end.
current_period_end string | null End of the current billing period.
created_at string<date-time>
updated_at string<date-time>
GET /v1/billing/usage

Get billable usage summary

Returns billable usage rollup for the authenticated organization. When `env_id` is absent or `all`, the global aggregate across all environments is returned. The `window` parameter controls the time range (default `30d`).

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
env_id query string no Environment UUID to scope usage to, or the literal `all` (default) for a global aggregate across all environments. Any other non-UUID value returns `400 VALIDATION_ERROR`.
window query string (enum) no Aggregation window. One of `24h`, `7d`, `30d` (default), `90d`, `all`.

Responses

StatusDescription
200 Usage summary.
400 `VALIDATION_ERROR` — `env_id` is not a UUID or `all`; `window` is not one of the allowed values.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
window string Requested aggregation window.
env_id string | null Environment the rollup is scoped to (null for global aggregate).
total_credits_used number Total AI credits consumed in the window.
by_env array of object Per-environment breakdown (only present in global-aggregate responses).
GET /v1/pricing

List public pricing plans

Returns pricing plans sourced from Stripe, optionally filtered by `package_id` and `environment`. This is a public endpoint — no authentication required. Results are KV-cached; `cached` and `cacheAge` indicate whether the response is from cache. Rate-limited to 50 requests per minute per IP (IPv6 normalized to /64).

Public Public: no authentication

Parameters

NameInTypeRequiredDescription
package_id query string<uuid> no Filter to a specific SaaS package (UUIDv7). When omitted all published packages are returned.
environment query string (enum) no Filter to a specific deployment environment.

Responses

StatusDescription
200 Pricing plans. The `cached` flag indicates whether the response was served from the KV cache.
400 400 Bad Request — a request parameter, body field, or the JSON body itself failed validation.
429 429 Too Many Requests — a rate limit or usage quota was exceeded. Retry after the indicated window.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
POST /v1/signup-checkout/intents

Create a pre-auth signup checkout intent

Creates a signup checkout intent before the user authenticates, capturing plan and package selection. No authentication is required; rate-limiting (20/hour per IP) is enforced by the platform. A Cloudflare Turnstile token is required (supplied as `cf-turnstile-response` header by the frontend). Only `https://` requests traversing the Cloudflare edge are accepted (the source IP is derived exclusively from `cf-connecting-ip`). At least one of `org_id`, `org_slug_or_id`, or `domain` must be supplied as a resolved UUID; slug/domain resolution is not performed server-side on this endpoint.

Public Public: no authentication

Request Body

FieldTypeRequiredDescription
anonymous_session_id string yes Stable anonymous session identifier to link the pre-auth intent to a post-auth session.
org_id string<uuid> no
org_slug_or_id string no
domain string no
package_id string<uuid> no
package_slug string no
pricing_plan_price_id string no
billing_interval string (enum) monthyear no
trial_period_days integer no Requested trial length (server-clamped to plan maximum).
metadata object no

Responses

StatusDescription
201 Signup checkout intent created.
400 `INVALID_JSON` (unparseable body), `VALIDATION_ERROR` (schema violation), `INVALID_PARAMETER` (source IP could not be determined — request must traverse Cloudflare edge).
403 `FORBIDDEN` — academic-verification prerequisite not met, or prerequisite plan not satisfied.
404 `NOT_FOUND` — the specified plan or package does not exist.
409 `CONFLICT` — org conflict or intent lifecycle state conflict (e.g. intent expired or already consumed). `error.details` may carry `intent_org_id` and `caller_org_id` for org-conflict disambiguation.
429 `RATE_LIMIT_EXCEEDED` — per-IP rate limit exceeded.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
201 response body: data fields
FieldTypeDescription
id string<uuid>
status string (enum) pendingprocessingcompletedexpiredcancelled
checkout_url string | null Hosted checkout redirect URL.
client_secret string | null Stripe embedded checkout client_secret.
created_at string<date-time>
expires_at string | null
POST /v1/signup-checkout/intents/{id}/resume

Resume a signup checkout intent (authenticated)

Resumes a previously created signup checkout intent after user authentication. The caller must be an active member of the intent's owning organization. When `resolved_org_id` differs from the session's `org_id`, the caller's membership in `resolved_org_id` is pre-checked at the route layer before the underlying platform call.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
id path string<uuid> yes Signup checkout intent identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
pricing_plan_price_id string no
billing_interval string (enum) monthyear no

Responses

StatusDescription
200 Intent resumed; checkout session details returned.
400 `INVALID_ID_FORMAT` (malformed `id`), `INVALID_JSON` (unparseable body), `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 `FORBIDDEN` — caller is not an active member of the resolved org.
404 `NOT_FOUND` — intent not found.
409 `CONFLICT` — intent expired, already consumed, or org conflict.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
status string (enum) pendingprocessingcompletedexpiredcancelled
checkout_url string | null Hosted checkout redirect URL.
client_secret string | null Stripe embedded checkout client_secret.
created_at string<date-time>
expires_at string | null
GET /v1/stripe-connect

Get legacy Stripe Connect configuration

Returns the Stripe Connect configuration for the given SaaS package and environment. Requires `stripe_connect.view` permission. This is the legacy per-(package, environment) endpoint; new integrations should use `/v1/stripe-profiles`.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
saas_package_id query string<uuid> yes SaaS package identifier (UUIDv7).
environment query string (enum) no Deployment environment.

Responses

StatusDescription
200 Stripe Connect configuration.
400 400 Bad Request — a request parameter, body field, or the JSON body itself failed validation.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 `NOT_FOUND` — Stripe Connect configuration not found for this package and environment.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
saas_package_id string<uuid>
environment string (enum) developmentstagingpreprodproduction
stripe_account_id string | null
charges_enabled boolean
payouts_enabled boolean
details_submitted boolean
platform_fee_pct number | null
client_fee_pct number | null
created_at string<date-time>
updated_at string<date-time>
POST /v1/stripe-connect

Create legacy Stripe Connect configuration

Creates a Stripe Connect configuration for a SaaS package and environment. Requires `stripe_connect.create` permission. This is the legacy per-(package, environment) endpoint; new integrations should use `/v1/stripe-profiles`.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
saas_package_id string<uuid> yes
environment string (enum) developmentstagingpreprodproduction no
stripe_account_id string yes Stripe account id (`acct_...`). Required for the legacy endpoint.
account_type string (enum) standardexpresscustom no Stripe Connect account type (default `express`).
publishable_key string no
business_profile object no
webhook_secret_ref string no Reference name for the stored webhook secret.

Responses

StatusDescription
201 Stripe Connect configuration created.
400 `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
409 `CONFLICT` — a Stripe Connect configuration already exists for this package and environment.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
201 response body: data fields
FieldTypeDescription
id string<uuid>
saas_package_id string<uuid>
environment string (enum) developmentstagingpreprodproduction
stripe_account_id string | null
charges_enabled boolean
payouts_enabled boolean
details_submitted boolean
platform_fee_pct number | null
client_fee_pct number | null
created_at string<date-time>
updated_at string<date-time>
PUT /v1/stripe-connect

Update legacy Stripe Connect configuration

Updates the Stripe Connect configuration for a SaaS package and environment. At least one mutable field (beyond `saas_package_id` and `environment`) must be supplied. Requires `stripe_connect.update` permission. A body-supplied `stripe_account_id` that does not match the verified stored value is rejected with `403 FORBIDDEN`. This is the legacy endpoint; new integrations should use `/v1/stripe-profiles`.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
saas_package_id string<uuid> yes
environment string (enum) developmentstagingpreprodproduction no
stripe_account_id string no
account_type string (enum) standardexpresscustom no
publishable_key string no
business_profile object no
webhook_secret_ref string no

Responses

StatusDescription
200 Updated Stripe Connect configuration.
400 `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 `FORBIDDEN` — missing permission or submitted `stripe_account_id` does not match the verified record.
404 `NOT_FOUND` — configuration not found.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
saas_package_id string<uuid>
environment string (enum) developmentstagingpreprodproduction
stripe_account_id string | null
charges_enabled boolean
payouts_enabled boolean
details_submitted boolean
platform_fee_pct number | null
client_fee_pct number | null
created_at string<date-time>
updated_at string<date-time>
PUT /v1/stripe-connect/fees

Update legacy Stripe Connect fee percentages

Updates the platform and/or client fee percentages for a Stripe Connect configuration. At least one fee field must be supplied. Requires `stripe_connect.update` permission. This is the legacy endpoint; prefer `/v1/stripe-profiles/:profileId/fees`.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
saas_package_id string<uuid> yes
environment string (enum) developmentstagingpreprodproduction no
platform_fee_percent number no
client_fee_percent number no

Responses

StatusDescription
200 Fee percentages updated.
400 `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 `NOT_FOUND` — configuration not found.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
POST /v1/stripe-connect/onboard

Start legacy Stripe Connect onboarding (deprecated — 410 Gone)

**Permanently retired.** This endpoint is disabled by default and returns `410 GONE` with `Sunset`, `Link`, and `Deprecation` headers pointing to the successor `/v1/stripe-profiles/:profileId/onboard/start`. It accepted a tenant-supplied `stripe_account_id`, making account-claim races possible. Use `POST /v1/stripe-profiles` and `POST /v1/stripe-profiles/:profileId/onboard/start` instead.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
saas_package_id string<uuid> yes
environment string (enum) developmentstagingpreprodproduction no
stripe_account_id string yes
refresh_url string<uri> no
return_url string<uri> no

Responses

StatusDescription
200 Onboarding started (only returned when the endpoint is explicitly re-enabled by the operator).
400 `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
409 `CONFLICT` — a Stripe Connect record already exists for this package/environment.
410 `GONE` — this endpoint is permanently retired. Use `POST /v1/stripe-profiles` + `POST /v1/stripe-profiles/:profileId/onboard/start`.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
POST /v1/stripe-connect/onboard/complete

Complete legacy Stripe Connect onboarding

Marks the Stripe Connect onboarding as complete by recording the `charges_enabled`, `payouts_enabled`, and `details_submitted` states reported by Stripe. Requires `stripe_connect.update` permission. This is the legacy endpoint; prefer `/v1/stripe-profiles/:profileId/onboard/complete`.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
saas_package_id string<uuid> yes
environment string (enum) developmentstagingpreprodproduction no
charges_enabled boolean no
payouts_enabled boolean no
details_submitted boolean no

Responses

StatusDescription
200 Onboarding completion recorded.
400 `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 `NOT_FOUND` — Stripe Connect configuration not found.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
POST /v1/stripe-connect/sync

Sync legacy Stripe Connect account status

Syncs Stripe account status fields (charges/payouts enabled, business profile, currency) for a legacy Stripe Connect configuration. Requires `stripe_connect.update` permission. This is the legacy endpoint; prefer `/v1/stripe-profiles/:profileId/sync`.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
saas_package_id string<uuid> yes
environment string (enum) developmentstagingpreprodproduction no

Responses

StatusDescription
200 Stripe Connect status synced.
400 `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 `NOT_FOUND` — configuration not found.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
GET /v1/stripe-profiles

List Stripe Connect profiles

Returns the organization's Stripe Connect profiles with optional status filter and page/pageSize pagination. Permission gating is enforced inside the platform; the caller must hold `stripe.profiles.view` or equivalent.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
page query integer no 1-based page number.
page_size query integer no Records per page (1–100).
status query string (enum) no Filter by profile onboarding status.

Responses

StatusDescription
200 Paginated list of Stripe Connect profiles.
400 400 Bad Request — a request parameter, body field, or the JSON body itself failed validation.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
POST /v1/stripe-profiles

Create a Stripe Connect profile

Creates a new Stripe Connect profile for the organization. `stripe_account_id` may not be supplied on creation — the Stripe account is created server-side by calling `POST /v1/stripe-profiles/:profileId/onboard/start`. Permission gating is enforced inside the platform.

Auth Session JWT (Bearer)

Request Body

FieldTypeRequiredDescription
name string yes
description string no
country string no
default_currency string no

Responses

StatusDescription
201 Stripe Connect profile created.
400 `INVALID_JSON`, `VALIDATION_ERROR` (including rejection of a supplied `stripe_account_id`).
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
409 `CONFLICT` — a profile with a conflicting constraint already exists.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
201 response body: data fields
FieldTypeDescription
id string<uuid>
org_id string<uuid>
name string
description string | null
stripe_account_id string | null
status string (enum) pendingonboardingrestrictedactivedisabled
country string | null
default_currency string | null
charges_enabled boolean
payouts_enabled boolean
details_submitted boolean
platform_fee_pct number | null
client_fee_pct number | null
version integer Optimistic concurrency version.
created_at string<date-time>
updated_at string<date-time>
GET /v1/stripe-profiles/{profileId}

Get a Stripe Connect profile

Returns a single Stripe Connect profile by id. Returns `404 NOT_FOUND` for profiles not belonging to the caller's organization.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
profileId path string<uuid> yes Stripe Connect profile identifier (UUIDv7).

Responses

StatusDescription
200 Stripe Connect profile.
400 `INVALID_ID_FORMAT` — malformed `profileId`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
org_id string<uuid>
name string
description string | null
stripe_account_id string | null
status string (enum) pendingonboardingrestrictedactivedisabled
country string | null
default_currency string | null
charges_enabled boolean
payouts_enabled boolean
details_submitted boolean
platform_fee_pct number | null
client_fee_pct number | null
version integer Optimistic concurrency version.
created_at string<date-time>
updated_at string<date-time>
PATCH /v1/stripe-profiles/{profileId}

Update a Stripe Connect profile

Updates mutable fields on a Stripe Connect profile (name, description, country, default currency, business profile). `expected_version` is required for optimistic concurrency. At least one mutable field (beyond `expected_version`) must be supplied.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
profileId path string<uuid> yes Stripe Connect profile identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
expected_version integer yes
name string no
description string no
country string no
default_currency string no

Responses

StatusDescription
200 Updated profile.
400 `INVALID_ID_FORMAT`, `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
409 `CONFLICT` — `expected_version` is stale (concurrent modification).
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
org_id string<uuid>
name string
description string | null
stripe_account_id string | null
status string (enum) pendingonboardingrestrictedactivedisabled
country string | null
default_currency string | null
charges_enabled boolean
payouts_enabled boolean
details_submitted boolean
platform_fee_pct number | null
client_fee_pct number | null
version integer Optimistic concurrency version.
created_at string<date-time>
updated_at string<date-time>
DELETE /v1/stripe-profiles/{profileId}

Delete (archive) a Stripe Connect profile

Soft-deletes a Stripe Connect profile. `expected_version` is required for optimistic concurrency. The platform enforces that the profile has no active subscriber dependencies before deletion.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
profileId path string<uuid> yes Stripe Connect profile identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
expected_version integer yes

Responses

StatusDescription
200 Profile archived.
400 `INVALID_ID_FORMAT`, `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
409 `CONFLICT` — `expected_version` is stale, profile has dependents, or profile is already archived.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
PATCH /v1/stripe-profiles/{profileId}/fees

Update Stripe Connect profile fee percentages

Updates the platform and/or client fee percentages for a Stripe Connect profile. Requires a fresh MFA step-up (the session's last MFA step-up must be within the configured window). `expected_version` is required for optimistic concurrency. At least one fee field must be supplied.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
profileId path string<uuid> yes Stripe Connect profile identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
expected_version integer yes
platform_fee_percent number | null no Platform application fee percentage (0–50). Nullable to clear.
client_fee_percent number no Client-side fee percentage (0–50).

Responses

StatusDescription
200 Fee percentages updated.
400 `INVALID_ID_FORMAT`, `INVALID_JSON`, `VALIDATION_ERROR`.
401 `AUTH_REQUIRED` or `MFA_STEPUP_REQUIRED` — a fresh MFA step-up is required for fee changes.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
409 `CONFLICT` — `expected_version` is stale.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
200 response body: data fields
FieldTypeDescription
id string<uuid>
org_id string<uuid>
name string
description string | null
stripe_account_id string | null
status string (enum) pendingonboardingrestrictedactivedisabled
country string | null
default_currency string | null
charges_enabled boolean
payouts_enabled boolean
details_submitted boolean
platform_fee_pct number | null
client_fee_pct number | null
version integer Optimistic concurrency version.
created_at string<date-time>
updated_at string<date-time>
POST /v1/stripe-profiles/{profileId}/links

Link a package to a Stripe Connect profile

Associates a (SaaS package, environment) combination with this Stripe Connect profile.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
profileId path string<uuid> yes Stripe Connect profile identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
saas_package_id string<uuid> yes
environment string (enum) developmentstagingpreprodproduction yes

Responses

StatusDescription
201 Package linked.
400 `INVALID_ID_FORMAT`, `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
409 `CONFLICT` — link already exists or live/test mode conflict.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
201 response body: data fields
FieldTypeDescription
profile_id string<uuid>
saas_package_id string<uuid>
environment string (enum) developmentstagingpreprodproduction
created_at string<date-time>
DELETE /v1/stripe-profiles/{profileId}/links/{pkgId}/{env}

Unlink a package from a Stripe Connect profile

Removes the link between a (SaaS package, environment) combination and this Stripe Connect profile. The `archive_stripe_products` query parameter controls whether linked Stripe products are archived.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
profileId path string<uuid> yes Stripe Connect profile identifier (UUIDv7).
pkgId path string<uuid> yes SaaS package identifier (UUIDv7).
env path string (enum) yes Deployment environment.
archive_stripe_products query string (enum) no When `true`, archive the associated Stripe products/prices for this link.

Responses

StatusDescription
200 Package unlinked.
400 `INVALID_ID_FORMAT` (malformed `profileId` or `pkgId`), `INVALID_PARAMETER` (invalid `env` value).
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
409 `CONFLICT` — link has active subscribers or active Stripe products (archive first or use `archive_stripe_products=true`).
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
POST /v1/stripe-profiles/{profileId}/onboard/complete

Complete Stripe Connect profile onboarding

Syncs the Stripe account's current state (charges/payouts/details, onboarding status, tax info) from Stripe into the platform record. `expected_version` is required for optimistic concurrency.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
profileId path string<uuid> yes Stripe Connect profile identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
expected_version integer yes

Responses

StatusDescription
200 Onboarding state synced from Stripe.
400 `INVALID_ID_FORMAT`, `INVALID_JSON`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
409 `CONFLICT` — profile has no Stripe account yet (call `/onboard/start` first) or `expected_version` is stale.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
502 `EXTERNAL_SERVICE_ERROR` — Stripe account lookup failed.
503 `SERVICE_UNAVAILABLE` — Stripe is not configured.
200 response body: data fields
FieldTypeDescription
id string<uuid>
org_id string<uuid>
name string
description string | null
stripe_account_id string | null
status string (enum) pendingonboardingrestrictedactivedisabled
country string | null
default_currency string | null
charges_enabled boolean
payouts_enabled boolean
details_submitted boolean
platform_fee_pct number | null
client_fee_pct number | null
version integer Optimistic concurrency version.
created_at string<date-time>
updated_at string<date-time>
POST /v1/stripe-profiles/{profileId}/onboard/start

Start Stripe Connect profile onboarding

Orchestrates server-side Stripe account creation (if the profile has no `stripe_account_id` yet) and returns a fresh Stripe AccountLink URL for the operator to complete KYC. Uses a stable Idempotency-Key so a retry after a transient failure does not create a duplicate Stripe account. `refresh_url` and `return_url` must use `https://` and target the configured application domain.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
profileId path string<uuid> yes Stripe Connect profile identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
refresh_url string<uri> no
return_url string<uri> no

Responses

StatusDescription
200 Onboarding URL returned. Redirect the operator to `onboarding_url`.
400 `INVALID_ID_FORMAT`, `INVALID_JSON`, `VALIDATION_ERROR` (disallowed redirect URL).
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
409 `CONFLICT` — profile state conflict (e.g. `stripe_account_id` already claimed by another profile).
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
502 `EXTERNAL_SERVICE_ERROR` — Stripe account creation or AccountLink creation failed.
503 `SERVICE_UNAVAILABLE` — Stripe is not configured for this deployment.
200 response body: data fields
FieldTypeDescription
onboarding_url string<uri> Stripe AccountLink URL for the operator.
expires_at string<date-time>
POST /v1/stripe-profiles/{profileId}/sync

Queue Stripe Connect profile sync

Enqueues a re-sync of linked Stripe plans for this profile, optionally filtered to specific environments. Returns `202 Accepted`.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
profileId path string<uuid> yes Stripe Connect profile identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
environments array of enum developmentstagingpreprodproduction no

Responses

StatusDescription
202 Sync enqueued.
400 `INVALID_ID_FORMAT`, `VALIDATION_ERROR`.
401 401 Unauthorized — missing, expired, malformed, or revoked credentials.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
409 `CONFLICT` — profile is not in a state that allows sync (e.g. onboarding not yet complete).
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.
POST /v1/stripe-profiles/{profileId}/webhook-secret/rotate

Rotate Stripe Connect profile webhook secret

Rotates the webhook signing secret for a Stripe Connect profile. Requires a fresh MFA step-up (PCI DSS Req 8.6 / SOC 2 CC6.6 key management control). `expected_version` is required for optimistic concurrency.

Auth Session JWT (Bearer)

Parameters

NameInTypeRequiredDescription
profileId path string<uuid> yes Stripe Connect profile identifier (UUIDv7).

Request Body

FieldTypeRequiredDescription
expected_version integer yes

Responses

StatusDescription
200 Webhook secret rotated.
400 `INVALID_ID_FORMAT`, `INVALID_JSON`, `VALIDATION_ERROR`.
401 `AUTH_REQUIRED` or `MFA_STEPUP_REQUIRED` — a fresh MFA step-up is required.
403 403 Forbidden — authenticated but lacking the required membership, role, permission, MFA step-up, or feature entitlement.
404 404 Not Found — the requested resource does not exist or is not visible to the caller.
409 `CONFLICT` — `expected_version` is stale.
500 500 Internal Server Error — an unexpected server-side error. The message is sanitized; the correlation id is logged server-side.