Build a catalog
Build the catalog once, in code or in the dashboard. From then on, your application references plans by code, not by ID, and the same code works in sandbox and live.
1. Decide the structure
For a typical SaaS:
Product: "Pro"└── Plan: "pro-monthly-usd" ├── Component: "base" (flat $49) ├── Component: "seats" (per-unit $10/seat above 5 included) └── Component: "calls" (usage-based, $0.001 per call)You’ll usually have one product per tier (Starter / Pro / Business) and one plan per (tier × interval × currency):
pro-monthly-usdpro-monthly-eurpro-yearly-usd
2. Create the product
POST /v1/productsIdempotency-Key: <uuid>{ "code": "pro", "name": "Pro", "description": "For growing teams.", "tax_code": "txcd_10000000"}The code is your handle. Use slug-case; it’s what your code will
reference forever.
3. Create the plan
POST /v1/plansIdempotency-Key: <uuid>{ "product_code": "pro", "code": "pro-monthly-usd", "currency": "USD", "interval": "month", "interval_count": 1, "trial_days": 14, "components": [ { "code": "base", "pricing": { "model": "flat", "amount": "49.00" } }, { "code": "seats", "pricing": { "model": "per_unit", "unit_amount": "10.00", "included_units": 5, "meter": "active_seats" } }, { "code": "calls", "pricing": { "model": "usage", "meter": "api_calls", "aggregation": "sum", "tiers": [{ "up_to": null, "unit_amount": "0.001" }] } } ]}4. Add a yearly plan
POST /v1/plansIdempotency-Key: <uuid>{ "product_code": "pro", "code": "pro-yearly-usd", "currency": "USD", "interval": "year", "components": [ { "code": "base", "pricing": { "model": "flat", "amount": "490.00" } }, { "code": "seats", "pricing": { "model": "per_unit", "unit_amount": "100.00", "included_units": 5, "meter": "active_seats" } } ]}Note the absence of the usage component — annual plans typically meter
usage on a separate, ungated component or fold it into the base price.
5. Verify
GET /v1/plans/pro-monthly-usdInspect the response. The components array, currency, interval,
and trial_days should match exactly.
6. Promote to live
The catalog you build in sandbox doesn’t auto-sync to live. Two paths:
Manual recreation: rerun the same POST calls against the live
base URL with a live API key.
Catalog export / import:
# Sandbox exportcurl https://api.sandbox.paylera.io/v1/admin/catalog/export \ -H "Authorization: Bearer $SANDBOX_KEY" > catalog.json
# Live importcurl -X POST https://api.paylera.io/v1/admin/catalog/import \ -H "Authorization: Bearer $LIVE_KEY" \ -H "Content-Type: application/json" \ --data @catalog.jsonImport is upsert by code: missing entries are created, existing entries are updated, nothing is deleted.
7. Lifecycle: editing, archiving
- Edit an existing plan: prices and components can change. Existing
subscriptions get the new prices on their next billing period (set
effective_atto control timing). - Archive a plan: existing subscriptions keep running, but no new subscriptions can be created against it. Use this for grandfathered pricing.
- Delete a plan: only allowed if no subscription has ever referenced it.
PATCH /v1/plans/pro-monthly-usd{ "status": "archived" }What you cannot change
currencyof a planintervalof a plancodeof a plan or product
If you need a different currency or interval, create a new plan and migrate.
Where to next
- Pricing models — what each component shape means.
- Onboard a customer — the next step in the integration.