A plan is a reusable pricing template that defines what a customer gets and how they're charged. Plans combine one or more products with pricing rules and are attached to subscriptions.
Plans support versioning - when you update a plan's pricing or product lineup, existing subscribers stay on their current version until explicitly migrated. This lets you iterate on pricing without disrupting active subscriptions.
Key concepts:
- Products - the items included in this plan, each with optional included quantities and rollover rules
- Versions - immutable snapshots of the plan configuration at a point in time
- Publishing - makes a draft plan version available for new subscriptions
- Migration - moves existing subscribers from one plan version to another, with configurable proration
List plans
Returns a paginated list of plans with optional status and custom field filters.
query Parameters
created_atFilter on created_at (date-time). Operators: eq, gt, gte, lt, lte — dot grammar, e.g. created_at.gt=value; a bare created_at=value means eq.
external_idFilter on external_id (string). Operators: eq, in — dot grammar, e.g. external_id.in=value; a bare external_id=value means eq. A bare comma-separated value is in-sugar: external_id=a,b means external_id.in=a,b.
nameFilter on name (string). Operators: eq, in, contains — dot grammar, e.g. name.in=value; a bare name=value means eq. A bare comma-separated value is in-sugar: name=a,b means name.in=a,b.
published_atFilter on published_at (date-time). Operators: eq, gt, gte, lt, lte — dot grammar, e.g. published_at.gt=value; a bare published_at=value means eq.
updated_atFilter on updated_at (date-time). Operators: eq, gt, gte, lt, lte — dot grammar, e.g. updated_at.gt=value; a bare updated_at=value means eq.
versionFilter on version (number). Operators: eq, gt, gte, lt, lte — dot grammar, e.g. version.gt=value; a bare version=value means eq.
limitPage size. Values above the server-side cap are clamped (standard default 50, cap 200; a few document-heavy lists use larger windows). Invalid values fall back to the default.
cursorOpaque continuation token from the previous response's pagination.cursor. Omit for the first page. Cursors are stateless and do not expire, but are only valid for the list and filters that produced them.
searchCase-insensitive substring match over name and external_id; relevance-ranked (exact > prefix > substring)
Case-insensitive substring match over name and external_id; relevance-ranked (exact > prefix > substring)
statusFilter by status. Filterable fields (status, name, external_id, version, published_at, created_at, updated_at) accept apifilter operator suffixes; custom_fields.
Filter by status. Filterable fields (status, name, external_id, version, published_at, created_at, updated_at) accept apifilter operator suffixes; custom_fields.
tagFilter by tag name (repeatable, containment semantics)
Filter by tag name (repeatable, containment semantics)
countsComma-separated countable fields (status) to include per-value counts for
Comma-separated countable fields (status) to include per-value counts for
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List plans › Responses
OK
Create a plan
Creates a new plan in draft status.
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Create a plan › Request Body
Org-defined custom field values for this plan.
descriptionInternal operator-facing description; not shown to buyers.
external_idCaller-supplied external identifier for reconciliation with an upstream system.
Free-form operator key/value metadata.
nameInternal operator-facing plan name.
public_descriptionBuyer-facing description shown in checkout and the customer portal.
Locale-keyed overrides for name, description and public_description.
Create a plan › Responses
Created
Bulk republish plans
Republishes each listed plan so its 'latest'-mode products re-snapshot at the product head. The manual fallback for plans the product→plan cascade did not update automatically. Best-effort per plan.
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Bulk republish plans › Request Body
plan_idsPlans to republish so their latest-mode products re-snapshot at the product head; at least one, at most 100 per request.
Bulk republish plans › Responses
OK
Get a plan
Returns a plan with its associated products.
path Parameters
idPlan UUID
Plan UUID
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get a plan › Responses
OK
Delete a plan
Soft-deletes a plan by UUID.
path Parameters
idPlan UUID
Plan UUID
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Delete a plan › Responses
No Content
Update a plan
Merge-patch update: only supplied fields change. This is ALSO the publish surface (ADR-0006): send status="active" to publish the working draft, optionally with effective_at to schedule the flip (omitted or past = immediate) and save_as_draft=true to stage without publishing. expected_version is the optimistic-concurrency token: when supplied and stale, the update returns 409.
path Parameters
idPlan UUID
Plan UUID
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Update a plan › Request Body
Org-defined custom field values for this plan.
descriptionNew internal description; null leaves it unchanged.
effective_atSchedule the versioned change to take effect at this instant instead of publishing immediately; mutually exclusive with save_as_draft.
expected_versionOptimistic-concurrency token: send the version you last read and a stale value answers 409 CONFLICT. Omitted: the update applies unconditionally (last write wins).
external_idNew external identifier; null leaves it unchanged.
Free-form operator key/value metadata.
nameNew internal plan name; null leaves it unchanged.
Per-product overrides of which product version each attached product is pinned to on this publish (product_id to product_version_id); products omitted from the map snap at the product's current head.
public_descriptionNew buyer-facing description; null leaves it unchanged.
save_as_draftPersist the change as a working draft instead of publishing it; mutually exclusive with effective_at.
statusNew catalog lifecycle status (draft, scheduled, active, archived); null leaves it unchanged.
Locale-keyed overrides for name, description and public_description.
Update a plan › Responses
OK
Copy a plan
Creates a new draft plan copying the source plan's name (with " (Copy)" suffix unless overridden), description, metadata, and every plan_products row. Versions are NOT copied - the new plan starts at version 1, status 'draft'. external_id is intentionally NOT copied.
path Parameters
idSource plan UUID
Source plan UUID
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Copy a plan › Responses
Created
Migrate plan subscribers
Previews or executes subscriber migration to a new plan version.
path Parameters
idPlan UUID
Plan UUID
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Migrate plan subscribers › Request Body
modeMigration mode: PREVIEW (dry run, no changes), IMMEDIATE (migrate now) or SCHEDULED (migrate at scheduled_at).
proration_strategyOptional proration strategy applied to the version change.
scheduled_atWhen to run the migration; required for and only valid with SCHEDULED mode, and must be in the future.
target_versionPlan version to migrate subscribers onto; must be greater than 0.
Migrate plan subscribers › Responses
OK
Add a product to a plan
Adds a product to the plan's working membership together with its allowance configuration (included quantity, rollover, refresh cadence, REFRESHING/POOL kind, limits). No product version is pinned at attach time: the membership's product_track_mode (defaulting to the org setting, else 'latest') decides at each plan publish whether the snapshot follows the product head or holds a pinned version. The membership reaches subscribers on the plan's next publish.
path Parameters
idPlan UUID
Plan UUID
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Add a product to a plan › Request Body
credit_unit_value^-?\d+(\.\d+)?$Money value of one POOL unit, MAJOR units in currency; POOL only.
currencyISO 4217 currency for a POOL's money-denominated knobs (credit_unit_value, spend_limit).
included_quantity^-?\d+(\.\d+)?$Quantity bundled into the base price before per-unit billing applies (the included allowance/seat floor); null means none.
kindEntitlement kind: REFRESHING (per-period allowance, the empty-string default) or POOL (durable credit that never resets).
Free-form operator key/value metadata for this membership.
product_idProduct to attach to the plan.
product_track_modeHow this membership tracks the product version: latest (auto-follow head) or pinned; omitted seeds from the org default.
refresh_countPlan-level default allowance-refresh cadence count; null (with refresh_unit) means one whole-period pool.
refresh_unitUnit for the refresh cadence: day, week, month, year or billing_cycle.
rollover_enabledWhen true, unused included allowance carries forward into later periods.
rollover_expiry_periodsNumber of periods after which rolled-over allowance expires; null means it never expires.
rollover_max^-?\d+(\.\d+)?$Cap on accumulated rolled-over allowance; null means uncapped.
spend_limit^-?\d+(\.\d+)?$Ceiling on a POOL's money spend, MAJOR units in currency; POOL only.
unit_limit^-?\d+(\.\d+)?$Ceiling on a POOL's unit balance; POOL only.
Add a product to a plan › Responses
Created
Remove a product from a plan
Deletes the plan-product membership row. Already-published plan versions keep their snapshot — the removal takes effect on the plan's next publish. Responds 404 when the product is not on the plan.
path Parameters
idPlan UUID
Plan UUID
productIdProduct UUID
Product UUID
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Remove a product from a plan › Responses
No Content
Update a plan-product membership
Updates a product's membership fields on a plan (e.g. the version track mode).
path Parameters
idPlan UUID
Plan UUID
productIdProduct UUID
Product UUID
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Update a plan-product membership › Request Body
product_track_modeNew version-tracking mode for the membership: latest (auto-follow the product head) or pinned; required (the only mutable field on this path).
Update a plan-product membership › Responses
OK
List plan versions (with lifecycle filter)
Returns version rows for the entity, keyset-paginated (default 100, max 200 per page). The status filter accepts draft, scheduled, published, archived; the default excludes draft (ADR-0007). Supports ?include=usage_count and ?archived_reason=.
path Parameters
idEntity UUID
Entity UUID
query Parameters
statusLifecycle status filter (repeatable / comma-separated): draft, scheduled, published, archived. Default excludes draft.
Lifecycle status filter (repeatable / comma-separated): draft, scheduled, published, archived. Default excludes draft.
archived_reasonNarrow archived rows by reason (repeatable / comma-separated): canceled, discarded, errored, superseded
Narrow archived rows by reason (repeatable / comma-separated): canceled, discarded, errored, superseded
includeComma-separated includes. Currently supports usage_count.
Comma-separated includes. Currently supports usage_count.
limitPage size. Values above the server-side cap are clamped (standard default 50, cap 200; a few document-heavy lists use larger windows). Invalid values fall back to the default.
cursorOpaque continuation token from the previous response's pagination.cursor. Omit for the first page. Cursors are stateless and do not expire, but are only valid for the list and filters that produced them.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List plan versions (with lifecycle filter) › Responses
OK
Get a plan version snapshot
Returns the historical snapshot of the given plan version (same shape as GET /plans/{id}).
path Parameters
idEntity UUID
Entity UUID
versionVersion number
Version number
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get a plan version snapshot › Responses
OK
List plan version referrers
Offset-paginated drill-down of the referrers of one type for one version of the entity.
path Parameters
idEntity UUID
Entity UUID
versionVersion number (positive integer)
Version number (positive integer)
query Parameters
typeReferrer type to list (required)
Referrer type to list (required)
limitPage size. Values above the server-side cap are clamped (standard default 50, cap 200; a few document-heavy lists use larger windows). Invalid values fall back to the default.
offsetRow offset (default 0)
Row offset (default 0)
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List plan version referrers › Responses
OK
Get plan version usage summary
Referrer counts for one version of the entity, grouped by referrer type. Powers the version-switcher hover card.
path Parameters
idEntity UUID
Entity UUID
versionVersion number (positive integer)
Version number (positive integer)
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get plan version usage summary › Responses
OK