Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.
Webhooks

subscription.access_granted

Access started: the subscription grants access now, and did not just before. Its first access, a payment that gives access back after it ended, a comeback, a resume that comes after a cancellation ended. A renewal paid on time, or a resume while the paid period still runs, keeps access and sends none: only subscription.renewed or subscription.resumed.
POSTyour endpointsent by Mesub

Guide: Webhooks.

Payload

application/json
idstring

The event's id: the same in every delivery of it, to every endpoint and on every retry, and on Retrieve an event. webhook-id names the delivery, not the event.

objectenum<string>

Always event.

One ofevent

typeenum<string>

The event's name.

One ofsubscription.access_granted

created_atstring<date-time>

When Mesub wrote the event: never before the event that says why, which shares its change, nor before any event written earlier. detail.changed_at is when access changed, and the moment data describes.

networkenum<string>

The Solana network the subscription is on.

One ofdevnetmainnet-beta

api_versionenum<string>

The version of this contract data and detail are written in, as Mesub-Version names them. Set when the body is written, and kept with it.

One of2026-10-02

sequenceintegernot always present

Its place among the events of this subscription: 1, 2, 3... in the order they happened, two events of one change included. Deliveries may arrive in any order: apply a subscription's events in the order of sequence, and skip one lower than the last you applied for it. Absent on bodies written before Mesub numbered events, and on an event it could not number. An event without sequence cannot be placed: read the subscription's state now, with Retrieve a subscription or Check access, rather than applying its data.

dataobject

The subscription as it stood right after the change that raised this event, as Retrieve a subscription would have answered it then, however late the delivery. Events raised by the same change share it, and detail says what each event is about: a last retry that fails raises subscription.payment_failed and subscription.stopped, both showing status: stopped, the failure's cause in payment_failed's detail.

Show child attributesHide child attributes
idstring
statusenum<string>

Where it stands. Unlike GET /v1/access, may be expired: a checkout nobody signed. cancelled while the cancellation runs, ended once its end passed. For a few minutes after a plan's end date it can still read active or unpaid with access false, never ended with access true.

pausedboolean

True on a seat parked over the project's subscriber limit, its status unchanged. subscription.paused and subscription.unpaused are sent when it changes.

end_reasonenum<string> | null

Why it ended. Set only when status is ended. Null on every other status, and on an old subscription that ended with no reason recorded.

late_reasonenum<string> | null

Why a payment is late, as the last failed charge found it. Set only when status is unpaid, or stopped for the payment it stopped on. Null on every other status, and when Mesub cannot name the cause.

One ofinsufficient_balanceapproval_revokedauthority_closed

stop_reasonenum<string> | null

Why it stopped. Set only when status is stopped. Null on every other status, and on a subscription that stopped before Mesub recorded why. - out_of_retries: every retry of the missed payment failed. The subscription stays open on Solana: the subscriber can come back, or cancel it and close it once the period ends. - retry_deadline_passed: on the Free tier, the last day to pay the missed payment passed. - authority_closed: the wallet closed the account that lets Mesub charge it, for good. Nobody can charge this subscription again, and the subscriber cannot come back on it. They can close it now (closable) to get its deposit back.

One ofout_of_retriesretry_deadline_passedauthority_closed

accessboolean

Whether this subscription grants access now, as GET /v1/access answers it. False from the plan's end date on, whatever period was paid for.

payment_statusenum<string>

late while a charge missed and a retry is pending. none where nothing is being billed.

One ofpaidlatenone

blocked_sincestring<date-time> | null

Since when every charge of this subscription is held for a reason that is not the customer's. Null when none is. status and payment_status do not change for it, and Mesub keeps trying. access ends at access_until, and comes back with the first charge that goes through, with nothing for the customer to sign.

blocked_reasonenum<string> | null

Why the charges are held. Null with blocked_since. Nine values name the plan's own setup, yours to fix or, for the last two, the token issuer's to lift: puller_not_allowed (Mesub's puller was taken off the plan on chain), receiver_not_allowed (the receiving wallet is not among the plan's destinations), receiver_account_missing (it has no account for the plan's token, and nobody else can be paid), receiver_account_frozen (its account for the plan's token is frozen), receiver_account_needs_memo (that account requires a memo on every transfer in, which Mesub's charges do not carry), receiver_account_confidential_only (that account takes confidential transfers only), mint_gone (the token no longer exists), mint_paused (the token's issuer paused it: no payment in it moves, and Mesub charges again once the pause is lifted) and mint_transfer_hook (the token's issuer made its transfers run a program of theirs, which Mesub does not support yet: it charges again if the issuer removes it). mesub is any cause on Mesub's side: nothing for you to fix. Mesub may add a value: keep a default branch.

planstring | null

The plan's slug.

walletstring

The wallet that pays.

emailstring | null

Where the customer's notices go, as given at creation.

external_idstring | null

Your own id for the customer, as given at creation.

current_period_startstring<date-time> | null

The start of the period last paid for. Null before the first.

current_period_endstring<date-time> | null

The end of that period.

next_charge_atstring<date-time> | null

The next charge on a running subscription. Null when a retry is pending instead, and when the plan's end date leaves no charge to make: a subscription in its last period has none.

next_retry_atstring<date-time> | null

The next retry on a late one. Null when the plan ends before it.

retry_deadlinestring<date-time> | null

On Free only: when retries by hand close for the missed period, or the plan's end date when that comes first.

next_retry_numberinteger | null

Which retry comes at next_retry_at: 1 is the first after the missed charge. Null unless Mesub retries on its own, and when next_retry_at is null.

retries_allowedinteger | null

How many retries the missed period gets. Null unless Mesub retries on its own.

access_untilstring<date-time> | null

When access ends unless a charge renews it. Never later than the plan's end date, where access stops for everybody. Null while access is false. On a subscription whose charges are blocked (blocked_since), the date access ends if nothing is collected by then: the end of the period after the one paid for, or sooner when the cause is the plan's setup, the end of the grace the plan gives a late payment.

closableboolean

Whether the subscriber can close the subscription on Solana now, which returns its deposit to their wallet: a cancellation whose end has passed, a subscription that ended with its plan, or one stopped with stop_reason authority_closed. As Mesub last knew the chain: the close reads the chain again, and may still be refused.

closable_fromstring<date-time> | null

When the subscriber can close it, if not now: the end of the period a cancellation set. Null when closable is true, and when no date is known yet: a running subscription, or one stopped with out_of_retries, must be cancelled first. As Mesub last knew the chain, like closable.

created_atstring<date-time>

When it was reserved.

confirmed_atstring<date-time> | null

When its first payment landed.

detailobject

The event's own fields.

Show child attributesHide child attributes
changed_atstring<date-time>

When access actually changed: the moment data describes. A date that passed with nothing else happening (the end of a paid period, of a cancellation, of the plan) is that date to the millisecond, even when Mesub wrote the event later. Keep, per subscription, the latest changed_at you applied, and the access it set.

blocked_reasonenum<string> | nullnot always present

Why the subscription's charges were held when access changed, in the words of data.blocked_reason. Null when they were not. Mesub may add a value: keep a default branch. Absent on an event written before Mesub added it.

access_untilstring<date-time> | nullnot always present

On a subscription whose charges are held (blocked_reason), the date access ends if nothing is collected by then. Null otherwise: a charge renews it. Absent on an event written before Mesub added it.

testbooleannot always present

True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise.

Request example
{ "id": "evt_cmg1x0evt0002", "object": "event", "type": "subscription.access_granted", "created_at": "2026-01-01T00:00:05.012Z", "network": "mainnet-beta", "api_version": "2026-10-02", "sequence": 2, "data": { "id": "cmg1x0abc0001", "status": "active", "paused": false, "end_reason": null, "late_reason": null, "stop_reason": null, "access": true, "payment_status": "paid", "blocked_since": null, "blocked_reason": null, "plan": "pro", "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", "email": "[email protected]", "external_id": "user_123", "current_period_start": "2026-01-01T00:00:00.000Z", "current_period_end": "2026-01-31T00:00:00.000Z", "next_charge_at": "2026-01-31T00:00:00.000Z", "next_retry_at": null, "retry_deadline": null, "next_retry_number": null, "retries_allowed": null, "access_until": "2026-01-31T00:00:00.000Z", "closable": false, "closable_from": null, "created_at": "2026-01-01T00:00:00.000Z", "confirmed_at": "2026-01-01T00:00:05.000Z" }, "detail": { "changed_at": "2026-01-01T00:00:05.000Z", "blocked_reason": null, "access_until": null } }