---
title: "Create a plan for a product"
method: POST
path: "/v1/sdk/products/{productRef}/plans"
tags: ["Plans"]
---

# Create a plan for a product

`POST /v1/sdk/products/{productRef}/plans`

## Request

- Base URL: `https://api.solvapay.com`
- URL: `https://api.solvapay.com/v1/sdk/products/{productRef}/plans`
- Auth: HTTP bearer
- TypeScript: <https://skmtc.dev/solvapay/apis/solvapay-rest-api/docs/v1/sdk/products/:productRef/plans/post.ts> (a function with typed inputs; its response is checked against the schema)

Creates a plan under the product from its composable pricing `options[]` (charges, billing cycle, tiers, limits, entitlements, trials, discounts). Options are validated for cross-option coherence and rejected with 400 if invalid; money fields are integer minor units.

## Path parameters

- `productRef` string, required

## Request body

- CreatePlanRequest
  - `accessExpiryDays` integer
  - `currency` string
  - `description` string
  - `features` object
  - `maxActiveUsers` integer
  - `metadata` object
  - `name` string, required
  - `options` union[], required
    - union
      - object
        - `amountMinor` integer, required
        - `currency` string, required
        - `kind` 'charge', required
        - `label` string
        - `meter` string
        - `oneTime` boolean
        - `per` 'unit' | 'seat' | 'flat', required
      - object
        - `anchor` string, date-time
        - `count` integer
        - `interval` 'week' | 'month' | 'year', required
        - `kind` 'billingCycle', required
      - object
        - `charge` object, required
          - `amountMinor` integer, required
          - `currency` string, required
          - `label` string
          - `meter` string
          - `per` 'unit' | 'seat' | 'flat', required
        - `from` integer, required
        - `kind` 'tier', required
        - `label` string
        - `mode` 'graduated' | 'volume', required
        - `to` integer, nullable, required
      - object
        - `cap` integer, required
        - `kind` 'limit', required
        - `label` string
        - `meter` string, required
        - `onExceed` 'block' | 'draw_credits', required
        - `scope` 'billing_period' | 'lifetime' | 'rolling_window', required
        - `windowDays` integer
      - object
        - `feature` string, required
        - `kind` 'entitlement', required
        - `label` string
        - `scope` string
        - `value` union, required
          - boolean
          - number
          - string
      - object
        - `days` integer, required
        - `kind` 'trial', required
        - `label` string
        - `onEnd` 'convert' | 'cancel', required
      - object
        - `appliesTo` 'recurring' | 'setup' | 'usage' | 'all'
        - `currency` string
        - `cycles` integer
        - `duration` 'once' | 'repeating' | 'forever', required
        - `kind` 'discount', required
        - `label` string
        - `mode` 'percentage' | 'fixed', required
        - `value` integer, required
      - object
        - `kind` 'autoAssigned', required
        - `label` string
      - object
        - `kind` 'hidden', required
        - `label` string
  - `status` 'active' | 'inactive' | 'archived'
  - `taxBehavior` 'auto' | 'inclusive' | 'exclusive'

## Response `201`

Plan created successfully

- Plan
  - `accessExpiryDays` number: Access expiry in days
  - `createdAt` string, required: Creation timestamp
  - `currency` string: Currency code (ISO 4217)
  - `currencySymbol` string: Currency symbol (derived from currency)
  - `description` string: Plan description
  - `features` object: Plan features
  - `isActive` boolean, required: Whether the plan is active (derived from status)
  - `maxActiveUsers` number: Maximum number of active users
  - `name` string: Plan name
  - `options` object[], required: Composable pricing options (money as integer minor units)
  - `price` number: Derived headline price in minor units (the base or one-time charge)
  - `reference` string, required: Plan reference
  - `requiresPayment` boolean: Whether payment is required
  - `status` string, required: Plan status
  - `type` 'recurring' | 'one-time' | 'usage-based' | 'hybrid', required: Derived plan type (read from options)
  - `updatedAt` string, required: Last update timestamp

## Other responses

- `404`: Product not found

## Changes

- **2026-09-30** `e13ee57d07a6`: 1 breaking, 1 info
  - removed `subschema #4, subschema #6, subschema #8` from the `options/items/` request property `oneOf` list
  - added `subschema #4, subschema #6` to the `options/items/` request property `oneOf` list
- **2026-09-28** `e38277a25e0c`: 1 breaking, 1 info
  - removed the enum value `top_up` of the request property `options/items/oneOf[subschema #4]/onExceed`
  - added the new `draw_credits` enum value to the request property `options/items/oneOf[subschema #4]/onExceed`
- **2026-09-18** `aacdf8ae7816`: 1 breaking, 1 info
  - removed `subschema #6, subschema #7` from the `options/items/` request property `oneOf` list
  - added `subschema #6` to the `options/items/` request property `oneOf` list
- **2026-08-09** `b6df401ff4cc`: 1 breaking, 1 info
  - removed `subschema #2, subschema #10, subschema #11` from the `options/items/` request property `oneOf` list
  - added `subschema #2, subschema #10, subschema #11` to the `options/items/` request property `oneOf` list
- **2026-08-05** `032545ac8d13`: 3 breaking, 30 warning, 1 info
  - added the new required request property `options`
  - the `currency` request property's minLength was increased from `0` to `3`
  - removed the required property `hidden` from the response with the `201` status
  - the `currency` request property's maxLength was set to `3`
  - …30 more

[Operation history](https://skmtc.dev/solvapay/apis/solvapay-rest-api/changes/v1/sdk/products/:productRef/plans/post.md)

---

[API](https://skmtc.dev/solvapay/apis/solvapay-rest-api.md) - [All operations](https://skmtc.dev/solvapay/apis/solvapay-rest-api/llms.txt) - [OpenAPI document](https://skmtc.dev/solvapay/apis/solvapay-rest-api/revisions/bd3a1cb8cb52?raw)
