> ## Documentation Index
> Fetch the complete documentation index at: https://commet.co/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Publish plan draft

> Publish a draft as the plan's current version, as a new experiment with its own code, or as the next version of the experiment it started from. A country missing a price, or removing the signup trial of the plan's group from the current version, stops the publication. With move, subscribers of earlier versions move at their renewal or now; if any of them does not fit the new version, nothing is published and the response lists each one with what would make it fit, to send back in changes. Subscribers whose status does not allow a move, such as paused ones, stay where they are. A move keeps each subscriber's currency. Send preview to check without publishing.

Publish a draft as the plan's current version, as a new experiment with its own code, or as the next version of the experiment it started from. A country missing a price, or removing the signup trial of the plan's group from the current version, stops the publication. With move, subscribers of earlier versions move at their renewal or now; if any of them does not fit the new version, nothing is published and the response lists each one with what would make it fit, to send back in changes. Subscribers whose status does not allow a move, such as paused ones, stay where they are. A move keeps each subscriber's currency. Send preview to check without publishing.

Starting an experiment moves nobody. Publishing with `move: "keep"` leaves every earlier subscriber on its version; move them later with [Move subscribers to a plan version](https://commet.co/docs/api-reference/plans/create-plan-move.md).


## OpenAPI

````yaml /openapi/en/rest/2026-08-27.json post /plans/{id}/drafts/{draftId}/publish
openapi: 3.1.0
info:
  title: Commet API
  version: '2026-08-27'
  description: Billing and payments API for SaaS and AI products.
servers:
  - url: https://commet.co/api/v1
security:
  - ApiKeyAuth: []
tags:
  - name: Test Clock
    description: >-
      Simulate the passage of time in sandbox to test billing flows. Mirrors
      Stripe test clocks: forward-only and sandbox-only.
  - name: Customers
  - name: Subscriptions
  - name: Plans
  - name: Markets
    description: >-
      Reusable country groups for market-specific prices and selectable price
      variants.
  - name: Offers
  - name: Promo Codes
  - name: Features
  - name: Usage
  - name: Seats
  - name: Quota
  - name: Add-ons
  - name: Portal
  - name: Credit Packs
  - name: Plan Groups
  - name: Invoices
  - name: Payments
    description: >-
      One-time payments with no subscription or plan. Charge a customer's saved
      payment method off-session, or create a payment link the customer opens to
      pay. Tax, invoice, and receipt are handled automatically.
  - name: Transactions
  - name: Webhooks
  - name: API Keys
  - name: Payouts
    description: >-
      Withdraw available balance to your bank and manage the payout account
      (verification + destination bank accounts).
  - name: Provisioning
    description: >-
      Take ownership of an organization an agent provisioned headlessly. The
      agent holds the API keys from the start; a human claims the account
      through a claim link.
paths:
  /plans/{id}/drafts/{draftId}/publish:
    post:
      tags:
        - Plans
      summary: Publish plan draft
      description: >-
        Publish a draft as the plan's current version, as a new experiment with
        its own code, or as the next version of the experiment it started from.
        A country missing a price, or removing the signup trial of the plan's
        group from the current version, stops the publication. With move,
        subscribers of earlier versions move at their renewal or now; if any of
        them does not fit the new version, nothing is published and the response
        lists each one with what would make it fit, to send back in changes.
        Paused subscribers stay where they are. Send preview to check without
        publishing.
      operationId: publish-plan-draft
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >-
            Unique key used to safely retry this write for 24 hours without
            applying it twice.
          schema:
            type: string
            minLength: 1
            maxLength: 200
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: draftId
          in: path
          required: true
          schema:
            type: string
        - name: Commet-Version
          in: header
          required: false
          description: >-
            API version used by this reference. Send this header to select this
            contract explicitly.
          schema:
            type: string
            default: '2026-08-27'
          example: '2026-08-27'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  properties:
                    publishAs:
                      type: string
                      const: current
                      description: current sells the draft to every new buyer.
                    move:
                      default: keep
                      description: >-
                        What happens to subscribers of earlier versions: keep
                        them, move them at their next renewal, or move them now
                        (features and balance change today, the price at
                        renewal). Nobody moves unless every one of them fits or
                        has a change below.
                      type: string
                      enum:
                        - keep
                        - period_end
                        - now
                    changes:
                      default: []
                      description: >-
                        Per-subscriber changes that make each one fit, applied
                        at its renewal.
                      type: array
                      items:
                        type: object
                        properties:
                          subscriptionId:
                            type: string
                          keepOnVersion:
                            description: Leave this subscriber on its version.
                            type: boolean
                            const: true
                          billingInterval:
                            type: string
                            enum:
                              - weekly
                              - monthly
                              - quarterly
                              - semiannual
                              - yearly
                          seatLimit:
                            description: Lower its seats to the version's limit at renewal.
                            type: boolean
                            const: true
                          removeAddonIds:
                            description: Add-ons it stops paying at renewal.
                            type: array
                            items:
                              type: string
                        required:
                          - subscriptionId
                    preview:
                      default: false
                      description: Check everything without publishing.
                      type: boolean
                  required:
                    - publishAs
                - type: object
                  properties:
                    publishAs:
                      type: string
                      const: experiment
                      description: >-
                        experiment starts an experiment with experimentCode,
                        sold with planCode plan:code. Starting an experiment
                        moves nobody, so it takes no move or changes.
                    experimentCode:
                      type: string
                      minLength: 1
                      description: The experiment's own code, after the plan's code.
                    preview:
                      default: false
                      description: Check everything without publishing.
                      type: boolean
                  required:
                    - publishAs
                    - experimentCode
                  additionalProperties: false
                - type: object
                  properties:
                    publishAs:
                      type: string
                      const: experiment_version
                      description: >-
                        experiment_version publishes the next version of the
                        experiment the draft started from.
                    move:
                      default: keep
                      description: >-
                        What happens to subscribers of earlier versions: keep
                        them, move them at their next renewal, or move them now
                        (features and balance change today, the price at
                        renewal). Nobody moves unless every one of them fits or
                        has a change below.
                      type: string
                      enum:
                        - keep
                        - period_end
                        - now
                    changes:
                      default: []
                      description: >-
                        Per-subscriber changes that make each one fit, applied
                        at its renewal.
                      type: array
                      items:
                        type: object
                        properties:
                          subscriptionId:
                            type: string
                          keepOnVersion:
                            description: Leave this subscriber on its version.
                            type: boolean
                            const: true
                          billingInterval:
                            type: string
                            enum:
                              - weekly
                              - monthly
                              - quarterly
                              - semiannual
                              - yearly
                          seatLimit:
                            description: Lower its seats to the version's limit at renewal.
                            type: boolean
                            const: true
                          removeAddonIds:
                            description: Add-ons it stops paying at renewal.
                            type: array
                            items:
                              type: string
                        required:
                          - subscriptionId
                    preview:
                      default: false
                      description: Check everything without publishing.
                      type: boolean
                  required:
                    - publishAs
      responses:
        '200':
          description: Success
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            RateLimit-Limit:
              description: Maximum number of requests allowed in the current window.
              schema:
                type: string
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlanDraftPublication'
              example:
                preview: false
                version:
                  id: plr_m3n4o5p6
                  label: '5'
                  number: 5
                  experiment: null
                  isCurrent: true
                  parentVersionId: null
                  subscriptionCount: 0
                  maxPlanCredits: null
                  maxPlanBalance: null
                  prices:
                    - id: pp_1a2b3c4d
                      billingInterval: monthly
                      price: 5500
                      includedCredits: null
                      includedBalance: 5000
                      regionalPrices:
                        - currency: mxn
                          price: 99900
                          includedBalance: null
                  createdAt: '2026-09-07T12:00:00.000Z'
                  object: plan_version
                moveId: pvm_1a2b3c4d
                subscribers:
                  moving: 180
                  keptOnVersion: 0
                  leftOut:
                    - subscriptionId: sub_7h8i9j0k
                      code: invalid_subscription_status
                      message: >-
                        A paused subscription cannot change versions. Only
                        active, trialing and past due subscriptions can.
        '400':
          description: Bad request
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  type: validation_error
                  code: validation_error
                  message: The request contains an invalid parameter.
                  doc_url: >-
                    https://commet.co/docs/api-reference/2026-08-27/errors/validation_error.md
        '401':
          description: Unauthorized
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  type: authentication_error
                  code: unauthorized
                  message: A valid API key is required.
                  doc_url: >-
                    https://commet.co/docs/api-reference/2026-08-27/errors/unauthorized.md
        '403':
          description: Forbidden
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  type: authentication_error
                  code: forbidden
                  message: You do not have permission to perform this operation.
                  doc_url: >-
                    https://commet.co/docs/api-reference/2026-08-27/errors/forbidden.md
        '404':
          description: Not found
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  type: not_found_error
                  code: not_found
                  message: The requested resource was not found.
                  doc_url: >-
                    https://commet.co/docs/api-reference/2026-08-27/errors/not_found.md
        '409':
          description: Conflict
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  type: conflict_error
                  code: idempotency_in_progress
                  message: >-
                    A request with this idempotency key is still being
                    processed.
                  doc_url: >-
                    https://commet.co/docs/api-reference/2026-08-27/errors/idempotency_in_progress.md
        '429':
          description: Too many requests
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            RateLimit-Limit:
              description: Maximum number of requests allowed in the current window.
              schema:
                type: string
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: string
            RateLimit-Reset:
              description: Seconds until the rate limit window resets.
              schema:
                type: string
            Retry-After:
              description: Seconds to wait before retrying the request.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  type: rate_limit_error
                  code: rate_limited
                  message: >-
                    Too many requests. Retry after the interval specified in the
                    Retry-After header.
                  doc_url: >-
                    https://commet.co/docs/api-reference/2026-08-27/errors/rate_limited.md
        '500':
          description: Internal server error
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  type: internal_error
                  code: internal_error
                  message: An unexpected error occurred while processing the request.
                  doc_url: >-
                    https://commet.co/docs/api-reference/2026-08-27/errors/internal_error.md
        '503':
          description: Service unavailable
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  type: internal_error
                  code: idempotency_unavailable
                  message: >-
                    Idempotency storage is temporarily unavailable. Retry with
                    the same idempotency key.
                  doc_url: >-
                    https://commet.co/docs/api-reference/2026-08-27/errors/idempotency_unavailable.md
components:
  headers:
    RequestId:
      description: >-
        Server-generated identifier that correlates this response with Platform
        logs.
      schema:
        type: string
        format: uuid
  schemas:
    PlanDraftPublication:
      type: object
      properties:
        preview:
          type: boolean
        version:
          anyOf:
            - type: object
              properties:
                id:
                  type: string
                  description: Public plan version ID.
                label:
                  type: string
                  description: >-
                    The plan's versions are numbered 1, 2, 3; an experiment's
                    own versions read with its code, such as price-test 2.
                number:
                  anyOf:
                    - type: integer
                      minimum: -9007199254740991
                      maximum: 9007199254740991
                    - type: 'null'
                  description: >-
                    The plan's version number. Null for a version that only
                    belongs to an experiment; an experiment version made current
                    takes the next number.
                experiment:
                  anyOf:
                    - type: object
                      properties:
                        code:
                          type: string
                          description: The experiment's code, as sold with planCode.
                        version:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                          description: Its number within the experiment.
                      required:
                        - code
                        - version
                      additionalProperties: false
                    - type: 'null'
                  description: The experiment this version belongs to, if any.
                isCurrent:
                  type: boolean
                  description: Whether new buyers get this version.
                parentVersionId:
                  description: The version this one was created from.
                  type:
                    - string
                    - 'null'
                subscriptionCount:
                  type: integer
                  minimum: -9007199254740991
                  maximum: 9007199254740991
                  description: Active, trialing, past due and paused subscriptions on it.
                maxPlanCredits:
                  anyOf:
                    - type: integer
                      minimum: -9007199254740991
                      maximum: 9007199254740991
                    - type: 'null'
                maxPlanBalance:
                  anyOf:
                    - type: integer
                      minimum: -9007199254740991
                      maximum: 9007199254740991
                    - type: 'null'
                prices:
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        type: string
                        description: Public plan price ID.
                      billingInterval:
                        type: string
                        enum:
                          - weekly
                          - monthly
                          - quarterly
                          - semiannual
                          - yearly
                          - one_time
                      price:
                        type: integer
                        minimum: -9007199254740991
                        maximum: 9007199254740991
                      includedCredits:
                        anyOf:
                          - type: integer
                            minimum: -9007199254740991
                            maximum: 9007199254740991
                          - type: 'null'
                      includedBalance:
                        anyOf:
                          - type: integer
                            minimum: -9007199254740991
                            maximum: 9007199254740991
                          - type: 'null'
                      regionalPrices:
                        type: array
                        items:
                          type: object
                          properties:
                            currency:
                              type: string
                            price:
                              type: integer
                              minimum: -9007199254740991
                              maximum: 9007199254740991
                            includedBalance:
                              anyOf:
                                - type: integer
                                  minimum: -9007199254740991
                                  maximum: 9007199254740991
                                - type: 'null'
                          required:
                            - currency
                            - price
                            - includedBalance
                          additionalProperties: false
                    required:
                      - id
                      - billingInterval
                      - price
                      - includedCredits
                      - includedBalance
                      - regionalPrices
                    additionalProperties: false
                createdAt:
                  type: string
                  format: date-time
              required:
                - id
                - label
                - number
                - experiment
                - isCurrent
                - parentVersionId
                - subscriptionCount
                - maxPlanCredits
                - maxPlanBalance
                - prices
                - createdAt
              additionalProperties: false
            - type: 'null'
          description: The published version. Null for a preview.
        moveId:
          description: The move of subscribers, if any.
          type:
            - string
            - 'null'
        subscribers:
          anyOf:
            - type: object
              properties:
                moving:
                  type: integer
                  minimum: -9007199254740991
                  maximum: 9007199254740991
                keptOnVersion:
                  type: integer
                  minimum: -9007199254740991
                  maximum: 9007199254740991
                needsAChange:
                  type: integer
                  minimum: -9007199254740991
                  maximum: 9007199254740991
                  description: >-
                    Left on their version because no change makes them fit; they
                    show among the subscribers that need a change.
                leftOut:
                  type: array
                  items:
                    type: object
                    properties:
                      subscriptionId:
                        type: string
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - subscriptionId
                      - code
                      - message
                    additionalProperties: false
              required:
                - moving
                - keptOnVersion
                - needsAChange
                - leftOut
              additionalProperties: false
            - type: 'null'
        object:
          type: string
          const: plan_draft_publication
        livemode:
          type: boolean
      required:
        - preview
        - version
        - moveId
        - subscribers
        - object
        - livemode
      additionalProperties: false
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              type: string
            code:
              type: string
            message:
              type: string
            param:
              type: string
            details: {}
            doc_url:
              type: string
          required:
            - type
            - code
            - message
            - doc_url
          additionalProperties: false
      required:
        - error
      additionalProperties: false
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.