transaction_expires_at estimates when they run out, last_valid_block_height says it. The terms live five minutes. Have the terms signed first, then ask for a fresh transaction when the wallet is ready to sign it.external_id, else the email, else nobody. Another customer naming the same wallet gets a subscription of its own: only the one the wallet signs for lands.costs.rent at zero.terms.message says that the last charge is in full and that access stops when the plan ends. When the plan ends before a second charge can be made, it says the charge is a single one.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 plan's slug: lowercase letters, digits and single hyphens.
The wallet that signs the terms and the transaction, and pays. A base58 Solana address.
Your own id for this customer, handed back as given. It is how GET /v1/access finds them later, whichever wallet pays.
Limits:1 to 255 characters
Where the customer's notices go. Not verified by Mesub. Trimmed and lowercased.
Limits:at most 254 characters
The subscription, pending, and what the wallet signs.
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
Keep it: every later call names it.
One ofpending
Base64, signed by nobody. The wallet signs it without sending it.
The block height after which the transaction can no longer land.
When last_valid_block_height is expected to pass: an estimate, about half a minute after the answer. Mesub reads it from how fast block heights went (skipped slots included), taking the faster of the last half hour and the last 30 seconds, and counts the blocks the chain's tip is ahead of what it read, so the estimate errs early, not late, while the chain keeps its pace. The height is the truth: rebuild the transaction when the chain refuses it with an expired blockhash, before this time as after it. Use this for a countdown, or to ask for a fresh transaction before it runs out.
What subscribing costs the wallet in SOL, in lamports, as strings.
A deposit the customer gets back when the subscription is closed.
Null when the customer already has an authority for that token.
Spent, not returned.
Plain text, one fact per line. The wallet signs it as is, before the transaction.
Five minutes after the terms were handed out. Submit before it.
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" \ -H "Authorization: Bearer $MESUB_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "plan": "pro", "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", "external_id": "user_42", "email": "[email protected]" }'
{ "subscription": { "id": "cmg1x0abc0001", "status": "pending" }, "transaction": "gAIBBg0Xq3ZkT9yLmR4vP7wJ8sN2aHcVbE5uYtKfDgQxWz1oMiCj6rA0lSnUhBdXe2GqFpYv7mNcTz4RwK8sLjH5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDBAUGBwgJCgsM", "last_valid_block_height": "301245678", "transaction_expires_at": "2026-01-01T00:00:33.000Z", "costs": { "rent": { "subscription": "1969680", "authority": "1628640", "total": "3598320" }, "fee": { "signatures": 2, "per_signature": "5000", "priority": "0", "total": "10000" }, "total": "3608320" }, "terms": { "message": "Mesub: the terms of the subscription...\nAmount: 9.99 USDC every month\n...", "expires_at": "2026-01-01T00:05:00.000Z" } }