Create a plan
POST takes the same shape it returns: kind plus the one matching block. Sending a block that does not match the kind is refused with VALIDATION_FAILED naming the offending key; silently ignoring it would let a caller believe they had set a billing cycle on a pass. Answers 201 with the plan in the same discriminated shape the read endpoint shows for each kind. A plan created on sale (active: true, the default) is queued for the push to the payment methods that keep a product catalogue (Stripe, PayPal, CoinPayments, Razorpay), so it is sellable through them within a minute; a draft is pushed when it is published.
Create a subscription
curl -X POST https://api.subscriby.net/v1/projects/$PROJECT_ID/plans \
-H "Authorization: Bearer $SUBSCRIBY_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"kind": "subscription",
"name": "Premium Monthly",
"currency_id": "8f27a0d4-63be-4915-8c07-1a5d9e34b628",
"price": 29.00,
"resources": ["b73c5f21-9d80-4a6e-8215-4f70ce13a9d6"],
"billing": { "billing_cycle": "month", "billing_cycle_count": 1, "trial_days": 7 }
}'
Create a pass
curl -X POST https://api.subscriby.net/v1/projects/$PROJECT_ID/plans \
-H "Authorization: Bearer $SUBSCRIBY_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"kind": "pass",
"name": "Sunday Slate Pass",
"currency_id": "8f27a0d4-63be-4915-8c07-1a5d9e34b628",
"price": 15.00,
"resources": ["b73c5f21-9d80-4a6e-8215-4f70ce13a9d6"],
"pass": {
"timezone": "America/New_York",
"schedule_mode": "repeating",
"recurrence": "weekly",
"sales_cutoff_minutes": 60,
"sales_cutoff_anchor": "before_start",
"slots": [
{ "weekday": 4, "start_time": "19:00", "duration_minutes": 180 },
{ "weekday": 0, "start_time": "09:00", "duration_minutes": 840 }
]
}
}'
Sending pass.slots replaces the whole schedule and rebuilds future windows. Windows a customer has already bought keep their original times and are never moved or deleted; only unsold future windows are regenerated. pass.windows is different: it adds explicitly dated windows for schedule_mode: fixed and never replaces anything.
Create a pass series
A series needs window ids, and those come from windows that already exist. Read them from pass.upcoming_windows on the source plan, from the pass windows endpoints, or from the list_pass_windows MCP tool.
curl -X POST https://api.subscriby.net/v1/projects/$PROJECT_ID/plans \
-H "Authorization: Bearer $SUBSCRIBY_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"kind": "pass_series",
"name": "Autumn Season Ticket",
"currency_id": "8f27a0d4-63be-4915-8c07-1a5d9e34b628",
"price": 99.00,
"pass_series": {
"prevent_overlaps": true,
"seat_cap": 50,
"presale_hours": 48,
"window_ids": ["3d5a8c72-b016-4e94-8fa7-61c209d4e738", "9c1f4e27-5a8b-4d63-b2e0-8f7a6c5d4e31"],
"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" }
]
}
}'
resources is optional on a series only. Every other kind must link at least one resource, or a purchase buys nothing. A series is the exception: each of its windows grants that window's own plan's resources, so a series with none still delivers exactly what was sold. Anything you do link here is a lounge, open for the whole span.
Never put a slate window's resource in a series resources. A lounge is granted at purchase with no window and kept until the season ends. Send a resource that one of your slate windows already opens and every holder is handed it permanently the moment they pay: the dates it was scheduled for stop gating anything, and a season ticket becomes a permanent key to that channel. The API accepts it, because a genuinely permanent room is a legitimate thing to sell. It is simply almost never what was meant. Keep resources for a holders-only room nothing on the slate opens; the slate's own channels are already granted by their own pass plans on their own dates. The dashboard warns when the two overlap. Over the API, checking is yours to do.
Rules keep working after the save: a matching window scheduled later is absorbed into the slate and granted to everyone already holding the series, at no charge. That emits pass_series.leg_added. Handpicked window_ids never grow on their own. The two compose; most real seasons use both.
Validation
The field-by-field rules are on each request field below. The rules that cross fields:
- name must be unique within the project, compared without regard to case or surrounding spaces; a duplicate is refused on name.
- price must be at least the $1.00 USD equivalent in currency_id, converted at the current rate. Payment providers reject dust amounts, so anything below that is unbuyable and is refused; the error names the minimum in both the plan's currency and USD.
- price of exactly 0 publishes a free plan. Plans priced at zero can only be sold by a Starter or Growth account, never on Free: the platform fee is a share of what you charge, so a zero-priced plan earns nothing to share. On Free the call is refused on price. The rule enforced is that a plan may not be simultaneously active and priced at 0 on a creator whose plan cannot sell one, so an update that leaves a zero-priced plan off sale is allowed.
- currency_id must be supported by at least one active payment method on the project.
- At most one of eligibility.newcomers_only, eligibility.customers_only and eligibility.churned_only may be true.
- resources is required with at least one id, except on kind: pass_series, where it is optional and means a lounge.
- billing.billing_cycle_count must be 1 when billing.billing_cycle is lifetime; billing.recurring is refused as true when the currency is a crypto or platform currency.
- pass.sales_cutoff_anchor: before_end requires pass.sales_cutoff_minutes of at least 5 and under the shortest slot's duration_minutes, and is refused outright when that shortest slot is 5 minutes or less. before_first_end and before_last_start are series-only anchors and are rejected on a pass plan, where a lone window is both the first and the last.
- pass_series.window_ids needs at least two windows unless a rule will supply them; every id must belong to a pass plan on this project; with prevent_overlaps on, two windows that run at the same time are refused. pass_series.rules[].take is required when kind is next_n, because a count rule with no count is incomplete rather than "all of them". pass_series.successor_plan_id must be another kind: pass_series plan on the same project.
Request
- Base URL: https://api.subscriby.net
- URL: https://api.subscriby.net/v1/projects/{project}/plans
- Auth: HTTP bearer
Path parameters
The project, resolved by the route binder.
Headers
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
Example request
{
"billing": {
"billing_cycle": "month",
"trial_type": "plan"
}
}Response
The new plan.
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.