Version

Grants

The access ledger as a connector may read and write it — finding a grant by id or by the reference you issued, what a summary tells you, and when a connector records a grant itself.

Subscriby\Connector\Core\Grants. The ledger is the core's record of who should be where: one grant per subscription, resource and pass window, with a mode, a state and the reference your AccessController issued. The connector holds the platform's side (the invite link, the role); this contract lets it read the ledger by that reference and, in a migration, write to it.

Reads

MethodReturnsNull when
find(string $id)?GrantSummaryNo grant has that UUID.
findByReference(string $connector, string $reference)?GrantSummaryThe connector never issued that reference.
listLiveForSubscription(string $subscriptionId)list<GrantSummary>Never null; empty when the purchase holds nothing live.
listLiveForIdentity(IdentityRef $identity)list<GrantSummary>Never null; empty when the account holds nothing live, in any project.
listWaitingForSubscription(string $subscriptionId)list<WaitingPlace>Never null; empty when nothing waits or no purchase has that id.

findByReference() is how a join request finds the purchase behind the link it arrived on: the platform tells you which invite link was used, the ledger tells you whose grant it is and whether it is still granted, held or already revoked, and your handler admits or refuses accordingly. It works for any mode whose reference is unique per connector (a bearer_link); a role reference may repeat across concurrent subscriptions and is read by id instead.

listLiveForIdentity() is for a platform that keeps a per-person fact beside the account rather than per place (Discord's Linked Roles metadata: active, member since, live plans): after every grant and revoke the connector reads what the account is owed right now, wherever it bought it, and writes the fact from the ledger rather than from the one change it just made.

listWaitingForSubscription() answers the places a purchase waits for because the member holds no account on their connector (the pending_identity rows), each a WaitingPlace: the ResourceRef, the connector and its connectorName, the member's words for the place (kindBadge, as Resources::memberKindBadge() words it), the windowId and opensAt of a dated place, and connectDoor, a signed portal address that lives half an hour and signs the member in with the connect flow for that connector already open. List them beside the doors still open, with a button on the door, so a member who bought on your platform learns what waits on another and is one tap from the account it needs. The same list rides on MembershipSummary::$waiting.

GrantSummary

FieldMeaning
idThe grant row.
projectId, subscriptionId, resourceId, windowIdWhose access, to what, for which pass window (null outside passes).
modeGrantMode: BearerLink, Membership, Role, CreatorTask.
stateGrantState: PendingIdentity, Pending, Held, Granted, Revoked, Failed.
identityIdThe account that holds it, null while it waits for one.
connector, referenceYour connector and what you issued.
grantedAt, revokedAtWhen.
memberIdThe member the purchase belongs to, null while the purchase has none; member() answers the MemberRef the identity links take, so a join that arrived on a reference can link the account that used it to the right member.

The states

StateMeans
PendingIdentityThe purchase is settled but the member has no identity on this connector yet; materialises when one is linked.
PendingThe grant is being issued.
HeldPre-issued for a pass whose window has not opened; released by admit().
GrantedLive.
RevokedEnded: cancelled, expired, paused, banned, uninstalled, or superseded by a reissue.
FailedThe connector could not issue it; the failure kind is on the row and the creator is shown a creator-actionable one.

record()

$ref = $grants->record(new GrantRecord(
    subscriptionId: $subscription->id,
    resourceId: $resource->id,
    mode: GrantMode::BearerLink,
    state: GrantState::Granted,
    windowId: null,
    identityId: $identity->id,
    connector: 'example',
    reference: $inviteLink,
    payload: [],
    grantedAt: $issuedAt,
));

Idempotent on the subscription, the resource and the window: it writes the grant or rewrites the one the purchase already holds for that resource and date. The core writes grants itself from your AccessController::grant() result, so a connector calls record() directly only when it learns of a grant outside that flow.

Read the ledger, never your own copy

A connector that keeps its own "who is in" table will disagree with the ledger the first time a subscription is paused from the dashboard. Keep the reference; ask the ledger.

markHeld(), markGranted() and rereference()

Three moves on a grant the core already holds, each by the grant's id and each answering the updated GrantSummary (or null for an unknown id). markHeld() records that the member used their grant before its window opened and the platform holds them at the door; markGranted() records that the platform let them in, the move a join handler makes when the reference it issued comes back used. rereference(string $id, string $reference) replaces your handle on the grant without changing its standing, for a platform where what was issued becomes something else once used: an invite link a member joined through becomes the role they now hold, and the reference revocation will read must follow it.

How is this guide?

Last updated on

Version

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

For AI agents: llms.txt · llms-full.txt