Skip to main content
A plan is the recurring billing template in Z2Pay. It is composed of components (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

MethodRouteDescription
POST/plansCreates a plan in draft
GET/plansLists plans (paginated, with filters)
GET/plans/{id}Retrieves a plan by ID
PATCH/plans/{id}Updates editable plan fields
POST/plans/{id}/publishPublishes the plan (draftactive)
POST/plans/{id}/archiveArchives the plan
GET/plans/{id}/templatePlan + components with their current price
GET/plans/{id}/itemsLists the plan’s components
POST/plans/{id}/itemsAdds a component to the plan
PATCH/plans/{id}/items/{itemId}Updates a component
DELETE/plans/{id}/items/{itemId}Archives a component
GET/plans/{id}/pricesLists the plan’s price versions
POST/plans/{id}/pricesCreates a new price version
POST/plans/{id}/prices/{priceId}/archiveArchives a price version
POST/plans/{id}/chargesCreates a component + price atomically
POST requests accept the optional Idempotency-Key header to safely retry requests. See Conventions.

Create a plan

POST /plans — returns 201 Created. The plan is created in draft.

Parameters (body)

code
string
required
Unique identifier for the plan. 1 to 100 characters. Lowercase letters, digits, hyphens, and underscores only (regex ^[a-z0-9-_]+$).
name
string
required
Plan name. 1 to 255 characters.
description
string
Free-form description. Up to 1000 characters. Optional.
metadata
object
Free-form key/value object for your own data. Optional.
curl -X POST https://billing-api.sandbox.z2pay.com/plans \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7f1c0a2e-create-plan-pro" \
  -d '{
    "code": "plano-pro",
    "name": "Plano Pro",
    "description": "Acesso completo, cobrado mensalmente",
    "metadata": { "tier": "pro" }
  }'
{
  "id": "plan_8aZqkR3v2NfLpW0d",
  "companyId": "comp_4yT2bX",
  "code": "plano-pro",
  "name": "Plano Pro",
  "description": "Acesso completo, cobrado mensalmente",
  "status": "draft",
  "metadata": { "tier": "pro" },
  "createdAt": "2026-06-24T13:40:11.000Z",
  "updatedAt": "2026-06-24T13:40:11.000Z",
  "deletedAt": null
}
If a plan with the same code already exists, the API responds with 409 Conflict. See Errors.

List plans

GET /plans — returns 200 OK with a paginated list.

Parameters (query)

status
string
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.
code
string
Partial match on the code field. Up to 100 characters.
name
string
Partial match on the name field. Up to 255 characters.
item
string
Partial match on any of the plan’s component names. Up to 255 characters.
priceMin
integer
Includes only plans that have at least one component whose current price is greater than or equal to this value (in cents). Minimum 0.
priceMax
integer
Includes only plans that have at least one component whose current price is less than or equal to this value (in cents). Minimum 0.
createdFrom
string
ISO 8601 date (with offset) or simple date (YYYY-MM-DD). Includes only plans created on or after this date (inclusive).
createdTo
string
ISO 8601 date (with offset) or simple date (YYYY-MM-DD). Includes only plans created on or before this date (inclusive).
sortBy
string
Sort field. Valid values: createdAt, name.
sortDir
string
Sort direction. Valid values: asc, desc.
page
integer
Pagination page number.
limit
integer
Number of items per page.
curl "https://billing-api.sandbox.z2pay.com/plans?status=active,draft&page=1&limit=20" \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX"

Get plan by ID

GET /plans/{id} — returns 200 OK, or 404 Not Found if the plan does not exist.
curl https://billing-api.sandbox.z2pay.com/plans/plan_8aZqkR3v2NfLpW0d \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX"

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)

currency
string
Restricts prices to a single currency (e.g., BRL). Optional — without it, the current price for each component across all currencies is returned.
curl "https://billing-api.sandbox.z2pay.com/plans/plan_8aZqkR3v2NfLpW0d/template?currency=BRL" \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX"

Update a plan

PATCH /plans/{id} — partial update. Returns 200 OK or 404 Not Found.
Pass only the fields you want to change. Status transitions are not done here — use publish / archive instead.

Parameters (body)

name
string
New name. 1 to 255 characters. Optional.
description
string
New description. Up to 1000 characters. Accepts null to clear. Optional.
metadata
object
New metadata object. Optional.
curl -X PATCH https://billing-api.sandbox.z2pay.com/plans/plan_8aZqkR3v2NfLpW0d \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Plano Pro (anual)" }'

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.
A plan can only be published if it has at least one recurring component with a price — otherwise, subscriptions created from it would generate invoices with no line items. Add a charge before publishing.
curl -X POST https://billing-api.sandbox.z2pay.com/plans/plan_8aZqkR3v2NfLpW0d/publish \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX"

Components (plan_items)

Each component represents a charge within the plan.
  • 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.
The default is 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.
key
string
required
Stable slug, unique per plan. 1 to 100 characters. Lowercase letters, digits, hyphens, and underscores only (regex ^[a-z0-9-_]+$).
name
string
required
Component name. 1 to 255 characters.
kind
string
default:"recurring"
recurring (charged every cycle) or activation (charged once, at enrollment).
quantityDefault
integer
default:1
Default quantity. Positive integer.
quantityIncluded
integer
default:0
Quantity already included (not charged beyond this). Integer greater than or equal to 0.
optional
boolean
default:false
Whether the component is optional in the bundle.
displayOrder
integer
default:0
Display order. Integer greater than or equal to 0.
description
string
Description. Up to 1000 characters. Optional.
metadata
object
Free-form object. Optional.
curl -X POST https://billing-api.sandbox.z2pay.com/plans/plan_8aZqkR3v2NfLpW0d/items \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "assinatura-base",
    "name": "Assinatura base",
    "kind": "recurring",
    "quantityDefault": 1,
    "quantityIncluded": 0,
    "optional": false,
    "displayOrder": 0
  }'
{
  "id": "pli_2bN7xQ9wKtLm4Rd0",
  "companyId": "comp_4yT2bX",
  "planId": "plan_8aZqkR3v2NfLpW0d",
  "key": "assinatura-base",
  "name": "Assinatura base",
  "kind": "recurring",
  "quantityDefault": 1,
  "quantityIncluded": 0,
  "optional": false,
  "displayOrder": 0,
  "description": null,
  "metadata": {},
  "createdAt": "2026-06-24T13:42:30.000Z",
  "updatedAt": "2026-06-24T13:42:30.000Z",
  "deletedAt": null
}

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).
On PATCH, the editable fields are: name, quantityDefault, quantityIncluded, optional, displayOrder, description (accepts null), and metadata. All optional. key and kind are not editable.
The default component (present in legacy plans) cannot be archived — DELETE responds with 409 Conflict in that case.
curl -X DELETE https://billing-api.sandbox.z2pay.com/plans/plan_8aZqkR3v2NfLpW0d/items/pli_2bN7xQ9wKtLm4Rd0 \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX"

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.
Creating a new price automatically marks the previous price for the same combination (currency, recurrence) as non-current.
planItemId
string
ID of the component (plan_items.id) this price versions. 1 to 36 characters. Optional — preferred when you just created the component.
planItemKey
string
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).
billingScheme
string
default:"fixed"
Billing scheme. Valid values: fixed, tiered, per_unit, package, metered.
money
object
required
Price value.
recurrence
object
required
Recurrence rule.
tiers
array
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.
packageSize
integer
Package size (for billingScheme=package). Positive integer. Optional.
meterId
string
Meter identifier (for billingScheme=metered). Up to 100 characters. Optional.
trialSpec
object
Trial configuration. Optional.
Invalid combinations of anchor / unit / anchorDay are rejected: anchorDay is required when anchor=day_of_month, and anchor=day_of_month/end_of_month is only valid with unit=month or unit=year.
curl -X POST https://billing-api.sandbox.z2pay.com/plans/plan_8aZqkR3v2NfLpW0d/prices \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3d8e1b40-price-pro-mensal" \
  -d '{
    "planItemId": "pli_2bN7xQ9wKtLm4Rd0",
    "billingScheme": "fixed",
    "money": { "amount": 4990, "currency": "BRL" },
    "recurrence": {
      "interval": 1,
      "unit": "month",
      "anchor": "subscription_start",
      "collectionTiming": "prepaid"
    },
    "trialSpec": { "durationDays": 7, "requiresPaymentMethod": true }
  }'
{
  "id": "price_5cM3pV8nDhQr2Yt9",
  "planItemId": "pli_2bN7xQ9wKtLm4Rd0",
  "planId": "plan_8aZqkR3v2NfLpW0d",
  "billingScheme": "fixed",
  "amount": 4990,
  "currency": "BRL",
  "recurrence": {
    "interval": 1,
    "unit": "month",
    "anchor": "subscription_start",
    "collectionTiming": "prepaid"
  },
  "tiers": null,
  "packageSize": null,
  "meterId": null,
  "trialSpec": { "durationDays": 7, "requiresPaymentMethod": true },
  "isCurrent": true,
  "publishedAt": "2026-06-24T13:45:02.000Z",
  "archivedAt": null,
  "createdAt": "2026-06-24T13:45:02.000Z",
  "updatedAt": "2026-06-24T13:45:02.000Z"
}

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).
curl -X POST https://billing-api.sandbox.z2pay.com/plans/plan_8aZqkR3v2NfLpW0d/prices/price_5cM3pV8nDhQr2Yt9/archive \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX"

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.
Creates the component and its initial price in a single transaction. Zod validates both bodies before any write — if validation fails, nothing is persisted (avoids orphaned components from the two-step POST /items + POST /prices flow).
item
object
required
Same fields as Add a component (key, name, kind, etc.).
price
object
required
Same fields as Create a price version, except planItemId and planItemKey — the service links the price to the newly created component automatically.
curl -X POST https://billing-api.sandbox.z2pay.com/plans/plan_8aZqkR3v2NfLpW0d/charges \
  -H "x-api-key: SUA_CHAVE_DE_SANDBOX" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a1c4-charge-base" \
  -d '{
    "item": {
      "key": "assinatura-base",
      "name": "Assinatura base",
      "kind": "recurring"
    },
    "price": {
      "billingScheme": "fixed",
      "money": { "amount": 4990, "currency": "BRL" },
      "recurrence": {
        "interval": 1,
        "unit": "month",
        "anchor": "subscription_start"
      }
    }
  }'
{
  "item": {
    "id": "pli_2bN7xQ9wKtLm4Rd0",
    "planId": "plan_8aZqkR3v2NfLpW0d",
    "key": "assinatura-base",
    "name": "Assinatura base",
    "kind": "recurring"
  },
  "price": {
    "id": "price_5cM3pV8nDhQr2Yt9",
    "planItemId": "pli_2bN7xQ9wKtLm4Rd0",
    "planId": "plan_8aZqkR3v2NfLpW0d",
    "billingScheme": "fixed",
    "amount": 4990,
    "currency": "BRL",
    "isCurrent": true
  }
}
1

Create the plan

POST /plans with code and name. It starts in draft.
2

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.
3

Publish the plan

POST /plans/{id}/publish moves the plan to active, allowing subscriptions to be created.
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.