transaction_expired: nothing was sent, and the same submit sent again cannot land. Ask for a fresh transaction, have the wallet sign it, and sign the terms again if their message changed.Limit: 60 calls a minute per API key.Guide: Subscribe from your server.
The version of this contract the call is written against. Without it, the call is served as the first version, 2026-10-02. Any other value is refused with 400 unsupported_version.
One of2026-10-02
The subscription's id, as POST /v1/subscriptions answered it.
Example:cmg1x0abc0001
The transaction as the wallet's signTransaction answered it, in base64. Signed, not sent.
Limits:at most 2048 characters
What the wallet's signMessage answered over terms.message, in base58, 64 bytes once decoded (86 to 88 characters for a real one).
Limits:at most 88 characters
The subscription as it now stands: active when it landed. With a reason and its reason_code, nothing was settled: still pending when nothing landed, failed when what landed is not what Mesub built. The codes this route answers today: transaction_failed, transaction_expired, nothing_landed, subscription_mismatch, subscription_remade, earlier_subscription_running, not_our_transaction, dev_not_running.
The version the answer is written in: the one asked for, or 2026-10-02 when the call named none, or one this API does not serve.
One of2026-10-02
The id Mesub gave this request, on every answer, errors included, and in request_id on an error body. Quote it to Mesub support to find the request in our logs. Written by Mesub on each request: one sent with the request is ignored.
The calls this route allows the API key in a minute, or the IP of a call without a valid key. Sent on every answer the rate limit counted.
Limits:at least 1
What is left of them in the current minute.
Limits:at least 0
Seconds until the count starts again.
Limits:at least 0
One subscription to one of your project's plans.
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.
Present only when nothing was settled, or when what landed is not what Mesub built, and always with reason_code. A sentence for a person, which Mesub may reword: branch on reason_code, never on this.
Why, for a program: present exactly when reason is. Stable and in snake case, never reworded. An open family rather than a closed list: Mesub adds codes without a new version, so keep a default branch. Known today:
- not_our_transaction: the signature is not of a transaction Mesub co-signed over terms the wallet signed for this subscription.
- transaction_failed: the transaction landed and the chain refused it: nothing it held happened.
- transaction_expired: its blockhash ran out before it landed: it never will.
- nothing_landed: on a submit, no subscription stands at its address yet: it stays pending.
- subscription_not_on_chain: on a cancel or a resume, the subscription no longer exists on chain.
- subscription_mismatch: what landed names another wallet, plan, amount or billing period than the subscription reserved, which is now failed.
- dev_not_running: Mesub's own Dev plan landed cancelled or part paid, and is now failed.
- earlier_subscription_running: a comeback paid while the subscription it came back over ran again: Mesub was told and sorts it out.
- subscription_remade: on a submit, it landed, and the subscription was closed and made again without Mesub since: now failed.
- cancellation_not_landed: the subscription stands on chain with no cancellation.
- resume_not_landed: the subscription stands on chain still cancelled.
- close_not_landed: the subscription still stands on chain.
- subscription_changed: the subscription moved on while the cancellation was read, and was left as it is.
- subscription_not_cancelled: on a resume, the subscription is no longer cancelled: nothing to resume.
- cancelled_subscription_remade: on a resume, the subscription cancelled was closed and made again without Mesub since: it stays cancelled.
- resume_unverified: on a resume, the subscription on chain cannot be told to be the one cancelled: it stays cancelled.
Bearer <API key>. The project's API key, which starts with SUB_. Keep it on your server. Where to get it.
curl -X POST "https://api.mesub.io/v1/subscriptions/cmg1x0abc0001/submit" \ -H "Authorization: Bearer $MESUB_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "transaction": "gAIBBg0Xq3ZkT9yLmR4vP7wJ8sN2aHcVbE5uYtKfDgQxWz1oMiCj6rA0lSnUhBdXe2GqFpYv7mNcTz4RwK8sLjH5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDBAUGBwgJCgsM", "terms_signature": "2Jd8sPq7XkVnT4mWcR9yHbFz3LgNuA6eKtQx5CvMjD1rYpZwSoEiGhUa8BfT2nXkVq4mWcR7yHbFz9LgNuA3eKtQ" }'
{ "subscription": { "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_42", "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" } }