plan_items)
— each representing a charge within the bundle — and each component has one or more price versions (prices),
always in cents.
The typical flow is: create the plan in draft, add at least one recurring component with a price (or use the
atomic charge endpoint), and publish the plan so it can accept subscriptions.
All endpoints on this page require the
x-api-key header. See Authentication.
The examples use the sandbox base URL https://billing-api.sandbox.z2pay.com.Concepts
Plan
Billing template identified by a unique
code. Starts in draft and must be published.
IDs prefixed with plan_.Component (plan_item)
Each charge within the plan. Can be
recurring (charged every cycle) or activation
(charged once, at enrollment). IDs prefixed with pli_.Price (price)
A price version for a component, in cents. Creating a new price automatically marks the previous
price for the same combination (currency, recurrence) as non-current. IDs prefixed with
price_.Recurrence
Defines the cadence (
unit + interval), the cycle anchor, and when to charge (prepaid/postpaid).Endpoints
| Method | Route | Description |
|---|---|---|
POST | /plans | Creates a plan in draft |
GET | /plans | Lists plans (paginated, with filters) |
GET | /plans/{id} | Retrieves a plan by ID |
PATCH | /plans/{id} | Updates editable plan fields |
POST | /plans/{id}/publish | Publishes the plan (draft → active) |
POST | /plans/{id}/archive | Archives the plan |
GET | /plans/{id}/template | Plan + components with their current price |
GET | /plans/{id}/items | Lists the plan’s components |
POST | /plans/{id}/items | Adds a component to the plan |
PATCH | /plans/{id}/items/{itemId} | Updates a component |
DELETE | /plans/{id}/items/{itemId} | Archives a component |
GET | /plans/{id}/prices | Lists the plan’s price versions |
POST | /plans/{id}/prices | Creates a new price version |
POST | /plans/{id}/prices/{priceId}/archive | Archives a price version |
POST | /plans/{id}/charges | Creates a component + price atomically |
Create a plan
POST /plans — returns 201 Created. The plan is created in draft.Parameters (body)
Unique identifier for the plan. 1 to 100 characters. Lowercase letters, digits, hyphens, and
underscores only (regex
^[a-z0-9-_]+$).Plan name. 1 to 255 characters.
Free-form description. Up to 1000 characters. Optional.
Free-form key/value object for your own data. Optional.
List plans
GET /plans — returns 200 OK with a paginated list.Parameters (query)
Filter by status. Accepts CSV (
?status=active,draft). Valid values: draft, active,
inactive, archived. Up to 100 characters.Quick search — partial match on
code OR name. Up to 100 characters.Partial match on the
code field. Up to 100 characters.Partial match on the
name field. Up to 255 characters.Partial match on any of the plan’s component names. Up to 255 characters.
Includes only plans that have at least one component whose current price is greater than or equal to this value (in cents). Minimum 0.
Includes only plans that have at least one component whose current price is less than or equal to this value (in cents). Minimum 0.
ISO 8601 date (with offset) or simple date (
YYYY-MM-DD). Includes only plans created on or after this date (inclusive).ISO 8601 date (with offset) or simple date (
YYYY-MM-DD). Includes only plans created on or before this date (inclusive).Sort field. Valid values:
createdAt, name.Sort direction. Valid values:
asc, desc.Pagination page number.
Number of items per page.
Get plan by ID
GET /plans/{id} — returns 200 OK, or 404 Not Found if the plan does not exist.Plan template
GET /plans/{id}/template — returns 200 OK with the plan plus each component and its current price. 404 Not Found if the plan does not exist.Parameters (query)
Restricts prices to a single currency (e.g.,
BRL). Optional — without it, the current price for each component across all currencies is returned.Update a plan
PATCH /plans/{id} — partial update. Returns 200 OK or 404 Not Found.publish / archive instead.
Parameters (body)
New name. 1 to 255 characters. Optional.
New description. Up to 1000 characters. Accepts
null to clear. Optional.New metadata object. Optional.
Publish and archive a plan
POST /plans/{id}/publish transitions the plan from draft to active.
POST /plans/{id}/archive archives the plan. Both return 200 OK or 404 Not Found.Components (plan_items)
Each component represents a charge within the plan.kind: recurring vs activation
kind: recurring vs activation
recurring— charged every cycle. Becomes a subscription item when a subscription is created from the plan.activation— charged/delivered once, at enrollment. Materializes on the enrollment invoice.
recurring.Add a component
POST /plans/{id}/items — returns 201 Created. 404 if the plan does not exist, 409 if the key already exists in the plan.Stable slug, unique per plan. 1 to 100 characters. Lowercase letters, digits, hyphens, and
underscores only (regex
^[a-z0-9-_]+$).Component name. 1 to 255 characters.
recurring (charged every cycle) or activation (charged once, at enrollment).Default quantity. Positive integer.
Quantity already included (not charged beyond this). Integer greater than or equal to 0.
Whether the component is optional in the bundle.
Display order. Integer greater than or equal to 0.
Description. Up to 1000 characters. Optional.
Free-form object. Optional.
List, update, and archive components
GET /plans/{id}/items lists the components (non-paginated).
PATCH /plans/{id}/items/{itemId} updates a component (200 / 404).
DELETE /plans/{id}/items/{itemId} archives a component (200 / 404).PATCH, the editable fields are: name, quantityDefault, quantityIncluded, optional,
displayOrder, description (accepts null), and metadata. All optional. key and kind are not editable.
Prices (prices)
Each price is a billing version of a component, always in cents.Create a price version
POST /plans/{id}/prices — returns 201 Created and marks this price as current. 404 if the plan does not exist.ID of the component (
plan_items.id) this price versions. 1 to 36 characters. Optional — preferred
when you just created the component.Slug of the component (
plan_items.key). 1 to 100 characters, regex ^[a-z0-9-_]+$. Optional.
If neither planItemId nor planItemKey is provided, uses the default component (legacy).Billing scheme. Valid values:
fixed, tiered, per_unit, package, metered.Price value.
Recurrence rule.
Price tiers (for
billingScheme=tiered). Each tier has upTo (positive integer or null
for the last tier, indicating infinity) and unitAmount (cents, integer greater than or equal to 0). Optional.Package size (for
billingScheme=package). Positive integer. Optional.Meter identifier (for
billingScheme=metered). Up to 100 characters. Optional.Trial configuration. Optional.
List and archive prices
GET /plans/{id}/prices lists the plan’s price versions (non-paginated).
POST /plans/{id}/prices/{priceId}/archive archives a specific version (200 / 404).Create a charge (component + price) atomically
POST /plans/{id}/charges — returns 201 Created with { item, price }. 400 if validation fails (item or price), 404 if the plan does not exist.POST /items + POST /prices flow).
Same fields as Add a component (
key, name, kind, etc.).Same fields as Create a price version, except
planItemId and
planItemKey — the service links the price to the newly created component automatically.Recommended flow
Add the charge
Use
POST /plans/{id}/charges to create the recurring component and its price in one step, or
call POST /plans/{id}/items followed by POST /plans/{id}/prices.With the plan
active and at least one recurring component with a price, you are ready to create subscriptions.See also
Subscriptions
Create and manage subscriptions from a published plan.
Invoices
How invoices are generated and charged each cycle.
Cycles
Understand cadence, anchors, and collection timing.
Subscriptions overview
Overview of the recurring billing module.

