Guide: Webhooks.
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.
Always event.
One ofevent
The event's name.
One ofsubscription.cancelled
When the event happened: the moment data describes.
The Solana network the subscription is on.
One ofdevnetmainnet-beta
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
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.
Limits:at least 1
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.
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.
One ofpendingactiveunpaidcancelledstoppedendedfailedexpiredsuperseded
True on a seat parked over the project's subscriber limit, its status unchanged. subscription.paused and subscription.unpaused are sent when it changes.
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.
One ofcancelledplan_removedplan_replacedplan_endedclosed_elsewhereclosed
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
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
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.
late while a charge missed and a retry is pending. none where nothing is being billed.
One ofpaidlatenone
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.
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.
One ofpuller_not_allowedreceiver_not_allowedreceiver_account_missingreceiver_account_frozenreceiver_account_needs_memoreceiver_account_confidential_onlymint_gonemint_pausedmint_transfer_hookmesub
The plan's slug.
The wallet that pays.
Where the customer's notices go, as given at creation.
Your own id for the customer, as given at creation.
The start of the period last paid for. Null before the first.
The end of that period.
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.
The next retry on a late one. Null when the plan ends before it.
On Free only: when retries by hand close for the missed period, or the plan's end date when that comes first.
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.
How many retries the missed period gets. Null unless Mesub retries on its own.
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.
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.
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.
When it was reserved.
When its first payment landed.
Empty on this event.
True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise.
The delivery's id: one per event and per endpoint, the same on every retry of that delivery and on a resend from the dashboard. Deduplicate on it. Two endpoints receiving one event get two ids.
When this attempt was sent, in Unix seconds: new on every retry, and signed with the body. Refuse one too far from your own clock.
v1, then the base64 HMAC-SHA256 of webhook-id.webhook-timestamp.body, keyed with the endpoint's signing secret (the part after whsec_, base64 decoded), the Standard Webhooks way. It may hold several, space separated: two while a regenerated secret's overlap runs, the new secret's first. Accept the delivery when any one matches your secret. Check it over the raw bytes received, before parsing them.
Received. Anything else, a redirect included, counts as a failure and is retried.
{ "id": "evt_cmg1x0evt0001", "object": "event", "type": "subscription.cancelled", "created_at": "2026-01-15T00:00:00.000Z", "network": "mainnet-beta", "api_version": "2026-10-02", "sequence": 3, "data": { "id": "cmg1x0abc0001", "status": "cancelled", "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": null, "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": "2026-01-31T00:00:00.000Z", "created_at": "2026-01-01T00:00:00.000Z", "confirmed_at": "2026-01-01T00:00:05.000Z" }, "detail": {} }