resource_kinds

Each kind of place a connector can gate — its labels, its icon, how access to it is granted, which plan shapes it sells, and whether it supports early admission, mirroring and in-place upgrades.

A resource is the core's decision to sell access to a space, a place the installation administers on the platform. resource_kinds lists the kinds of place the connector knows how to gate. A connector that gates nothing (a pure payment or messaging connector) leaves the list out; a connector that declares access_control must list at least one kind (access.declares_kinds).

"resource_kinds": [
    {
        "kind": "channel",
        "label": "Channel",
        "portal_label": "Channel",
        "icon": "megaphone",
        "grant_mode": "bearer_link",
        "supports_early_admission_hold": true,
        "mirrorable": true,
        "plan_kinds": ["one_time", "recurring", "pass", "pass_series"]
    }
]

The fields

FieldRequiredMeaning
kind^[a-z][a-z0-9_-]*$, unique within the connector. Stored on a resource as <key>:<kind> (telegram:channel), the value the API returns as kind and the ResourceKind value object carries.
labelWhat creators see: in the "Link a resource" picker, on resource rows, in plan editors. A translation key.
portal_labelWhat members see on the portal and in confirmations. Often the same word; sometimes friendlier ("Private room" for a room). A translation key.
iconA Heroicons outline name (megaphone, user-group, users, home, clipboard-document-check), drawn beside the kind wherever the label appears.
grant_modeHow the connector gives a member access to a place of this kind; one of the four modes below.
supports_early_admission_holdWhether a dated grant (a pass whose window has not opened) can be issued early and held at the door until the window opens. Default false. Requires the early_admission_hold capability.
mirrorableWhether a standby place of this kind can receive a live copy of every post from the place it stands by for (RecoverySupport::mirror()). Default false. Requires recovery_mirror.
upgrades_fromAnother kind of the same connector that a place of this kind can be an in-place upgrade of. A resource sold as that kind may move onto a place of this kind in a recovery swap or take one as its standby; any other pair of kinds is refused, because the plans that sell the resource promised one kind of place. Must name a kind declared in the same file.
plan_kindsThe shapes of plan a place of this kind can be sold under: one_time, recurring, pass, pass_series. Every shape when omitted. The directory renders these as the four-row checklist on the kind's card, so a connector whose places cannot be left by force says so honestly rather than leaving creators to find out at the first cancellation.

What a kind can sell

plan_kinds is a promise to creators, and the loader holds it to three rules: the list is never empty (a kind that sells nothing is not a resource kind), a pass_series needs pass (a series bundles other plans' passes), and a kind with supports_early_admission_hold: true sells pass (the hold exists for a pass whose window has not opened). The order in the file does not matter; the SDK spells the list in the enum's order, one-time, recurring, pass, series, so two manifests never list the same set differently.

ValueThe creator can sellDeclare it when
one_timeOne payment for access that never renews, a lifetime membership included.Almost always; the only thing it needs is a way in.
recurringAccess billed each cycle and ended when the member stops paying.The connector can revoke access on its own, so a lapsed member is really removed.
passA ticket to one dated access window.Access can be given and taken at a set time, whether or not the connector can hold a member at the door first.
pass_seriesOne payment for a curated slate of other plans' passes.The kind sells pass.

The four grant modes

The mode decides what the core stores as a grant's reference, what AccessController::grant() is expected to do, and what a member is shown.

ModeThe grant isReference storedThe member sees
bearer_linkA personal, single-use link the connector mints; whoever presents it is let in. Telegram's invite link that creates a join request the bot approves.The link itself, unique per connector.The link, in the confirmation message and on the portal's membership card ("Join …").
membershipThe installation adds the account to the place directly. A forum's private board, a chat the bot can add people to.The connector's handle, or null."You now have access", and the connector's grant_action slot if it fills one.
roleThe installation gives the account a role that unlocks the place. Discord's role gate.guild:role:user, repeatable across grants.The role appears on the platform; nothing to tap.
creator_taskNothing the platform can do: the creator must act by hand (add the person, ship a perk). The core opens a creator task and tells the member their organiser will act.Null until the task is completed."Your organiser will add you"; the grant turns granted when the creator completes the task.

A bearer_link reference is unique per connector (a join request finds the purchase behind the link it arrived on through Core\Grants::findByReference()); a role reference may legitimately repeat across two concurrent subscriptions of the same member, and the core's access policy treats that as legal.

Early admission

A pass sells access to a place for a dated window. When the window has not opened yet, the core asks the connector to grant() with opensAt set. A kind with supports_early_admission_hold: true (and the early_admission_hold capability) may pre-issue the grant and answer held: true, so the member can already present the link and wait at the door; when the window opens the core calls admit() and the connector lets them in. A kind without it is granted when the window opens, and the plan editor's copy about early admission is hidden for plans built on it.

The Telegram kinds

"resource_kinds": [
    {"kind": "channel", "label": "Channel", "portal_label": "Channel", "icon": "megaphone", "grant_mode": "bearer_link", "supports_early_admission_hold": true, "mirrorable": true, "plan_kinds": ["one_time", "recurring", "pass", "pass_series"]},
    {"kind": "group", "label": "Group", "portal_label": "Group", "icon": "user-group", "grant_mode": "bearer_link", "supports_early_admission_hold": true, "plan_kinds": ["one_time", "recurring", "pass", "pass_series"]},
    {"kind": "supergroup", "label": "Supergroup", "portal_label": "Supergroup", "icon": "users", "grant_mode": "bearer_link", "supports_early_admission_hold": true, "upgrades_from": "group", "plan_kinds": ["one_time", "recurring", "pass", "pass_series"]}
]

Telegram cannot add anyone to anything, so every kind is a bearer_link; a join request waits at the door, so every kind supports early admission and every kind sells all four plan shapes; only a channel is mirrorable, because a channel is a feed of posts the bot can copy and a group is a conversation it cannot. A supergroup upgrades_from a group because Telegram converts a group into a supergroup in place, so a resource sold as a group may be swapped onto the supergroup it became.

What the kit checks

manifest.resource_kinds: every entry has a non-empty label, portal_label and icon. access.declares_kinds: a connector that declares access_control lists at least one kind. The loader refuses a kind outside its pattern, a duplicate kind, a grant_mode outside the four, a plan_kinds value outside the four or a list that breaks the three rules above, and an upgrades_from naming a kind the file does not declare.

How is this guide?

On this page

Subscriby is a product designed by you — for you.

No boardroom full of executives deciding what we ships next. Our roadmap always shaped by you with your feedback.

Share feedback or a request