Skip to main content
A plan defines how much to charge and how often. Plans live inside plan groups — a group is your tier structure (e.g., “Core Plans” with Starter, Pro, and Max inside it).

Plan groups and plans

A plan group is a logical container. You might have one group called “Monthly Plans” and another called “Annual Plans”, or one called “Core” and another called “Enterprise”. A plan belongs to a group and has:
  • A name (e.g., “Pro”)
  • An amount in kobo (e.g., 500000 for ₦5,000)
  • A billing interval (month or year)
  • An optional trial period in days
  • A required lookup_key — a stable handle (e.g. pro_monthly) you reference instead of the UUID

Create a plan group and plan

First, create the group:
curl -X POST https://api.useplinth.com/v1/plan-groups \
  -H "Authorization: Bearer sk_test_DOCS_SHARED_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Core Plans",
    "description": "Standard monthly tiers"
  }'
Then create plans inside it:
curl -X POST https://api.useplinth.com/v1/plan-groups/pg_01JABC.../plans \
  -H "Authorization: Bearer sk_test_DOCS_SHARED_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pro",
    "amount_minor": 500000,
    "interval": "month",
    "trial_period_days": 14,
    "lookup_key": "pro_monthly"
  }'
Response
{
  "id": "plan_pro_01...",
  "plan_group_id": "pg_01JABC...",
  "name": "Pro",
  "amount_minor": 500000,
  "interval": "month",
  "trial_period_days": 14,
  "active": true,
  "created_at": "2026-06-01T00:00:00Z"
}

Trial periods

Set trial_period_days on a plan. New subscribers start in trialing state and aren’t charged until the trial ends. When the trial expires, the subscription transitions to active and the first invoice is created. If a subscriber cancels during trial, they’re never charged. The subscription.canceled webhook fires.

Amounts are in kobo

All amount_minor values are in kobo — 1/100th of a Naira. Never pass decimal values.
Naira priceamount_minor
₦500/month50000
₦2,000/month200000
₦5,000/month500000
₦12,000/month1200000
₦120,000/year12000000

Lookup keys

Every plan requires a lookup_key (lowercase letters, numbers, underscores — unique per tenant) so your app references a plan by a stable role instead of an environment-specific UUID:
curl -X POST https://api.useplinth.com/v1/plans \
  -H "Authorization: Bearer sk_test_DOCS_SHARED_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "plan_group_id": "pg_01JABC...", "name": "Pro", "amount_minor": 500000,
        "billing_interval": "month", "lookup_key": "pro_monthly" }'
Now your app can GET /v1/plans, find the plan whose lookup_key is pro_monthly, and use its id — no hardcoded ids, and the mapping survives re-provisioning, renames, and price changes. (Reusing a key returns 409.)

Changing prices, archiving & deleting plans

Prices are immutable once a plan has subscribers. Changing amount_minor or the interval would silently re-price everyone at their next renewal, so Plinth rejects it (400 plan_immutable). Marketing fields (name, lookup_key, trial days) can always change.
To change a price: create a new plan (new price, new lookup_key e.g. pro_monthly_v2), archive the old one, then either grandfather existing subscribers or migrate them with a plan change. Deleting is archive-by-default. DELETE /v1/plans/{id}:
  • If the plan has subscribers → it’s archived (active: false): existing subscribers keep billing, history is preserved, and it stops accepting new sign-ups. Response: { archived: true, deleted: false }.
  • If the plan was never subscribed → it’s hard-deleted. Response: { archived: false, deleted: true }.
Historical invoices are never affected either way — they snapshot the amount at billing time.

List all plans

curl https://api.useplinth.com/v1/plans \
  -H "Authorization: Bearer sk_test_DOCS_SHARED_KEY"
Returns active plans only by default. Pass ?include_archived=true to include archived plans.