Get a plan
One plan in the same discriminated shape as the list. kind names the one block the plan carries, billing, pass or pass_series; the other two are absent. A plan of another project is 404 RESOURCE_NOT_FOUND, because {plan} is resolved within {project}.
Common fields
Present on every kind.
| Field | Type | Notes |
|---|---|---|
| kind | string | subscription, pass or pass_series. Names the one block below it. |
| price | string | What one purchase costs. On a pass that buys one window; on a series it buys the whole slate, once. |
| currency | object | { id, iso, symbol }. |
| cadence | string | The human-readable duration string: "Per Month", "Per 3 Hours", "For all 10 passes". |
| eligibility | object | Audience restrictions. The first three are mutually exclusive. |
| resources | array | Present when the relation is loaded: each resource's id, name, kind (manual or connector:kind) and connector. On a series these are the lounge, not the thing being sold. |
Why cadence exists. Both the portal and the bot already compute exactly this string, and every integrator without it reinvents it, wrongly for a dated plan, printing "1 Month" beside a three-hour window. Use it rather than deriving a duration from the cycle fields.
kind: subscription
| Field | Type | Notes |
|---|---|---|
| billing.billing_cycle | string | day, week, month, year, lifetime. |
| billing.billing_cycle_count | integer | 1–99. Must be 1 when billing_cycle is lifetime. |
| billing.recurring | boolean | Rejected as true for crypto or platform currencies. |
| billing.disabled_renewal | boolean | Charges once, then lapses rather than renewing. |
| billing.trial_days | integer | 0–365. |
| billing.trial_cardless | boolean | Whether the trial starts without a payment method. |
| billing.trial_type | string | project or plan: whose trial rule applies. |
kind: pass
A plan that owns its own dated windows and sells one per purchase.
| Field | Type | Notes |
|---|---|---|
| pass.timezone | string | IANA zone the schedule was authored in. Render window times in this, never UTC. |
| pass.schedule_mode | string | repeating or fixed. |
| pass.recurrence | string | null | daily, weekly or monthly. Null in fixed mode. |
| pass.recurrence_ends_at | timestamp | null | When window generation stops. Renamed, see the note below. |
| pass.sales_cutoff_minutes | integer | null | Stops sales this many minutes before the moment sales_cutoff_anchor names. |
| pass.sales_cutoff_anchor | string | before_start or before_end. Never null: a plan that never set it reads as before_start. before_first_end and before_last_start are series-only and refused here. |
| pass.slots[].start_time | string HH:MM | Local wall-clock time in timezone. |
| pass.slots[].duration_minutes | integer | Each slot carries its own length, so one plan can mix a 3-hour and a 14-hour window. |
| pass.upcoming_windows[] | array | Present only when the relation is loaded. Timestamps are UTC. The pass windows endpoints list and manage a plan's dates. |
series_ends_at was renamed to recurrence_ends_at. Same field, same meaning: how long this plan keeps generating windows from its recurrence. It never had anything to do with a Pass Series, but now that a series is a real plan kind, a field called series_ends_at sitting on a pass read as "when this plan's series ends", which is a different thing and one that does not exist here. Update any reader to the new key; there is no alias.
kind: pass_series
A season ticket. It owns no windows: it points at windows that already exist on your pass plans, which is the whole distinction from kind: pass.
| Field | Type | Notes |
|---|---|---|
| pass_series.timezone | string | Borrowed from the source plans; a series has no zone of its own. A series whose sources disagree is refused at authoring time. |
| pass_series.prevent_overlaps | boolean | Refuses to absorb a window clashing with one already on the slate. |
| pass_series.sales_cutoff_minutes | integer | Measured against the whole season, not one window. |
| pass_series.sales_cutoff_anchor | string | Four anchors, earliest deadline first. before_start closes before the first window opens. before_first_end closes during that opening window. before_last_start closes as the last window opens, so a buyer always gets one whole window. before_end closes as the last window ends. |
| pass_series.seat_cap | integer | null | Concurrent holder limit. null is unlimited. |
| pass_series.seats_taken | integer | Holders currently counted against the cap. |
| pass_series.seats_remaining | integer | null | null when uncapped. |
| pass_series.starts_at | timestamp | null | First window's start, UTC. |
| pass_series.ends_at | timestamp | null | Last window's end, UTC. |
| pass_series.window_count | integer | Slate length. Capped at 120. |
| pass_series.successor_plan_id | string | null | The next season, offered to holders first when this one finishes. |
| pass_series.presale_hours | integer | null | How long that offer is held for holders only. 1–8760. |
| pass_series.windows[] | array | The slate. Each entry names the plan the window belongs to; a series can mix several. |
| pass_series.windows[].added_by_rule | boolean | true when a rule absorbed it rather than the creator picking it by hand. |
| pass_series.rules[] | array | Automatic inclusion rules. kind is date_range or next_n; take is required on next_n. |
| pass_series.blackout_window_ids | array | Windows a rule matches but the creator has permanently excluded. |
Request
- Base URL: https://api.subscriby.net
- URL: https://api.subscriby.net/v1/projects/{project}/plans/{plan}
- Auth: HTTP bearer
Path parameters
The project, resolved by the route binder.
The plan, resolved within the project by the route binder.
Response
The 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.