pass_series.presale_opened
A finished season's holders were given first refusal on the next one.
When this fires
A season ticket ran its last date, the creator had linked a Next Series, and the holders-only window on that successor opened. Every eligible holder has been messaged with a link straight to it.
For the length of the window, nobody who did not hold the finished season can buy the successor — the portal and the bot both turn them away and say when it goes on general sale.
Who is invited is worked out now, not at checkout
Nothing is stamped onto a purchase when someone pays. Eligibility is resolved at the moment the presale opens, which is what lets a creator link a successor months after the season sold out and still reach everyone who bought it.
Required ability
project-subscription-plan:view — token must carry this at mint time to subscribe an endpoint to this event.
Payload
{
"id": "evt_01HX...",
"type": "pass_series.presale_opened",
"created_at": "2026-11-29T21:00:11Z",
"api_version": "2026-05-01",
"project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
"data": {
"plan_id": "c4e82f16-93a7-4d5b-b81c-6e0f27a94d3b",
"plan_name": "Autumn Season Ticket",
"successor_plan_id": "b3e70d21-5f94-4a6c-8127-cd4a91e6f508",
"eligible_holders": 38,
"presale_hours": 48,
"closes_at": "2026-12-01T21:00:11Z"
}
}Field reference
| Field | Type | Notes |
|---|---|---|
id | string ULID | Unique event id, prefixed evt_. Use for idempotent processing. |
type | string | Always pass_series.presale_opened for this event. |
created_at | ISO 8601 timestamp | Server-side emission time. |
api_version | string | Webhook API contract version. |
project_id | string UUID | Project this event belongs to. |
data.plan_id | string UUID | The season that just finished — the one whose holders are being invited. |
data.plan_name | string | Its name. |
data.successor_plan_id | string UUID | The season now reserved for them. |
data.eligible_holders | integer | How many holders were invited. |
data.presale_hours | integer | The window's length in hours, as the creator set it. |
data.closes_at | ISO 8601 timestamp | When the successor goes on general sale, always UTC. |
Caveats
- Fires once per season, ever. The window's opening is stamped, so a repeated sweep does not re-invite anybody. If you need to reach these people again, do it from your own side.
eligible_holderscounts everyone who bought the finished season and did not cancel it, whether or not their subscription has expired — by this point most have, which is the normal case and not a disqualification. Someone who cancelled partway through is excluded: they said they did not want the season.- It counts holders resolved for invitation, not messages Telegram accepted. A holder who has blocked the bot is included here and did not receive anything.
- Nothing closes the window explicitly. It lapses at
closes_atand the successor sells to everyone from then on, with no further event. - A successor with no
presale_hoursset produces no presale and no event.
Related events
pass_series.completed— each holder reaching the end of the finished season.plan.deactivated— the finished season coming off sale, withreason: series_exhausted.pass_series.purchased— a holder taking up the offer.- Pass series events overview — back to family overview.
How is this guide?