Plans

Update a plan

The same discriminated shape as create, with every field optional. Omit kind to keep the plan's current kind; a block you send must match it. Where a cross-field rule needs a value you did not send, the billing cycle behind a lifetime count check, the currency behind the price floor, the eligibility flags behind the exclusivity check, the plan's stored value stands in, so a partial update is validated against the plan it will actually produce.

  • resources is optional; omitting it preserves the existing links, sending it replaces them.
  • Every update of a plan on sale queues its push to the payment methods that keep a product catalogue, so a changed price or name reaches Stripe, PayPal, CoinPayments and Razorpay within a minute.
  • sales_cap may be set, raised, lowered or cleared with null; any change restarts sales_cap_sold at 0.
  • pass.slots replaces the whole schedule and rebuilds unsold future windows; pass.windows adds dated windows and never replaces.
  • Setting an active plan's price to 0 on a Free account is refused; editing a zero-priced plan that is off sale is allowed on any account.
  • sales_cap_sold, position and paused are read-only.

Answers 200 with the whole plan after the change.

patch/v1/projects/{project}/plans/{plan}

Request

  • Base URL: https://api.subscriby.net
  • URL: https://api.subscriby.net/v1/projects/{project}/plans/{plan}
  • Auth: HTTP bearer

Path parameters

projectstring uuid required

The project, resolved by the route binder.

planstring uuid required

The plan, resolved within the project by the route binder.

Headers

Idempotency-Keystring uuid required

A key unique to this operation, such as a fresh UUID. The same key replays the original 2xx response for 24 hours (with Idempotent-Replay: true), so a retry after a timeout never repeats the write; the same key with a different body is refused with 409.

Request body

kind'subscription' | 'pass' | 'pass_series'

What a purchase of a creator's plan actually buys. Plan shape had no discriminator before this. It was inferred from pass_enabled plus recurring plus billing_cycle, which is why a third shape kept falling through filters written as where('pass_enabled', false): a boolean cannot express three things, so every query site had to remember which combination it meant and one of them always forgot. A match over this enum with no default is exhaustive, so a fourth shape later fails loudly at every site that has to care rather than silently taking whichever branch happened to be last.

subscription <br/> Access starts at payment and runs on a billing cycle.
pass <br/> One purchase buys one of the plan's own scheduled access windows.
pass_series <br/> One purchase buys a curated slate of other plans' access windows.
namestring

The name buyers see, 5 to 255 characters, unique within the project without regard to case or surrounding spaces. Required on create.

descriptionstring nullable

The pitch under the name, up to 1,000 characters. HTML is filtered to a safe subset before it is stored.

currency_idstring

The id of a currency that at least one of the project's active payment methods can charge. Required on create.

pricenumber

What one purchase costs: at least the $1.00 USD equivalent in currency_id at the current rate, or exactly 0 for a free plan, which only a Starter or Growth account may put on sale. Required on create.

activeboolean

Whether the plan is on sale. Defaults to true on create; prefer the publish and unpublish endpoints to change it later.

sales_capinteger nullable

Pause the plan after this many purchases, 1 to 100,000; null for no limit. Changing it restarts sales_cap_sold at 0.

resourcesstring[]

Ids of the project's resources the plan grants: at least one, required on create, except on kind: pass_series, where it is optional and names a lounge open for the whole span. Omit on an update to keep the existing links.

Example request

{
  "billing": {
    "billing_cycle": "month",
    "trial_type": "plan"
  }
}

Response

The plan after the change.

Example response

{
  "data": {
    "id": "c4e82f16-93a7-4d5b-b81c-6e0f27a94d3b",
    "project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
    "kind": "subscription",
    "name": "Premium Monthly",
    "description": "Unlimited access to the research channel.",
    "price": "29.00",
    "currency": {
      "id": "8f27a0d4-63be-4915-8c07-1a5d9e34b628",
      "iso": "USD",
      "symbol": "$"
    },
    "active": true,
    "sales_cap": 10,
    "sales_cap_sold": 3,
    "cadence": "Per Month",
    "resources": [
      {
        "id": "b73c5f21-9d80-4a6e-8215-4f70ce13a9d6",
        "name": "Research channel",
        "kind": "telegram:supergroup",
        "connector": "telegram"
      }
    ],
    "billing": {
      "billing_cycle": "month",
      "billing_cycle_count": 1,
      "recurring": true,
      "trial_days": 7,
      "trial_type": "plan"
    },
    "pass": {
      "timezone": "America/New_York",
      "schedule_mode": "repeating",
      "recurrence": "weekly",
      "sales_cutoff_minutes": 60,
      "sales_cutoff_anchor": "before_start",
      "slots": [
        {
          "start_time": "09:00",
          "duration_minutes": 840
        }
      ],
      "upcoming_windows": [
        {
          "id": "3d5a8c72-b016-4e94-8fa7-61c209d4e738",
          "starts_at": "2026-09-20T13:00:00Z",
          "ends_at": "2026-09-21T03:00:00Z",
          "status": "scheduled"
        }
      ]
    },
    "pass_series": {
      "timezone": "America/New_York",
      "prevent_overlaps": true,
      "sales_cutoff_anchor": "before_start",
      "seat_cap": 50,
      "seats_taken": 37,
      "seats_remaining": 13,
      "starts_at": "2026-09-20T13:00:00Z",
      "ends_at": "2026-11-29T22:00:00Z",
      "window_count": 10,
      "presale_hours": 48,
      "windows": [
        {
          "id": "3d5a8c72-b016-4e94-8fa7-61c209d4e738",
          "plan_id": "0b8e6a2f-4c1d-4e3a-9f52-7d6c1b2a3e45",
          "plan_name": "Match Day Pass",
          "starts_at": "2026-09-20T13:00:00Z",
          "ends_at": "2026-09-20T16:00:00Z",
          "status": "scheduled"
        }
      ],
      "rules": [
        {
          "source_plan_id": "0b8e6a2f-4c1d-4e3a-9f52-7d6c1b2a3e45",
          "kind": "date_range",
          "from_at": "2026-09-01T00:00:00Z",
          "to_at": "2026-12-01T00:00:00Z"
        }
      ]
    },
    "created_at": "2026-05-18T10:05:00Z",
    "updated_at": "2026-05-18T10:05:00Z"
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.