{
  "openapi": "3.1.0",
  "info": {
    "title": "Mesub API",
    "version": "2026-10-02",
    "description": "Recurring payments on Solana, read and driven from your own server. This document is the contract of `/v1` and of the webhooks: what it does not name is not part of it.\n\n## Calls\n\n- Every route takes the project's API key as a bearer token, `Authorization: Bearer SUB_...`, and never in the query string. Everything is scoped to the project the key belongs to.\n- Fields are in snake case, statuses and reasons in lowercase. Dates are ISO 8601 strings in UTC, with milliseconds. Token amounts are strings in the token's smallest unit, and so is every other integer that may not fit in a double (`last_valid_block_height`, lamports).\n- A field nobody set is `null`, never left out, except where a field says otherwise (`reason` and `reason_code` on a confirmation, `attempts` on an access answer).\n- A query or body field a route does not know is refused, `400 invalid_request`.\n- Every answer, errors included, carries `Mesub-Request-Id`, the id Mesub gave the request (`req_` and 24 letters or digits). Log it beside your own; quote it to Mesub support to find the request in our logs. Mesub writes it: an id sent with the request is ignored.\n\n## Versions\n\n- Send `Mesub-Version` on every call to name the version of this contract your code is written against. The one version served today is `2026-10-02`.\n- A call without the header is served as the first version, `2026-10-02`, for good: an integration written without it never changes under its feet.\n- A value that is not, to the letter, a version this API serves (another date, `latest`, an empty header) is refused with `400 unsupported_version`, whose message lists the versions served. The API key and the rate limit are checked first: a wrong key reads `401` whatever version it names.\n- Every answer under `/v1`, errors included, says in `Mesub-Version` the version it is written in: the one asked for, or `2026-10-02` when the call named none or one this API does not serve.\n\n## Compatibility\n\n- Within a version, this contract only grows. Without a new `Mesub-Version`, an answer may gain a field, an enum may gain a value, an answer may gain an error code, an attempt may gain a reason, a confirmation may gain a `reason_code`, and a webhook may gain an event type or a field. Your code must ignore what it does not know: never fail on an unknown field, and treat an unknown enum value, code, reason or event as \"something else\".\n- Anything else takes a new version: removing or renaming a field, changing its type or its meaning, making an optional request field required, or changing the status of an error.\n- Before the first integrator, exceptions were made once, in this version: values moved to snake case (an attempt's `reason`, for example `insufficient_balance` and `program_4`), and each fact got one word: the codes `plan_deleted` and `plan_rebuilt` become `plan_removed` and `plan_replaced`, an attempt's `plan_gone`, `delegation_gone` and `subscription_cancelled` become `plan_removed`, `closed_elsewhere` and `cancelled`, and `end_reason` `authority_closed` becomes `closed_elsewhere`; and webhook bodies took the shape of an event (see Webhooks).\n\n## Rate limits\n\n- Counted per API key, per route, over a minute: 1,000 calls to the routes that read, 60 to the routes that build a transaction, read the chain or write (each route says its own in `x-mesub.limit`). A call without a valid key is counted against its IP, 120 a minute.\n- Counted in the memory of the server that answers: the limits are a floor, not a promise of what gets through.\n- `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (seconds) come on every answer the limit counted. Past the limit: `429 rate_limited` with `Retry-After`, in seconds.\n\n## Errors\n\n- Every error under `/v1` answers the same body: `statusCode`, `error` (the status's name), `code`, `message`, `retryable` and `request_id`. `message` is a sentence for a person, or a list of them when the body or the query failed validation; it may be reworded any day.\n- Branch on `code`. A code is never moved to another status or reused for another cause, and is renamed only in a new version (see Compatibility). The codes each answer may carry are listed under it, in `x-codes`.\n- `retryable` says whether the same request, sent again unchanged, may succeed later. `Retry-After`, when present, says how long to wait.\n- Beside what each route lists: a path or a method that does not exist answers `404 not_found`; a body over 100 kB answers `413 payload_too_large`; a fault of ours answers `500 internal_error`, retryable, since every route is safe to call twice.\n\n## Webhooks\n\n- Each delivery is a `POST` of a JSON body, `{ id, object, type, created_at, network, api_version, sequence, data, detail }`: `id` is the event's, `sequence` its place among the subscription's events, `data` the subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) would have answered it then, and `detail` what the event is about. A delivery sent by hand from the dashboard also carries `test: true`.\n- `data` is the subscription as it stood right after the change that raised this event; 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 is in `payment_failed`'s `detail`. Do not assume the order events arrive in: apply a subscription's events in the order of their `sequence` (1, 2, 3...), skipping one lower than the last you applied, or read [Retrieve a subscription](/reference/subscriptions/retrieve) for the state now. An event without `sequence` (one written before Mesub numbered events, or one it could not number) cannot be placed: read the subscription's state now rather than applying its `data`.\n- Access: `subscription.access_granted` and `subscription.access_revoked` are sent exactly when the access a subscription grants changes (`data.access`), whatever made it change, a date passing included. `detail.changed_at` is when access changed (a date that passed, to the millisecond, even when the event is written seconds or hours later), and the moment `data` describes; `created_at` is when Mesub wrote the event, never before an event written earlier, so [List events](/reference/events/list) serves it in the order it was written. One event per change, never two. Drive your customer's access from these two: `sequence` orders one subscription's events, the access of the highest one applied stands; the other events say why. A renewal paid on time, or a resume while the paid period runs, changes nothing and sends neither. `detail.blocked_reason` and `detail.access_until` say whether the charges were held then, and when access ended or ends.\n- Transitions: a cancellation that reached its end sends `subscription.ended` (`end_reason: cancelled`, `detail.changed_at` its expiry) once the chain shows the cancellation still in place past its expiry, after the `subscription.access_revoked` of that expiry; a resume seen late can still follow an `access_revoked`; a seat parked over the project's subscriber limit sends `subscription.paused`, and `subscription.unpaused` when it is back. Each once per change, ordered by `sequence` beside the access events. `subscription.created` carries its first payment in `detail.first_payment`.\n- [List events](/reference/events/list) serves the same bodies, byte for byte once parsed, over the last 30 days: catch up there on what an endpoint missed.\n- Bodies written before `available_since` (see [List events](/reference/events/list)), and the few written during the deploy that brought this shape by a server still on the previous version, keep the shape they were sent in: `{ type, created_at, data }`, with `detail` inside `data`, no `id`, and `data` as it was at the delivery's first attempt. A retry or a resend of one sends it unchanged.\n- An event whose capture failed is held: its deliveries are not sent and it is missing from [List events](/reference/events/list) until Mesub recovers it. Mesub is alerted, and never sends or serves an event with a later state.\n- Signed the Standard Webhooks way: `webhook-signature` is `v1,` and the base64 HMAC-SHA256 of `webhook-id.webhook-timestamp.body`, keyed with the endpoint's secret (the part after `whsec_`, base64 decoded). Check it over the raw bytes received.\n- The header may list several signatures, space separated. When an endpoint's secret is regenerated with an overlap (up to 24 hours, chosen then), the old secret keeps signing beside the new one until the overlap ends: every attempt in that window carries two, the new secret's first, and an attempt after it, a retry included, carries the new one alone. Verify against each and accept the delivery when any one matches; the Standard Webhooks libraries and `@mesub/node` do.\n- `webhook-id` names the delivery: one per event and per endpoint, the same on every retry of it and on a resend from the dashboard. Deduplicate on it. Two endpoints of a project receiving one event get two ids.\n- The body is written once, at the event's first attempt, and every retry and every endpoint gets the same bytes; `webhook-timestamp` is the attempt's own.\n- Answer with a 2xx within 10 seconds. Anything else, a redirect included, is a failure: the delivery is tried again 1 minute, 5 minutes, 30 minutes and 2 hours later, then every 12 hours, eleven attempts over about three days. An endpoint that fails for three days with no success in between is turned off.\n\n## What may change without a new version\n\nWithout a new `Mesub-Version`, Mesub may add:\n\n- fields to an answer or to a webhook body;\n- values to an enum (a `status`, a reason, an `outcome`), and to the open families that are not enums (an attempt's `reason`);\n- error codes, to any route;\n- webhook events;\n- optional parameters and optional body fields.\n\nYour code must ignore a field it does not know, and keep a default branch on every enum, reason and code. Anything else, renaming or removing a field, changing its type or its meaning, making something optional required, takes a new version, and the versions already served keep answering as they did.\n\nDecided before the first integrator, and made without a new version:\n\n- reasons and `detail` values move to snake case (`insufficient-balance` becomes `insufficient_balance`, `program:4` becomes `program_4`, `grace-ended` becomes `retry_deadline_passed`);\n- the error code `plan_deleted` becomes `plan_removed`, and `plan_rebuilt` becomes `plan_replaced`, the words `end_reason` already uses;\n- an attempt's `reason` takes the word `end_reason` uses for the same fact: `plan_gone` becomes `plan_removed`, `subscription_cancelled` becomes `cancelled`, and `delegation_gone` becomes `closed_elsewhere`;\n- `end_reason` `authority_closed` becomes `closed_elsewhere`, the subscription account closed outside Mesub: `authority_closed` names only the subscription authority closed for good, as `late_reason` does;\n- a webhook body gains `id`, `object`, `network` and `api_version`, `detail` moves out of `data` to the top, and `data` describes the subscription at the event rather than at the first attempt. Bodies written before `available_since` keep their shape.\n\n`network` keeps `mainnet-beta`: the name Solana gives its cluster."
  },
  "servers": [
    {
      "url": "https://api.mesub.io"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Access",
      "description": "Whether a customer may use a plan."
    },
    {
      "name": "Plans",
      "description": "The plans of the project, to build a pricing page."
    },
    {
      "name": "Subscriptions",
      "description": "Subscribe a customer from your server, and read subscriptions back."
    },
    {
      "name": "Cancel, resume, close",
      "description": "Each action is two calls with the wallet in between: build, then confirm."
    },
    {
      "name": "Project",
      "description": "The project an API key belongs to."
    },
    {
      "name": "Events",
      "description": "What your webhooks carried over the last 30 days, to catch up on what an endpoint missed."
    },
    {
      "name": "Webhooks",
      "description": "What Mesub posts to your endpoints."
    }
  ],
  "paths": {
    "/v1/access": {
      "get": {
        "tags": ["Access"],
        "summary": "Check access",
        "operationId": "checkAccess",
        "description": "Whether a customer may use a plan, and what explains the answer. Always 200 for a customer, subscribed or not: a customer who never subscribed answers `access: false` with `status: \"none\"`. The 404 is about the slug, never about them.\n\nLeave `plan` out to get every plan of the project the customer has anything on.\n\nOn a plan with an end date, nobody has access past that date: `access` is false from the plan's end on and `access_until` is never later, whatever period was paid for. The subscription turns `ended` within minutes of it, so `status` can still read `active` or `unpaid` for that long with `access` false. Gate on `access`.\n\nA charge may be held for a reason that is not the customer's: the answer then carries `blocked_since` and `blocked_reason`, both null otherwise. `status` and `payment_status` stay as they were, and Mesub keeps trying. `access` is never true past `access_until` on a blocked subscription. [Lifecycle](/docs/lifecycle#blocked-charges) says what each reason asks of you.",
        "x-mesub": {
          "href": "reference/access/check",
          "title": "Check a customer's access",
          "limit": "1,000 calls a minute per API key",
          "guide": {
            "label": "Check access",
            "href": "docs/access"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\n// true or false, cached, safe during an outage\nif (await mesub.hasAccess({ external_id: 'user_42' }, 'pro')) {\n    // serve what is paid\n}\n\n// the whole answer\nconst answer = await mesub.access({ external_id: 'user_42' }, 'pro');\n\n// every plan they have anything on\nconst { plans } = await mesub.accessList({ external_id: 'user_42' });"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "external_id",
            "in": "query",
            "description": "Your own id for the customer, as you gave it when the subscription was created. It follows the person whichever wallet pays. Name the customer by exactly one of `wallet`, `external_id` and `email`.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "pattern": "^[^\\u0000-\\u001f\\u007f]*$"
            },
            "example": "user_42"
          },
          {
            "name": "wallet",
            "in": "query",
            "description": "The wallet that pays, a base58 Solana address. Name the customer by exactly one of `wallet`, `external_id` and `email`.",
            "schema": {
              "type": "string",
              "pattern": "^[1-9A-HJ-NP-Za-km-z]{32,44}$"
            },
            "x-example": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
          },
          {
            "name": "email",
            "in": "query",
            "description": "The email given when the subscription was created. Never verified by Mesub. Found however it was typed: it is trimmed and lowercased. Name the customer by exactly one of `wallet`, `external_id` and `email`.",
            "schema": {
              "type": "string",
              "format": "email",
              "maxLength": 254
            },
            "x-example": "ada@example.com"
          },
          {
            "name": "plan",
            "in": "query",
            "description": "The plan's slug, resolved inside the project the key belongs to. Left out, the answer lists every plan the customer has anything on.",
            "schema": {
              "type": "string",
              "pattern": "^(?=.*[a-z])[a-z0-9]+(?:-[a-z0-9]+)*$"
            },
            "example": "pro"
          },
          {
            "name": "attempts",
            "in": "query",
            "description": "Whether to send the last charges back, five at most. Takes `true`, `1`, `false` or `0`.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The answer for one plan when `plan` is given, the list of plans otherwise.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AccessAnswer"
                    },
                    {
                      "$ref": "#/components/schemas/AccessList"
                    }
                  ]
                },
                "examples": {
                  "With a plan": {
                    "value": {
                      "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                      "plan": "pro",
                      "subscription_id": "cmg1x0abc0001",
                      "access": true,
                      "status": "active",
                      "paused": false,
                      "end_reason": null,
                      "late_reason": null,
                      "stop_reason": null,
                      "payment_status": "paid",
                      "blocked_since": null,
                      "blocked_reason": null,
                      "subscribed_since": "2026-01-01T00:00:05.000Z",
                      "first_subscribed_at": "2026-01-01T00:00:05.000Z",
                      "current_period_end": "2026-01-31T00:00:00.000Z",
                      "cancelled_at": null,
                      "access_until": "2026-01-31T00:00:00.000Z",
                      "closable": false,
                      "closable_from": null,
                      "next_charge_at": "2026-01-31T00:00:00.000Z",
                      "next_retry_at": null,
                      "retry_deadline": null,
                      "revalidate_after": 300
                    }
                  },
                  "Blocked": {
                    "value": {
                      "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                      "plan": "pro",
                      "subscription_id": "cmg1x0abc0001",
                      "access": true,
                      "status": "active",
                      "paused": false,
                      "end_reason": null,
                      "late_reason": null,
                      "stop_reason": null,
                      "payment_status": "paid",
                      "blocked_since": "2026-01-31T00:02:00.000Z",
                      "blocked_reason": "puller_not_allowed",
                      "subscribed_since": "2026-01-01T00:00:05.000Z",
                      "first_subscribed_at": "2026-01-01T00:00:05.000Z",
                      "current_period_end": "2026-01-31T00:00:00.000Z",
                      "cancelled_at": null,
                      "access_until": "2026-02-03T00:06:00.000Z",
                      "closable": false,
                      "closable_from": null,
                      "next_charge_at": "2026-01-31T00:12:00.000Z",
                      "next_retry_at": null,
                      "retry_deadline": null,
                      "revalidate_after": 300
                    }
                  },
                  "Not subscribed": {
                    "value": {
                      "wallet": null,
                      "plan": "pro",
                      "subscription_id": null,
                      "access": false,
                      "status": "none",
                      "paused": false,
                      "end_reason": null,
                      "late_reason": null,
                      "stop_reason": null,
                      "payment_status": "none",
                      "blocked_since": null,
                      "blocked_reason": null,
                      "subscribed_since": null,
                      "first_subscribed_at": null,
                      "current_period_end": null,
                      "cancelled_at": null,
                      "access_until": null,
                      "closable": false,
                      "closable_from": null,
                      "next_charge_at": null,
                      "next_retry_at": null,
                      "retry_deadline": null,
                      "revalidate_after": 10
                    }
                  },
                  "Without a plan": {
                    "value": {
                      "plans": [
                        {
                          "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                          "plan": "pro",
                          "subscription_id": "cmg1x0abc0001",
                          "access": true,
                          "status": "active",
                          "paused": false,
                          "end_reason": null,
                          "late_reason": null,
                          "stop_reason": null,
                          "payment_status": "paid",
                          "blocked_since": null,
                          "blocked_reason": null,
                          "subscribed_since": "2026-01-01T00:00:05.000Z",
                          "first_subscribed_at": "2026-01-01T00:00:05.000Z",
                          "current_period_end": "2026-01-31T00:00:00.000Z",
                          "cancelled_at": null,
                          "access_until": "2026-01-31T00:00:00.000Z",
                          "closable": false,
                          "closable_from": null,
                          "next_charge_at": "2026-01-31T00:00:00.000Z",
                          "next_retry_at": null,
                          "retry_deadline": null,
                          "revalidate_after": 300
                        }
                      ],
                      "revalidate_after": 300
                    }
                  }
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "A parameter is missing, malformed, or not one this route knows.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Ask about exactly one of wallet, external_id or email.",
                  "error": "Bad Request",
                  "code": "invalid_request",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "plan_not_found",
                "when": "No plan of your project under that slug.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No plan of yours is named pro-max.",
                  "error": "Not Found",
                  "code": "plan_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        }
      }
    },
    "/v1/plans": {
      "get": {
        "tags": ["Plans"],
        "summary": "List plans",
        "operationId": "listPlans",
        "description": "Every plan of the project that is published and named by a slug, active or sunset, sorted by slug. Drafts and closed plans are left out.",
        "x-mesub": {
          "href": "reference/plans/list",
          "title": "List the plans of a project",
          "limit": "1,000 calls a minute per API key"
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst plans = await mesub.plans.list();"
          }
        ],
        "responses": {
          "200": {
            "description": "The plans. Maybe empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["plans"],
                  "properties": {
                    "plans": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Plan"
                      }
                    }
                  }
                },
                "example": {
                  "plans": [
                    {
                      "slug": "pro",
                      "name": "Pro",
                      "description": "Everything in Free, plus analytics.",
                      "project_name": "Fraise",
                      "logo_url": "https://cdn.mesub.io/logos/3f9c2a7e5b1d4c68.webp",
                      "amount": "9990000",
                      "amount_display": "9.99",
                      "amount_usd": "9.99",
                      "decimals": 6,
                      "symbol": "USDC",
                      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                      "token": {
                        "symbol": "USDC",
                        "name": "USD Coin",
                        "logo_url": "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v/logo.png",
                        "verified": true
                      },
                      "period_hours": 720,
                      "network": "mainnet-beta",
                      "status": "active",
                      "available": true,
                      "ends_at": null
                    },
                    {
                      "slug": "supporter",
                      "name": "Supporter",
                      "description": null,
                      "project_name": "Fraise",
                      "logo_url": "https://cdn.mesub.io/logos/3f9c2a7e5b1d4c68.webp",
                      "amount": "3000000000",
                      "amount_display": "30000",
                      "amount_usd": "0.45",
                      "decimals": 5,
                      "symbol": null,
                      "mint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
                      "token": {
                        "symbol": "Bonk",
                        "name": "Bonk",
                        "logo_url": "https://arweave.net/hQiPZOsRZXGXBJd_82PhVdlM_hACsT_q6wqwf5cSY7I",
                        "verified": true
                      },
                      "period_hours": 720,
                      "network": "mainnet-beta",
                      "status": "active",
                      "available": true,
                      "ends_at": null
                    }
                  ]
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Mesub-Version must be one of the versions this API serves: 2026-10-02.",
                  "error": "Bad Request",
                  "code": "unsupported_version",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          }
        ]
      }
    },
    "/v1/plans/{slug}": {
      "get": {
        "tags": ["Plans"],
        "summary": "Retrieve a plan",
        "operationId": "retrievePlan",
        "description": "One plan: its name, its price in the smallest unit and as a person counts it, its period, its token, its status, its end date, and whether it takes new subscribers now.\n\nA plan past its end date takes no new subscriber: `available` is false, and [Create a subscription](/reference/subscriptions/create) answers `plan_ended`.",
        "x-mesub": {
          "href": "reference/plans/retrieve",
          "limit": "1,000 calls a minute per API key"
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst plan = await mesub.plans.retrieve('pro');"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "The plan's slug: lowercase letters, digits and single hyphens.",
            "schema": {
              "type": "string",
              "pattern": "^(?=.*[a-z])[a-z0-9]+(?:-[a-z0-9]+)*$"
            },
            "example": "pro"
          }
        ],
        "responses": {
          "200": {
            "description": "The plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                },
                "example": {
                  "slug": "pro",
                  "name": "Pro",
                  "description": "Everything in Free, plus analytics.",
                  "project_name": "Fraise",
                  "logo_url": "https://cdn.mesub.io/logos/3f9c2a7e5b1d4c68.webp",
                  "amount": "9990000",
                  "amount_display": "9.99",
                  "amount_usd": "9.99",
                  "decimals": 6,
                  "symbol": "USDC",
                  "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                  "token": {
                    "symbol": "USDC",
                    "name": "USD Coin",
                    "logo_url": "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v/logo.png",
                    "verified": true
                  },
                  "period_hours": 720,
                  "network": "mainnet-beta",
                  "status": "active",
                  "available": true,
                  "ends_at": null
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "A parameter is missing, malformed, or not one this route knows.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "That is not a plan slug.",
                  "error": "Bad Request",
                  "code": "invalid_request",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "plan_not_found",
                "when": "No plan of your project under that slug.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No plan of yours is named pro-max.",
                  "error": "Not Found",
                  "code": "plan_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions": {
      "post": {
        "tags": ["Subscriptions"],
        "summary": "Create a subscription",
        "operationId": "createSubscription",
        "description": "Reserves a subscription to one of your plans for the wallet named, and builds what that wallet signs: the terms, as a message, then the transaction, without sending it. Hand both to [Submit](/reference/subscriptions/submit).\n\nThe transaction lives 150 blocks, about half a minute: `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](/reference/subscriptions/transaction) when the wallet is ready to sign it.\n\nCalled again for the same plan, wallet and customer while nothing has landed, it answers the same subscription with a fresh transaction. The customer is the `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.\n\nA wallet whose subscription to this plan stopped comes back over its existing authorisation: a new subscription, with `costs.rent` at zero.\n\nOn a plan with an end date, `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.",
        "x-mesub": {
          "href": "reference/subscriptions/create",
          "limit": "60 calls a minute per API key",
          "confirm": "This reserves a real subscription in the project the key belongs to. It charges nothing until a wallet signs and you submit.",
          "guide": {
            "label": "Subscribe from your server",
            "href": "docs/subscribe"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst { subscription, transaction, terms, costs } =\n    await mesub.subscriptions.create({\n        plan: 'pro',\n        wallet, // the wallet that signs and pays\n        external_id: 'user_42', // optional: your own id for this customer\n        email: 'ada@example.com', // optional: where their notices go\n    });"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["plan", "wallet"],
                "properties": {
                  "plan": {
                    "type": "string",
                    "pattern": "^(?=.*[a-z])[a-z0-9]+(?:-[a-z0-9]+)*$",
                    "description": "The plan's slug: lowercase letters, digits and single hyphens."
                  },
                  "wallet": {
                    "type": "string",
                    "pattern": "^[1-9A-HJ-NP-Za-km-z]{32,44}$",
                    "description": "The wallet that signs the terms and the transaction, and pays. A base58 Solana address."
                  },
                  "external_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255,
                    "description": "Your own id for this customer, handed back as given. It is how `GET /v1/access` finds them later, whichever wallet pays.",
                    "pattern": "^[^\\u0000-\\u001f\\u007f]*$"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "Where the customer's notices go. Not verified by Mesub. Trimmed and lowercased."
                  }
                }
              },
              "example": {
                "plan": "pro",
                "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                "external_id": "user_42",
                "email": "ada@example.com"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The subscription, `pending`, and what the wallet signs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscribeResult"
                },
                "example": {
                  "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"
                  }
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "A parameter is missing, malformed, or not one this route knows.",
                "retryable": false
              },
              {
                "code": "mint_not_on_chain",
                "when": "The plan's token does not exist on chain.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": ["wallet must be a base58 Solana address"],
                  "error": "Bad Request",
                  "code": "invalid_request",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "plan_not_found",
                "when": "No plan of your project under that slug.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No plan under slug pro-max",
                  "error": "Not Found",
                  "code": "plan_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "The request does not fit the current state.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfterWithSomeCodes"
              }
            },
            "x-codes": [
              {
                "code": "already_subscribed",
                "when": "This wallet already holds a running subscription to this plan.",
                "retryable": false
              },
              {
                "code": "subscription_cancelled",
                "when": "The wallet's subscription to this plan is cancelled, and runs to the end of its period.",
                "retryable": false
              },
              {
                "code": "payment_pending",
                "when": "A payment on this plan failed and is being retried: the wallet has to be topped up.",
                "retryable": false
              },
              {
                "code": "insufficient_balance",
                "when": "The wallet holds less of the plan's token than the first period costs.",
                "retryable": false
              },
              {
                "code": "insufficient_sol",
                "when": "The wallet holds less SOL than the transaction's network fee and the rent of the accounts it opens. The message says how much it holds and needs.",
                "retryable": false
              },
              {
                "code": "seat_cap_reached",
                "when": "The project is at its tier's subscriber limit.",
                "retryable": false
              },
              {
                "code": "plan_not_on_chain",
                "when": "The plan was never published: it is still a draft.",
                "retryable": false
              },
              {
                "code": "plan_sunset",
                "when": "The plan takes no new subscribers.",
                "retryable": false
              },
              {
                "code": "plan_ended",
                "when": "The plan is past its end date.",
                "retryable": false
              },
              {
                "code": "plan_replaced",
                "when": "Another plan now stands at its address.",
                "retryable": false
              },
              {
                "code": "plan_mismatch",
                "when": "The plan on chain is not the one Mesub recorded.",
                "retryable": false
              },
              {
                "code": "receiver_account_missing",
                "when": "Nobody the plan pays has an account for its token.",
                "retryable": false
              },
              {
                "code": "subscription_stopped",
                "when": "The wallet's subscription stopped after missed payments, and its authorisation is cancelled.",
                "retryable": false
              },
              {
                "code": "subscription_terms_changed",
                "when": "The plan's terms changed since the wallet's subscription stopped.",
                "retryable": false
              },
              {
                "code": "comeback_in_flight",
                "when": "A return to this plan may still land. Ask again later.",
                "retryable": true
              },
              {
                "code": "comeback_landed",
                "when": "A return to this plan landed and is being confirmed.",
                "retryable": false
              },
              {
                "code": "comeback_period_rolling",
                "when": "A return asked within two minutes of the end of a billing period. Wait for `Retry-After`.",
                "retryable": true
              },
              {
                "code": "subscription_changed",
                "when": "Another call took the waiting subscription over meanwhile. Asking again settles it.",
                "retryable": true
              },
              {
                "code": "receiver_account_frozen",
                "when": "The account the plan pays for its token is frozen, so the first charge would fail.",
                "retryable": false
              },
              {
                "code": "receiver_account_needs_memo",
                "when": "The account the plan pays for its token requires a memo on every transfer in, which Mesub's charges do not carry.",
                "retryable": false
              },
              {
                "code": "receiver_account_confidential_only",
                "when": "The account the plan pays for its token takes confidential transfers only, so the first charge would fail.",
                "retryable": false
              },
              {
                "code": "mint_paused",
                "when": "The plan's token is paused by its issuer: no payment in it moves until the issuer lifts the pause.",
                "retryable": false
              },
              {
                "code": "mint_transfer_hook",
                "when": "The plan's token now runs its issuer's own program on every transfer, which Mesub does not support yet.",
                "retryable": false
              },
              {
                "code": "plan_removed",
                "when": "The plan is gone from the chain.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 409,
                  "message": "You are already subscribed to this plan.",
                  "error": "Conflict",
                  "code": "already_subscribed",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "pending_cap_reached",
                "when": "Too many subscriptions wait for a signature, in the project or for this customer. `Retry-After` says when one frees up.",
                "retryable": true
              },
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "Too many subscriptions are waiting for a signature in this project. Try again later.",
                  "error": "Too Many Requests",
                  "code": "pending_cap_reached",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "503": {
            "description": "The Solana network did not answer.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "network_unavailable",
                "when": "The Solana network does not answer. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 503,
                  "message": "The Solana network did not answer. Try again in a few seconds.",
                  "error": "Service Unavailable",
                  "code": "network_unavailable",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          }
        ]
      },
      "get": {
        "tags": ["Subscriptions"],
        "summary": "List subscriptions",
        "operationId": "listSubscriptions",
        "description": "A customer's subscriptions to your project's plans, newest first: what a \"my subscriptions\" page needs. A customer with nothing is not an error: `data` is empty.\n\nExpired checkouts are listed too, and `access` says they grant nothing. A subscription replaced by a newer one of the same wallet (`superseded`) is left out, though its id still answers on [Retrieve](/reference/subscriptions/retrieve).",
        "x-mesub": {
          "href": "reference/subscriptions/list",
          "title": "List a customer's subscriptions",
          "limit": "1,000 calls a minute per API key",
          "guide": {
            "label": "Read a subscription back",
            "href": "docs/subscribe#read-a-subscription-back"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst { data, has_more } = await mesub.subscriptions.list({\n    external_id: 'user_42',\n});\n\n// every page\nfor await (const sub of mesub.subscriptions.listAll({ external_id: 'user_42' })) {\n    // ...\n}"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "external_id",
            "in": "query",
            "description": "Your own id for the customer, as you gave it when the subscription was created. It follows the person whichever wallet pays. Name the customer by exactly one of `wallet`, `external_id` and `email`.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "pattern": "^[^\\u0000-\\u001f\\u007f]*$"
            },
            "example": "user_42"
          },
          {
            "name": "wallet",
            "in": "query",
            "description": "The wallet that pays, a base58 Solana address. Name the customer by exactly one of `wallet`, `external_id` and `email`.",
            "schema": {
              "type": "string",
              "pattern": "^[1-9A-HJ-NP-Za-km-z]{32,44}$"
            },
            "x-example": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
          },
          {
            "name": "email",
            "in": "query",
            "description": "The email given when the subscription was created. Never verified by Mesub. Found however it was typed: it is trimmed and lowercased. Name the customer by exactly one of `wallet`, `external_id` and `email`.",
            "schema": {
              "type": "string",
              "format": "email",
              "maxLength": 254
            },
            "x-example": "ada@example.com"
          },
          {
            "name": "plan",
            "in": "query",
            "description": "Only this plan's, by slug. Left out, every plan of the project.",
            "schema": {
              "type": "string",
              "pattern": "^(?=.*[a-z])[a-z0-9]+(?:-[a-z0-9]+)*$"
            },
            "x-example": "pro"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many items to answer.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "x-example": 20
          },
          {
            "name": "starting_after",
            "in": "query",
            "description": "The id of the last subscription of the previous page. The next page starts after it.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,64}$"
            },
            "x-example": "cmg1x0abc0001"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of subscriptions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionList"
                },
                "example": {
                  "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": "ada@example.com",
                      "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"
                    }
                  ],
                  "has_more": false
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "A parameter is missing, malformed, or not one this route knows.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": ["limit must be a whole number from 1 to 100"],
                  "error": "Bad Request",
                  "code": "invalid_request",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "plan_not_found",
                "when": "No plan of your project under that slug.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No plan of yours is named pro-max.",
                  "error": "Not Found",
                  "code": "plan_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/transaction": {
      "post": {
        "tags": ["Subscriptions"],
        "summary": "Get a fresh transaction",
        "operationId": "freshSubscriptionTransaction",
        "description": "A fresh transaction for a subscription still waiting for its signature, with the terms it names. A transaction lives 150 blocks, about half a minute: `transaction_expires_at` estimates when they run out, `last_valid_block_height` says it. The terms live five minutes. So have the terms signed first, and call this route when the wallet is ready for the transaction: its 150 blocks start here, not while the customer reads. Have the wallet sign it at once, then send it with the terms signature to [Submit](/reference/subscriptions/submit). Always sign the transaction of the last call.\n\nTo know whether a terms signature you hold is still good, compare `terms.message` with the message the wallet signed. The same text is the same terms: submit with the signature you hold. Another text is new terms: have the wallet sign this message, since submit refuses the old signature with `invalid_terms_signature`.\n\nThe terms come back unchanged while they have more than two minutes left before `terms.expires_at`. New terms come after that, after a submit that spent them (one answered `transaction_expired` may have), or when the plan changed on chain.\n\nThis route reserves nothing and takes no body: the wallet, the plan and the customer are the subscription's own. For a subscription that expired, call [Create](/reference/subscriptions/create) again.",
        "x-mesub": {
          "href": "reference/subscriptions/transaction",
          "limit": "60 calls a minute per API key",
          "confirm": "This builds a new transaction for a real subscription that waits for its signature. It charges nothing until its wallet signs and you submit.",
          "guide": {
            "label": "Subscribe from your server",
            "href": "docs/subscribe"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\n// once the wallet has signed the terms\nconst { transaction, terms } =\n    await mesub.subscriptions.transaction('cmg1x0abc0001');\n\nif (terms.message !== signedMessage) {\n    // new terms: have the wallet sign terms.message again\n}"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          }
        ],
        "responses": {
          "201": {
            "description": "The subscription, `pending`, with a new transaction and the terms it names, as `POST /v1/subscriptions` answers them.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscribeResult"
                },
                "example": {
                  "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"
                  }
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "The body is not valid JSON.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              },
              {
                "code": "mint_not_on_chain",
                "when": "The plan's token does not exist on chain.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Mesub-Version must be one of the versions this API serves: 2026-10-02.",
                  "error": "Bad Request",
                  "code": "unsupported_version",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "The request does not fit the current state.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfterWithSomeCodes"
              }
            },
            "x-codes": [
              {
                "code": "not_awaiting_signature",
                "when": "The subscription is not `pending`: signed already, expired or ended.",
                "retryable": false
              },
              {
                "code": "plan_sunset",
                "when": "The plan takes no new subscribers.",
                "retryable": false
              },
              {
                "code": "plan_removed",
                "when": "The plan is gone from the chain.",
                "retryable": false
              },
              {
                "code": "plan_replaced",
                "when": "Another plan now stands at its address.",
                "retryable": false
              },
              {
                "code": "plan_ended",
                "when": "The plan is past its end date.",
                "retryable": false
              },
              {
                "code": "plan_mismatch",
                "when": "The plan on chain is not the one Mesub recorded.",
                "retryable": false
              },
              {
                "code": "insufficient_balance",
                "when": "The wallet holds less of the plan's token than the first period costs.",
                "retryable": false
              },
              {
                "code": "insufficient_sol",
                "when": "The wallet holds less SOL than the transaction's network fee and the rent of the accounts it opens. The message says how much it holds and needs.",
                "retryable": false
              },
              {
                "code": "receiver_account_missing",
                "when": "Nobody the plan pays has an account for its token.",
                "retryable": false
              },
              {
                "code": "already_subscribed",
                "when": "Another subscription of this wallet to this plan landed meanwhile.",
                "retryable": false
              },
              {
                "code": "subscription_cancelled",
                "when": "The wallet's subscription to this plan is cancelled, and runs to the end of its period.",
                "retryable": false
              },
              {
                "code": "payment_pending",
                "when": "A payment on this plan failed and is being retried: the wallet has to be topped up.",
                "retryable": false
              },
              {
                "code": "subscription_stopped",
                "when": "The wallet's subscription stopped after missed payments, and its authorisation is cancelled.",
                "retryable": false
              },
              {
                "code": "subscription_terms_changed",
                "when": "The plan's terms changed since the wallet's subscription stopped.",
                "retryable": false
              },
              {
                "code": "comeback_in_flight",
                "when": "A return to this plan may still land. Ask again later.",
                "retryable": true
              },
              {
                "code": "comeback_landed",
                "when": "A return to this plan landed and is being confirmed.",
                "retryable": false
              },
              {
                "code": "comeback_period_rolling",
                "when": "A return asked within two minutes of the end of a billing period. Wait for `Retry-After`.",
                "retryable": true
              },
              {
                "code": "receiver_account_frozen",
                "when": "The account the plan pays for its token is frozen, so the first charge would fail.",
                "retryable": false
              },
              {
                "code": "receiver_account_needs_memo",
                "when": "The account the plan pays for its token requires a memo on every transfer in, which Mesub's charges do not carry.",
                "retryable": false
              },
              {
                "code": "receiver_account_confidential_only",
                "when": "The account the plan pays for its token takes confidential transfers only, so the first charge would fail.",
                "retryable": false
              },
              {
                "code": "mint_paused",
                "when": "The plan's token is paused by its issuer: no payment in it moves until the issuer lifts the pause.",
                "retryable": false
              },
              {
                "code": "mint_transfer_hook",
                "when": "The plan's token now runs its issuer's own program on every transfer, which Mesub does not support yet.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 409,
                  "message": "This subscription is not waiting for a signature.",
                  "error": "Conflict",
                  "code": "not_awaiting_signature",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "503": {
            "description": "The Solana network did not answer.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "network_unavailable",
                "when": "The Solana network does not answer. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 503,
                  "message": "The Solana network did not answer. Try again in a few seconds.",
                  "error": "Service Unavailable",
                  "code": "network_unavailable",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/submit": {
      "post": {
        "tags": ["Subscriptions"],
        "summary": "Submit a subscription",
        "operationId": "submitSubscription",
        "description": "Relays what the wallet signed: the transaction and the terms. Mesub checks both signatures, co-signs, sends the transaction and waits for the chain, so this call is slow by design: up to 90 seconds.\n\nSafe to send again unchanged. Mesub recognises what it already co-signed and answers from the chain, so a customer is never charged twice.\n\nA transaction submitted after its 150 blocks, about half a minute, is refused with `transaction_expired`: nothing was sent, and the same submit sent again cannot land. Ask for [a fresh transaction](/reference/subscriptions/transaction), have the wallet sign it, and sign the terms again if their message changed.",
        "x-mesub": {
          "href": "reference/subscriptions/submit",
          "limit": "60 calls a minute per API key",
          "confirm": "This sends a signed transaction: if it lands, the wallet pays the first period.",
          "guide": {
            "label": "Subscribe from your server",
            "href": "docs/subscribe"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst { subscription, reason } = await mesub.subscriptions.submit(\n    'cmg1x0abc0001',\n    { transaction: signedTransaction, terms_signature },\n);\n\nif (subscription.access) {\n    // it landed: grant the plan\n}"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["transaction", "terms_signature"],
                "properties": {
                  "transaction": {
                    "type": "string",
                    "maxLength": 2048,
                    "pattern": "^[A-Za-z0-9+/]+={0,2}$",
                    "description": "The transaction as the wallet's `signTransaction` answered it, in base64. Signed, not sent."
                  },
                  "terms_signature": {
                    "type": "string",
                    "pattern": "^[1-9A-HJ-NP-Za-km-z]+$",
                    "description": "What the wallet's `signMessage` answered over `terms.message`, in base58, 64 bytes once decoded (86 to 88 characters for a real one).",
                    "maxLength": 88
                  }
                }
              },
              "example": {
                "transaction": "gAIBBg0Xq3ZkT9yLmR4vP7wJ8sN2aHcVbE5uYtKfDgQxWz1oMiCj6rA0lSnUhBdXe2GqFpYv7mNcTz4RwK8sLjH5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDBAUGBwgJCgsM",
                "terms_signature": "2Jd8sPq7XkVnT4mWcR9yHbFz3LgNuA6eKtQx5CvMjD1rYpZwSoEiGhUa8BfT2nXkVq4mWcR7yHbFz9LgNuA3eKtQ"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "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`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Confirmed"
                },
                "examples": {
                  "Landed": {
                    "value": {
                      "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": "ada@example.com",
                        "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"
                      }
                    }
                  },
                  "Not landed": {
                    "value": {
                      "subscription": {
                        "id": "cmg1x0abc0001",
                        "status": "pending",
                        "paused": false,
                        "end_reason": null,
                        "late_reason": null,
                        "stop_reason": null,
                        "access": false,
                        "payment_status": "none",
                        "blocked_since": null,
                        "blocked_reason": null,
                        "plan": "pro",
                        "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                        "email": "ada@example.com",
                        "external_id": "user_42",
                        "current_period_start": null,
                        "current_period_end": null,
                        "next_charge_at": null,
                        "next_retry_at": null,
                        "retry_deadline": null,
                        "next_retry_number": null,
                        "retries_allowed": null,
                        "access_until": null,
                        "closable": false,
                        "closable_from": null,
                        "created_at": "2026-01-01T00:00:00.000Z",
                        "confirmed_at": null
                      },
                      "reason": "The transaction expired before it landed.",
                      "reason_code": "transaction_expired"
                    }
                  }
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "A parameter is missing, malformed, or not one this route knows.",
                "retryable": false
              },
              {
                "code": "not_our_transaction",
                "when": "The transaction is not the one Mesub built, or the wallet's signature on it is missing or wrong.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              },
              {
                "code": "mint_not_on_chain",
                "when": "The plan's token does not exist on chain.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": ["transaction must be base64"],
                  "error": "Bad Request",
                  "code": "invalid_request",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "403": {
            "description": "The signed terms were refused.",
            "x-codes": [
              {
                "code": "terms_expired",
                "when": "The terms ran out, five minutes after they were handed out. Create again.",
                "retryable": false
              },
              {
                "code": "terms_missing",
                "when": "No terms were handed out for this subscription, or none are left unsigned.",
                "retryable": false
              },
              {
                "code": "invalid_terms_signature",
                "when": "The terms signature is not the subscription's wallet's, over the terms Mesub handed out.",
                "retryable": false
              },
              {
                "code": "terms_used",
                "when": "The terms were already spent by another submit.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 403,
                  "message": "These terms expired. Ask for them again.",
                  "error": "Forbidden",
                  "code": "terms_expired",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "The request does not fit the current state.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfterWithSomeCodes"
              }
            },
            "x-codes": [
              {
                "code": "not_awaiting_signature",
                "when": "The subscription is no longer waiting for a signature: settled, expired or failed.",
                "retryable": false
              },
              {
                "code": "transaction_not_built",
                "when": "Submitted before any transaction was built for the wallet to sign.",
                "retryable": false
              },
              {
                "code": "terms_changed",
                "when": "Newer terms were handed out, or the plan changed since they were signed, its end date come too close for the second charge they name included. Create again.",
                "retryable": false
              },
              {
                "code": "transaction_expired",
                "when": "The transaction's blockhash expired before it was sent. Nothing was sent, and the terms may be spent. Ask for a new transaction with `POST /v1/subscriptions/:id/transaction`.",
                "retryable": false
              },
              {
                "code": "transaction_refused",
                "when": "The network's simulation refused the transaction. Nothing was sent.",
                "retryable": false
              },
              {
                "code": "already_subscribed",
                "when": "This wallet already holds a running subscription to this plan.",
                "retryable": false
              },
              {
                "code": "seat_cap_reached",
                "when": "The project is at its tier's subscriber limit.",
                "retryable": false
              },
              {
                "code": "subscription_stopped",
                "when": "The wallet's subscription stopped after missed payments, and its authorisation is cancelled.",
                "retryable": false
              },
              {
                "code": "subscription_terms_changed",
                "when": "The plan's terms changed since the wallet's subscription stopped.",
                "retryable": false
              },
              {
                "code": "comeback_in_flight",
                "when": "A return to this plan may still land. Ask again later.",
                "retryable": true
              },
              {
                "code": "comeback_landed",
                "when": "A return to this plan landed and is being confirmed.",
                "retryable": false
              },
              {
                "code": "comeback_period_rolling",
                "when": "A return asked within two minutes of the end of a billing period. Wait for `Retry-After`.",
                "retryable": true
              },
              {
                "code": "plan_sunset",
                "when": "The plan takes no new subscribers.",
                "retryable": false
              },
              {
                "code": "plan_removed",
                "when": "The plan is gone from the chain.",
                "retryable": false
              },
              {
                "code": "plan_replaced",
                "when": "Another plan now stands at its address.",
                "retryable": false
              },
              {
                "code": "plan_ended",
                "when": "The plan is past its end date.",
                "retryable": false
              },
              {
                "code": "plan_mismatch",
                "when": "The plan on chain is not the one Mesub recorded.",
                "retryable": false
              },
              {
                "code": "receiver_account_missing",
                "when": "Nobody the plan pays has an account for its token.",
                "retryable": false
              },
              {
                "code": "receiver_account_frozen",
                "when": "The account the plan pays for its token is frozen, so the first charge would fail.",
                "retryable": false
              },
              {
                "code": "receiver_account_needs_memo",
                "when": "The account the plan pays for its token requires a memo on every transfer in, which Mesub's charges do not carry.",
                "retryable": false
              },
              {
                "code": "receiver_account_confidential_only",
                "when": "The account the plan pays for its token takes confidential transfers only, so the first charge would fail.",
                "retryable": false
              },
              {
                "code": "mint_paused",
                "when": "The plan's token is paused by its issuer: no payment in it moves until the issuer lifts the pause.",
                "retryable": false
              },
              {
                "code": "mint_transfer_hook",
                "when": "The plan's token now runs its issuer's own program on every transfer, which Mesub does not support yet.",
                "retryable": false
              },
              {
                "code": "subscription_cancelled",
                "when": "The wallet's subscription to this plan is cancelled, and runs to the end of its period.",
                "retryable": false
              },
              {
                "code": "payment_pending",
                "when": "A payment on this plan failed and is being retried: the wallet has to be topped up.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 409,
                  "message": "This subscription is not waiting for a signature.",
                  "error": "Conflict",
                  "code": "not_awaiting_signature",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "503": {
            "description": "The Solana network did not answer, or too many submits are waiting.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "network_unavailable",
                "when": "The Solana network does not answer. `Retry-After` says when to come back.",
                "retryable": true
              },
              {
                "code": "unavailable",
                "when": "Too many submits are waiting for the database or for this project's seats. Nothing was signed or sent: send the same request again after `Retry-After`.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 503,
                  "message": "The Solana network did not answer. Try again in a few seconds.",
                  "error": "Service Unavailable",
                  "code": "network_unavailable",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}": {
      "get": {
        "tags": ["Subscriptions"],
        "summary": "Retrieve a subscription",
        "operationId": "retrieveSubscription",
        "description": "One subscription: its status, whether it grants access and until when, its wallet, email and `external_id`, the period it last paid for, and what comes next. `access`, `payment_status` and `access_until` are what [Check access](/reference/access/check) answers for it.\n\nA `superseded` id, left out of the list, still answers here, so an id you stored keeps working.\n\nOn a plan with an end date, `access` is false from that date on and `access_until` is never later than it, whatever period was paid for. `next_charge_at` and `next_retry_at` are null once the plan ends before the charge or the retry.",
        "x-mesub": {
          "href": "reference/subscriptions/retrieve",
          "limit": "1,000 calls a minute per API key",
          "guide": {
            "label": "Lifecycle",
            "href": "docs/lifecycle"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst subscription = await mesub.subscriptions.retrieve('cmg1x0abc0001');"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          }
        ],
        "responses": {
          "200": {
            "description": "The subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                },
                "example": {
                  "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": "ada@example.com",
                  "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"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Mesub-Version must be one of the versions this API serves: 2026-10-02.",
                  "error": "Bad Request",
                  "code": "unsupported_version",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/attempts": {
      "get": {
        "tags": ["Subscriptions"],
        "summary": "List payments",
        "operationId": "listSubscriptionAttempts",
        "description": "The charges of one subscription, newest first, and what it paid since it began: what a screen showing a customer one subscription with its payments needs.\n\n`paid` is counted over every paid charge of the subscription, not over the page. Only this id's charges are listed: a customer who came back holds a new subscription, and the payments of the one it replaced stay under that one's id.",
        "x-mesub": {
          "href": "reference/subscriptions/attempts",
          "title": "List a subscription's payments",
          "limit": "1,000 calls a minute per API key",
          "guide": {
            "label": "List its payments",
            "href": "docs/manage#list-its-payments"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst { data, has_more, paid } = await mesub.subscriptions.attempts('cmg1x0abc0001');\n\n// every page\nfor await (const attempt of mesub.subscriptions.allAttempts('cmg1x0abc0001')) {\n    // ...\n}"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many items to answer.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "x-example": 20
          },
          {
            "name": "starting_after",
            "in": "query",
            "description": "The id of the last attempt of the previous page. The next page starts after it.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,64}$"
            },
            "x-example": "cmg9x1b7a0003"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of charges, and the total paid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttemptList"
                },
                "example": {
                  "data": [
                    {
                      "id": "cmg9x4k2c0007",
                      "attempted_at": "2026-02-02T00:01:04.512Z",
                      "outcome": "paid",
                      "reason": null,
                      "amount": "9990000",
                      "amount_received": "9990000",
                      "amount_usd": "9.99",
                      "signature": "5wHu1qwD4kLDLoREEPBpKvGvhbxWFxFFfr5nQq7dxAsRRDXvHRqpNvfsCvC1qzjWQhqmPtBb8LnZjKmvpehJqSWk",
                      "retry": true,
                      "retry_number": 2,
                      "retries_allowed": 3,
                      "period_start": "2026-01-31T00:00:00.000Z"
                    },
                    {
                      "id": "cmg9x1b7a0003",
                      "attempted_at": "2026-01-31T00:00:02.000Z",
                      "outcome": "skipped",
                      "reason": "insufficient-balance",
                      "amount": "9990000",
                      "amount_received": null,
                      "amount_usd": "9.99",
                      "signature": null,
                      "retry": false,
                      "retry_number": null,
                      "retries_allowed": null,
                      "period_start": "2026-01-31T00:00:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "paid": {
                    "count": 2,
                    "amount": "19980000"
                  }
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "A parameter is missing, malformed, or not one this route knows.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": ["limit must be a whole number from 1 to 100"],
                  "error": "Bad Request",
                  "code": "invalid_request",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/retry": {
      "post": {
        "tags": ["Subscriptions"],
        "summary": "Retry a late payment",
        "operationId": "retrySubscription",
        "description": "Runs the charge of the missed period now, rather than at its next retry: for a customer who topped up their wallet and wants their access back from your page. Nothing to sign: the wallet approved the plan when it subscribed.\n\nThe charge runs in the background, with the checks and records of a scheduled one. The answer is the subscription, still `unpaid`: the outcome comes as `subscription.renewed` or `subscription.payment_failed`, and on the subscription.\n\nRefused before anything is queued when it cannot succeed, as the chain reads now, so that it never spends a retry for nothing: the wallet holds less than the period costs, no longer approves the subscription, or its token account is frozen, or the last charge found its authorisation closed.\n\nOn Free, a retry by hand is one of the missed period's three, whoever makes it, at least ten minutes apart, and none within two minutes of the end of the missed period. On Dev and Business it runs beside the schedule, and the wait between two doubles.",
        "x-mesub": {
          "href": "reference/subscriptions/retry",
          "limit": "60 calls a minute per API key",
          "confirm": "This charges the missed period from the customer's wallet, for real."
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          }
        ],
        "responses": {
          "202": {
            "description": "The charge is queued. The subscription as [Retrieve a subscription](/reference/subscriptions/retrieve) answers it, still `unpaid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                },
                "example": {
                  "id": "cmg1x0abc0001",
                  "status": "unpaid",
                  "paused": false,
                  "end_reason": null,
                  "late_reason": "insufficient_balance",
                  "stop_reason": null,
                  "access": true,
                  "payment_status": "late",
                  "blocked_since": null,
                  "blocked_reason": null,
                  "plan": "pro",
                  "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                  "email": "ada@example.com",
                  "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": null,
                  "next_retry_at": "2026-02-01T00:01:00.000Z",
                  "retry_deadline": null,
                  "next_retry_number": 1,
                  "retries_allowed": 3,
                  "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"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "The body is not valid JSON.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Mesub-Version must be one of the versions this API serves: 2026-10-02.",
                  "error": "Bad Request",
                  "code": "unsupported_version",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "The charge cannot be retried now.",
            "x-codes": [
              {
                "code": "subscription_not_late",
                "when": "The subscription is not behind on its payment: only an `unpaid` one is retried.",
                "retryable": false
              },
              {
                "code": "subscription_paused",
                "when": "A late seat parked over the project's subscriber limit: nothing charges it until a seat frees up.",
                "retryable": false
              },
              {
                "code": "retry_too_soon",
                "when": "The wait since the last retry by hand is not over. `Retry-After` says when it is.",
                "retryable": true
              },
              {
                "code": "insufficient_balance",
                "when": "The wallet holds less of the plan's token than the period costs.",
                "retryable": false
              },
              {
                "code": "retry_cannot_succeed",
                "when": "The wallet no longer approves the subscription, its token account is frozen, or its authorisation is closed: a retry would fail whatever the wallet holds. `late_reason` says which.",
                "retryable": false
              },
              {
                "code": "subscription_changed",
                "when": "Two retries met, or a charge moved the subscription on meanwhile. Asking again settles it.",
                "retryable": true
              },
              {
                "code": "pull_running",
                "when": "On Free, a charge of this subscription is running now: its outcome is on the subscription soon.",
                "retryable": true
              },
              {
                "code": "retries_spent",
                "when": "On Free, the missed period's three retries by hand are spent: the subscription stops at that period's end.",
                "retryable": false
              },
              {
                "code": "retry_deadline_passed",
                "when": "On Free, the missed period ends within two minutes, or has ended: retries by hand are closed.",
                "retryable": false
              },
              {
                "code": "plan_ended",
                "when": "The plan is past its end date: nothing more is charged.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 409,
                  "message": "This subscription is not behind on its payment.",
                  "error": "Conflict",
                  "code": "subscription_not_late",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfterWithSomeCodes"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "503": {
            "description": "The charge could not be queued, or the chain could not be read. Nothing changed: try again after `Retry-After`.",
            "x-codes": [
              {
                "code": "network_unavailable",
                "when": "The Solana network does not answer. `Retry-After` says when to come back.",
                "retryable": true
              },
              {
                "code": "unavailable",
                "when": "The charge could not be queued. Nothing changed: `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 503,
                  "message": "The retry could not be queued. Try again in a moment.",
                  "error": "Service Unavailable",
                  "code": "unavailable",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/cancel": {
      "post": {
        "tags": ["Cancel, resume, close"],
        "summary": "Cancel a subscription",
        "operationId": "cancelSubscription",
        "description": "Builds the transaction that cancels an `active`, `unpaid` or `stopped` subscription. The subscription's own wallet signs and sends it, then you [confirm](/reference/manage/confirm-cancel). Nothing changes until the confirmation.\n\nA subscription that is paid up keeps its access until the end of the period. A late or stopped one has none, and its missed period is never collected.",
        "x-mesub": {
          "href": "reference/manage/cancel",
          "limit": "60 calls a minute per API key",
          "confirm": "This builds a cancellation transaction for a real subscription. Nothing changes until its wallet signs and sends it.",
          "guide": {
            "label": "Manage from your server",
            "href": "docs/manage"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst { transaction } = await mesub.subscriptions.cancel('cmg1x0abc0001');\n// send `transaction` to your page: the paying wallet signs and sends it"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          }
        ],
        "responses": {
          "201": {
            "description": "The transaction, built for the subscription's own wallet. Nothing has changed yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnsignedTransaction"
                },
                "example": {
                  "transaction": "gAIBBg0Xq3ZkT9yLmR4vP7wJ8sN2aHcVbE5uYtKfDgQxWz1oMiCj6rA0lSnUhBdXe2GqFpYv7mNcTz4RwK8sLjH5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDBAUGBwgJCgsM",
                  "last_valid_block_height": "301245678",
                  "transaction_expires_at": "2026-01-01T00:00:33.000Z"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "The body is not valid JSON.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Mesub-Version must be one of the versions this API serves: 2026-10-02.",
                  "error": "Bad Request",
                  "code": "unsupported_version",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "The request does not fit the current state.",
            "x-codes": [
              {
                "code": "subscription_not_active",
                "when": "It is not `active`, `unpaid` or `stopped`.",
                "retryable": false
              },
              {
                "code": "subscription_cancelled",
                "when": "The wallet's subscription to this plan is cancelled, and runs to the end of its period.",
                "retryable": false
              },
              {
                "code": "subscription_not_on_chain",
                "when": "It is closed already, or gone from the chain.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 409,
                  "message": "This subscription is not running.",
                  "error": "Conflict",
                  "code": "subscription_not_active",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "503": {
            "description": "The Solana network did not answer.",
            "x-codes": [
              {
                "code": "network_unavailable",
                "when": "The Solana network does not answer. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 503,
                  "message": "The Solana network did not answer. Try again in a few seconds.",
                  "error": "Service Unavailable",
                  "code": "network_unavailable",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/cancel/confirm": {
      "post": {
        "tags": ["Cancel, resume, close"],
        "summary": "Confirm a cancellation",
        "operationId": "confirmCancelSubscription",
        "description": "Settles the cancellation against the chain once the wallet sent it. The account on chain is the verdict, not the signature. Mesub waits for the chain, up to 90 seconds. Safe to call twice.",
        "x-mesub": {
          "href": "reference/manage/confirm-cancel",
          "limit": "60 calls a minute per API key",
          "confirm": "This settles a cancel on a real subscription, if its wallet sent the transaction.",
          "guide": {
            "label": "Manage from your server",
            "href": "docs/manage"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst { subscription, reason } = await mesub.subscriptions.confirmCancel(\n    'cmg1x0abc0001',\n    { signature },\n);\n\nif (reason === undefined) {\n    // settled: subscription.status is 'cancelled'\n}"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["signature"],
                "properties": {
                  "signature": {
                    "type": "string",
                    "pattern": "^[1-9A-HJ-NP-Za-km-z]+$",
                    "description": "What the wallet answered when it sent the transaction: a base58 transaction signature, 64 bytes once decoded (86 to 88 characters for a real one).",
                    "maxLength": 88
                  }
                }
              },
              "example": {
                "signature": "5wHu1qwD4kLDLoREEPBpKvGvhbxWFxFFfr5nQq7dxAsRRDXvHRqpNvfsCvC1qzjWQhqmPtBb8LnZjKmvpehJqSWk"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The subscription, `cancelled`, or `ended` with `end_reason: \"cancelled\"` once its end passed. When nothing was settled, the subscription comes back as it was, with a `reason` and its `reason_code`. The codes this route answers today: `transaction_failed`, `transaction_expired`, `subscription_not_on_chain`, `cancellation_not_landed`, `subscription_changed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Confirmed"
                },
                "example": {
                  "subscription": {
                    "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": "ada@example.com",
                    "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": 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"
                  }
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "A parameter is missing, malformed, or not one this route knows.",
                "retryable": false
              },
              {
                "code": "nothing_to_confirm",
                "when": "A confirm before any transaction was built.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": ["signature must be a base58 transaction signature"],
                  "error": "Bad Request",
                  "code": "invalid_request",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "The request does not fit the current state.",
            "x-codes": [
              {
                "code": "subscription_not_active",
                "when": "It is not `active`, `unpaid` or `stopped`.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 409,
                  "message": "This subscription is not running.",
                  "error": "Conflict",
                  "code": "subscription_not_active",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "503": {
            "description": "The Solana network did not answer.",
            "x-codes": [
              {
                "code": "network_unavailable",
                "when": "The Solana network does not answer. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 503,
                  "message": "The Solana network did not answer. Try again in a few seconds.",
                  "error": "Service Unavailable",
                  "code": "network_unavailable",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/resume": {
      "post": {
        "tags": ["Cancel, resume, close"],
        "summary": "Resume a subscription",
        "operationId": "resumeSubscription",
        "description": "Builds the transaction that takes a cancellation back while the paid period still runs. The subscription's own wallet signs and sends it, then you [confirm](/reference/manage/confirm-resume).",
        "x-mesub": {
          "href": "reference/manage/resume",
          "limit": "60 calls a minute per API key",
          "confirm": "This builds a resume transaction for a real subscription. Nothing changes until its wallet signs and sends it.",
          "guide": {
            "label": "Manage from your server",
            "href": "docs/manage"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst { transaction } = await mesub.subscriptions.resume('cmg1x0abc0001');\n// send `transaction` to your page: the paying wallet signs and sends it"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          }
        ],
        "responses": {
          "201": {
            "description": "The transaction, built for the subscription's own wallet. Nothing has changed yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnsignedTransaction"
                },
                "example": {
                  "transaction": "gAIBBg0Xq3ZkT9yLmR4vP7wJ8sN2aHcVbE5uYtKfDgQxWz1oMiCj6rA0lSnUhBdXe2GqFpYv7mNcTz4RwK8sLjH5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDBAUGBwgJCgsM",
                  "last_valid_block_height": "301245678",
                  "transaction_expires_at": "2026-01-01T00:00:33.000Z"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "The body is not valid JSON.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Mesub-Version must be one of the versions this API serves: 2026-10-02.",
                  "error": "Bad Request",
                  "code": "unsupported_version",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "The request does not fit the current state.",
            "x-codes": [
              {
                "code": "subscription_not_cancelled",
                "when": "Nothing cancelled it.",
                "retryable": false
              },
              {
                "code": "subscription_ended",
                "when": "The paid period is over: too late to resume. Subscribe again.",
                "retryable": false
              },
              {
                "code": "subscription_not_on_chain",
                "when": "It is closed already, or gone from the chain.",
                "retryable": false
              },
              {
                "code": "plan_removed",
                "when": "The plan is gone from the chain.",
                "retryable": false
              },
              {
                "code": "plan_replaced",
                "when": "Another plan now stands at its address, which the subscription was never signed for.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 409,
                  "message": "This subscription is not cancelled.",
                  "error": "Conflict",
                  "code": "subscription_not_cancelled",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "503": {
            "description": "The Solana network did not answer.",
            "x-codes": [
              {
                "code": "network_unavailable",
                "when": "The Solana network does not answer. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 503,
                  "message": "The Solana network did not answer. Try again in a few seconds.",
                  "error": "Service Unavailable",
                  "code": "network_unavailable",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/resume/confirm": {
      "post": {
        "tags": ["Cancel, resume, close"],
        "summary": "Confirm a resume",
        "operationId": "confirmResumeSubscription",
        "description": "Settles the resume against the chain once the wallet sent it. The subscription is `active` again, with its next charge back on schedule.",
        "x-mesub": {
          "href": "reference/manage/confirm-resume",
          "limit": "60 calls a minute per API key",
          "confirm": "This settles a resume on a real subscription, if its wallet sent the transaction.",
          "guide": {
            "label": "Manage from your server",
            "href": "docs/manage"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst { subscription, reason } = await mesub.subscriptions.confirmResume(\n    'cmg1x0abc0001',\n    { signature },\n);\n\nif (reason === undefined) {\n    // settled: subscription.status is 'active'\n}"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["signature"],
                "properties": {
                  "signature": {
                    "type": "string",
                    "pattern": "^[1-9A-HJ-NP-Za-km-z]+$",
                    "description": "What the wallet answered when it sent the transaction: a base58 transaction signature, 64 bytes once decoded (86 to 88 characters for a real one).",
                    "maxLength": 88
                  }
                }
              },
              "example": {
                "signature": "5wHu1qwD4kLDLoREEPBpKvGvhbxWFxFFfr5nQq7dxAsRRDXvHRqpNvfsCvC1qzjWQhqmPtBb8LnZjKmvpehJqSWk"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The subscription, `active`. When nothing was settled, the subscription comes back as it was, with a `reason` and its `reason_code`. The codes this route answers today: `transaction_failed`, `transaction_expired`, `subscription_not_on_chain`, `resume_not_landed`, `subscription_not_cancelled`, `cancelled_subscription_remade`, `resume_unverified`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Confirmed"
                },
                "example": {
                  "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": "ada@example.com",
                    "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"
                  }
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "A parameter is missing, malformed, or not one this route knows.",
                "retryable": false
              },
              {
                "code": "nothing_to_confirm",
                "when": "A confirm before any transaction was built.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": ["signature must be a base58 transaction signature"],
                  "error": "Bad Request",
                  "code": "invalid_request",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "503": {
            "description": "The Solana network did not answer.",
            "x-codes": [
              {
                "code": "network_unavailable",
                "when": "The Solana network does not answer. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 503,
                  "message": "The Solana network did not answer. Try again in a few seconds.",
                  "error": "Service Unavailable",
                  "code": "network_unavailable",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/close": {
      "post": {
        "tags": ["Cancel, resume, close"],
        "summary": "Close a subscription",
        "operationId": "closeSubscription",
        "description": "Builds the transaction that closes the authorisation of a cancelled or stopped subscription whose end has passed, or at once of one stopped with `stop_reason` `authority_closed`. `closable` on the subscription says whether it can close now, and `closable_from` from when. The subscription's own wallet signs and sends it, and gets its rent back. Then you [confirm](/reference/manage/confirm-close).",
        "x-mesub": {
          "href": "reference/manage/close",
          "limit": "60 calls a minute per API key",
          "confirm": "This builds a closing transaction for a real subscription. Nothing changes until its wallet signs and sends it.",
          "guide": {
            "label": "Manage from your server",
            "href": "docs/manage"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst { transaction } = await mesub.subscriptions.close('cmg1x0abc0001');\n// send `transaction` to your page: the paying wallet signs and sends it"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          }
        ],
        "responses": {
          "201": {
            "description": "The transaction, built for the subscription's own wallet. Nothing has changed yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnsignedTransaction"
                },
                "example": {
                  "transaction": "gAIBBg0Xq3ZkT9yLmR4vP7wJ8sN2aHcVbE5uYtKfDgQxWz1oMiCj6rA0lSnUhBdXe2GqFpYv7mNcTz4RwK8sLjH5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDBAUGBwgJCgsM",
                  "last_valid_block_height": "301245678",
                  "transaction_expires_at": "2026-01-01T00:00:33.000Z"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "The body is not valid JSON.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Mesub-Version must be one of the versions this API serves: 2026-10-02.",
                  "error": "Bad Request",
                  "code": "unsupported_version",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "The request does not fit the current state.",
            "x-codes": [
              {
                "code": "close_too_early",
                "when": "Its end has not passed yet.",
                "retryable": false
              },
              {
                "code": "subscription_not_cancelled",
                "when": "Nothing cancelled it.",
                "retryable": false
              },
              {
                "code": "subscription_not_on_chain",
                "when": "It is closed already, or gone from the chain.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 409,
                  "message": "This subscription runs until 2026-01-31T00:00:00.000Z, and can be closed from then.",
                  "error": "Conflict",
                  "code": "close_too_early",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "503": {
            "description": "The Solana network did not answer.",
            "x-codes": [
              {
                "code": "network_unavailable",
                "when": "The Solana network does not answer. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 503,
                  "message": "The Solana network did not answer. Try again in a few seconds.",
                  "error": "Service Unavailable",
                  "code": "network_unavailable",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/subscriptions/{id}/close/confirm": {
      "post": {
        "tags": ["Cancel, resume, close"],
        "summary": "Confirm a close",
        "operationId": "confirmCloseSubscription",
        "description": "Settles the close against the chain once the wallet sent it. The account gone is the verdict, and it frees the plan for a fresh subscription from that wallet. Safe to call twice.",
        "x-mesub": {
          "href": "reference/manage/confirm-close",
          "limit": "60 calls a minute per API key",
          "confirm": "This settles a close on a real subscription, if its wallet sent the transaction.",
          "guide": {
            "label": "Manage from your server",
            "href": "docs/manage"
          }
        },
        "x-codeSamples": [
          {
            "lang": "TypeScript",
            "label": "@mesub/node",
            "source": "import { Mesub } from '@mesub/node';\n\nconst mesub = new Mesub(); // reads MESUB_API_KEY\n\nconst { subscription, reason } = await mesub.subscriptions.confirmClose(\n    'cmg1x0abc0001',\n    { signature },\n);\n\nif (reason === undefined) {\n    // settled: subscription.status is 'ended'\n}"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription's id, as `POST /v1/subscriptions` answered it.",
            "schema": {
              "type": "string"
            },
            "example": "cmg1x0abc0001"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["signature"],
                "properties": {
                  "signature": {
                    "type": "string",
                    "pattern": "^[1-9A-HJ-NP-Za-km-z]+$",
                    "description": "What the wallet answered when it sent the transaction: a base58 transaction signature, 64 bytes once decoded (86 to 88 characters for a real one).",
                    "maxLength": 88
                  }
                }
              },
              "example": {
                "signature": "5wHu1qwD4kLDLoREEPBpKvGvhbxWFxFFfr5nQq7dxAsRRDXvHRqpNvfsCvC1qzjWQhqmPtBb8LnZjKmvpehJqSWk"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The subscription, `ended` with `end_reason: \"closed\"`. When nothing was settled, the subscription comes back as it was, with a `reason` and its `reason_code`. The codes this route answers today: `transaction_failed`, `transaction_expired`, `close_not_landed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Confirmed"
                },
                "example": {
                  "subscription": {
                    "id": "cmg1x0abc0001",
                    "status": "ended",
                    "paused": false,
                    "end_reason": "closed",
                    "late_reason": null,
                    "stop_reason": null,
                    "access": false,
                    "payment_status": "none",
                    "blocked_since": null,
                    "blocked_reason": null,
                    "plan": "pro",
                    "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                    "email": "ada@example.com",
                    "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": null,
                    "next_retry_at": null,
                    "retry_deadline": null,
                    "next_retry_number": null,
                    "retries_allowed": null,
                    "access_until": null,
                    "closable": false,
                    "closable_from": null,
                    "created_at": "2026-01-01T00:00:00.000Z",
                    "confirmed_at": "2026-01-01T00:00:05.000Z"
                  }
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "A parameter is missing, malformed, or not one this route knows.",
                "retryable": false
              },
              {
                "code": "nothing_to_confirm",
                "when": "A confirm before any transaction was built.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": ["signature must be a base58 transaction signature"],
                  "error": "Bad Request",
                  "code": "invalid_request",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "Nothing of your project at that address.",
            "x-codes": [
              {
                "code": "subscription_not_found",
                "when": "No subscription of your project under that id.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No subscription of yours under that id.",
                  "error": "Not Found",
                  "code": "subscription_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "The request does not fit the current state.",
            "x-codes": [
              {
                "code": "subscription_not_cancelled",
                "when": "Nothing cancelled it.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 409,
                  "message": "Cancel this subscription before closing it.",
                  "error": "Conflict",
                  "code": "subscription_not_cancelled",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          },
          "503": {
            "description": "The Solana network did not answer.",
            "x-codes": [
              {
                "code": "network_unavailable",
                "when": "The Solana network does not answer. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 503,
                  "message": "The Solana network did not answer. Try again in a few seconds.",
                  "error": "Service Unavailable",
                  "code": "network_unavailable",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          }
        }
      }
    },
    "/v1/project": {
      "get": {
        "tags": ["Project"],
        "summary": "Retrieve the project",
        "operationId": "retrieveProject",
        "description": "The id of the project the API key belongs to. The quickest way to check that a key works. The SDK has no call for it.",
        "x-mesub": {
          "href": "reference/project/retrieve",
          "limit": "1,000 calls a minute per API key",
          "guide": {
            "label": "Get your API key",
            "href": "docs/api-key"
          }
        },
        "responses": {
          "200": {
            "description": "The project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                },
                "example": {
                  "id": "cmufsl77w001ag2s6pp1ngf9p"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Mesub-Version must be one of the versions this API serves: 2026-10-02.",
                  "error": "Bad Request",
                  "code": "unsupported_version",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          }
        ]
      }
    },
    "/v1/events": {
      "get": {
        "tags": ["Events"],
        "summary": "List events",
        "operationId": "listEvents",
        "description": "Your project's subscription events of the last 30 days, newest first (within one millisecond, by `sequence`, highest first), each the very body its webhooks carry: the same `id`, the same `data`, byte for byte once parsed. Read it to catch up on what an endpoint missed while it was down or turned off.\n\nThe 30 days are counted from each call. An event is served once Mesub has written its body, which it does at its first delivery or at the first read here, whichever comes first, and never changes after.\n\nAn event whose capture failed is missing from the list until Mesub recovers it: Mesub is alerted, and an event is never served with a later state. Events before `available_since` are not served; every page says `available_since`.\n\n`created_at` is set when the event's transaction begins, a moment before it is visible here: an event can appear below one you have already read. When you poll for what is new, do not stop at the first id you know: page on until `created_at` is a minute older than the newest you held, and skip the ids you have seen.",
        "x-mesub": {
          "href": "reference/events/list",
          "limit": "1,000 calls a minute per API key",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "type",
            "in": "query",
            "description": "Only events of this name.",
            "schema": {
              "type": "string",
              "enum": [
                "subscription.created",
                "subscription.renewal_upcoming",
                "subscription.renewed",
                "subscription.payment_failed",
                "subscription.stopped",
                "subscription.cancelled",
                "subscription.resumed",
                "subscription.ended",
                "subscription.expired",
                "subscription.access_granted",
                "subscription.access_revoked",
                "subscription.paused",
                "subscription.unpaused"
              ]
            },
            "x-example": "subscription.payment_failed"
          },
          {
            "name": "subscription",
            "in": "query",
            "description": "Only events about this subscription, by its id. Another project's id finds nothing.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,64}$"
            },
            "x-example": "cmg1x0abc0001"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many events to answer.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            },
            "x-example": 25
          },
          {
            "name": "starting_after",
            "in": "query",
            "description": "The id of the last event of the previous page. The next page starts after it.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,64}$"
            },
            "x-example": "evt_cmg1x0evt0001"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of events.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventList"
                },
                "example": {
                  "data": [
                    {
                      "id": "evt_cmg1x0evt0001",
                      "object": "event",
                      "type": "subscription.renewed",
                      "created_at": "2026-01-31T00:01:00.000Z",
                      "network": "mainnet-beta",
                      "api_version": "2026-10-02",
                      "sequence": 3,
                      "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": "subscriber@example.com",
                        "external_id": "user_123",
                        "current_period_start": "2026-01-31T00:00:00.000Z",
                        "current_period_end": "2026-03-02T00:00:00.000Z",
                        "next_charge_at": "2026-03-02T00:00:00.000Z",
                        "next_retry_at": null,
                        "retry_deadline": null,
                        "next_retry_number": null,
                        "retries_allowed": null,
                        "access_until": "2026-03-02T00: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": {
                        "amount": "9990000",
                        "amount_received": "9990000",
                        "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                        "period_start": "2026-01-31T00:00:00.000Z",
                        "period_end": "2026-03-02T00:00:00.000Z",
                        "signature": "5wHu1qwD4kLDLoREEPBpKvGvhbxWFxFFfr5nQq7dxAsRRDXvHRqpNvfsCvC1qzjWQhqmPtBb8LnZjKmvpehJqSWk"
                      }
                    }
                  ],
                  "has_more": false,
                  "available_since": "2026-10-16T09:00:00.000Z"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "invalid_request",
                "when": "A parameter is missing, malformed, or not one this route knows, or `starting_after` is not an event of your project.",
                "retryable": false
              },
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": ["limit must be a whole number from 1 to 100"],
                  "error": "Bad Request",
                  "code": "invalid_request",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/{id}": {
      "get": {
        "tags": ["Events"],
        "summary": "Retrieve an event",
        "operationId": "retrieveEvent",
        "description": "One event, as [List events](/reference/events/list) serves it, by the `id` its webhook body carries.",
        "x-mesub": {
          "href": "reference/events/retrieve",
          "limit": "1,000 calls a minute per API key",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/MesubVersion"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The event's id, as its webhook body carries it.",
            "schema": {
              "type": "string"
            },
            "example": "evt_cmg1x0evt0001"
          }
        ],
        "responses": {
          "200": {
            "description": "The event.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                },
                "example": {
                  "id": "evt_cmg1x0evt0001",
                  "object": "event",
                  "type": "subscription.renewed",
                  "created_at": "2026-01-31T00:01:00.000Z",
                  "network": "mainnet-beta",
                  "api_version": "2026-10-02",
                  "sequence": 3,
                  "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": "subscriber@example.com",
                    "external_id": "user_123",
                    "current_period_start": "2026-01-31T00:00:00.000Z",
                    "current_period_end": "2026-03-02T00:00:00.000Z",
                    "next_charge_at": "2026-03-02T00:00:00.000Z",
                    "next_retry_at": null,
                    "retry_deadline": null,
                    "next_retry_number": null,
                    "retries_allowed": null,
                    "access_until": "2026-03-02T00: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": {
                    "amount": "9990000",
                    "amount_received": "9990000",
                    "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                    "period_start": "2026-01-31T00:00:00.000Z",
                    "period_end": "2026-03-02T00:00:00.000Z",
                    "signature": "5wHu1qwD4kLDLoREEPBpKvGvhbxWFxFFfr5nQq7dxAsRRDXvHRqpNvfsCvC1qzjWQhqmPtBb8LnZjKmvpehJqSWk"
                  }
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "400": {
            "description": "The request was refused as written.",
            "x-codes": [
              {
                "code": "unsupported_version",
                "when": "`Mesub-Version` names a version this API does not serve. The message lists the ones it does.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 400,
                  "message": "Mesub-Version must be one of the versions this API serves: 2026-10-02.",
                  "error": "Bad Request",
                  "code": "unsupported_version",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "description": "The API key is missing or wrong.",
            "x-codes": [
              {
                "code": "missing_api_key",
                "when": "No `Authorization: Bearer` header carrying an API key.",
                "retryable": false
              },
              {
                "code": "invalid_api_key",
                "when": "Not a key Mesub issued, or one rotated since.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 401,
                  "message": "Send your API key as a bearer token.",
                  "error": "Unauthorized",
                  "code": "missing_api_key",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "No such event served.",
            "x-codes": [
              {
                "code": "event_not_found",
                "when": "No event of your project under that id in the last 30 days, or one from before `available_since`.",
                "retryable": false
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 404,
                  "message": "No event of yours under that id in the last 30 days.",
                  "error": "Not Found",
                  "code": "event_not_found",
                  "retryable": false,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            },
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "Too many calls.",
            "headers": {
              "Mesub-Version": {
                "$ref": "#/components/headers/MesubVersion"
              },
              "Mesub-Request-Id": {
                "$ref": "#/components/headers/MesubRequestId"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "x-codes": [
              {
                "code": "rate_limited",
                "when": "Too many calls this minute. `Retry-After` says when to come back.",
                "retryable": true
              }
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "statusCode": 429,
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "code": "rate_limited",
                  "retryable": true,
                  "request_id": "req_7Hq2LxVb9TzK4mWc1RpN6sYd"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "subscription.created": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.created",
        "operationId": "webhook_subscription_created",
        "description": "The first payment landed. `detail.first_payment` says what it paid.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-created",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.created"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "The event's own fields.",
                    "properties": {
                      "previous_id": {
                        "type": "string",
                        "description": "On a comeback only: the id of the stopped subscription this one took over, now `superseded`. Absent otherwise."
                      },
                      "first_payment": {
                        "type": ["object", "null"],
                        "description": "The first payment, the transfer the subscriber's own transaction carried. Null when Mesub recorded none at the confirmation: the subscription landed in a period paid only in part, which the next charge collects. Absent on events written before Mesub told it.",
                        "required": ["amount", "mint", "signature"],
                        "properties": {
                          "amount": {
                            "type": "string",
                            "description": "What was paid, in the token's smallest unit: what the subscriber's wallet was charged."
                          },
                          "amount_received": {
                            "type": ["string", "null"],
                            "description": "What the receiving wallet got of `amount`, in the token's smallest unit: `amount` less the fee a Token-2022 token withholds on every transfer, which goes to the token's issuer. The same as `amount` on a token without a transfer fee. Null when Mesub could not tell; absent on events written before Mesub told it."
                          },
                          "mint": {
                            "type": ["string", "null"],
                            "description": "The token's mint address."
                          },
                          "signature": {
                            "type": ["string", "null"],
                            "description": "The signature of the transaction that paid it."
                          }
                        }
                      }
                    }
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0001",
                "object": "event",
                "type": "subscription.created",
                "created_at": "2026-01-01T00:00:05.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 1,
                "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": "subscriber@example.com",
                  "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": {
                  "first_payment": {
                    "amount": "9990000",
                    "amount_received": "9990000",
                    "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                    "signature": "5wHu1qwD4kLDLoREEPBpKvGvhbxWFxFFfr5nQq7dxAsRRDXvHRqpNvfsCvC1qzjWQhqmPtBb8LnZjKmvpehJqSWk"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.renewal_upcoming": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.renewal_upcoming",
        "operationId": "webhook_subscription_renewal_upcoming",
        "description": "A renewal is near, and whether it can pay.\n\nSent once per period, when the subscription enters the last quarter of it and three days before the charge at most, whether the charge can pay or not. Mesub reads the customer's wallet at that moment: `detail` is that reading, not a promise, and the charge itself answers with `subscription.renewed` or `subscription.payment_failed`. An endpoint receives it only if this event is selected for it.\n\nNot sent for a charge that cannot happen: on a plan with an end date, a subscription in its last period has no renewal, and gets no event.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-renewal-upcoming",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.renewal_upcoming"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "The event's own fields.",
                    "required": ["can_pay", "renewal_issue", "amount", "mint", "due_at"],
                    "properties": {
                      "can_pay": {
                        "type": "boolean",
                        "description": "Whether the charge would go through as the wallet stood when it was read."
                      },
                      "renewal_issue": {
                        "type": ["string", "null"],
                        "enum": ["balance", "authority", null],
                        "description": "`balance`: the wallet holds less than the amount, or has no account for the token. `authority`: Mesub can no longer move the tokens, the approval was replaced or is too small, or the account is frozen. Null when it can pay."
                      },
                      "amount": {
                        "type": "string",
                        "description": "What will be charged, in the token's smallest unit."
                      },
                      "mint": {
                        "type": "string",
                        "description": "The token."
                      },
                      "due_at": {
                        "type": "string",
                        "format": "date-time",
                        "description": "When the charge is due."
                      }
                    }
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0001",
                "object": "event",
                "type": "subscription.renewal_upcoming",
                "created_at": "2026-02-22T12:00:00.000Z",
                "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": "subscriber@example.com",
                  "external_id": "user_123",
                  "current_period_start": "2026-01-31T00:00:00.000Z",
                  "current_period_end": "2026-03-02T00:00:00.000Z",
                  "next_charge_at": "2026-03-02T00:00:00.000Z",
                  "next_retry_at": null,
                  "retry_deadline": null,
                  "next_retry_number": null,
                  "retries_allowed": null,
                  "access_until": "2026-03-02T00: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": {
                  "can_pay": false,
                  "renewal_issue": "balance",
                  "amount": "9990000",
                  "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                  "due_at": "2026-03-02T00:00:00.000Z"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.renewed": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.renewed",
        "operationId": "webhook_subscription_renewed",
        "description": "A period was paid.\n\nSent once per payment. `data` shows the subscription with that period paid: `detail.period_start` and `detail.period_end` name it. When Mesub records a payment late, the event comes late, and its `data` still shows the period paid. On a cancelled or paused subscription, a payment that went through gives access to the end of the period it paid, and the subscription stays cancelled or paused.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-renewed",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.renewed"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "The event's own fields.",
                    "required": ["amount", "mint", "period_start", "period_end", "signature"],
                    "properties": {
                      "amount": {
                        "type": "string",
                        "description": "What was paid, in the token's smallest unit: what the subscriber's wallet was charged."
                      },
                      "amount_received": {
                        "type": ["string", "null"],
                        "description": "What the receiving wallet got of `amount`, in the token's smallest unit: `amount` less the fee a Token-2022 token withholds on every transfer, which goes to the token's issuer. The same as `amount` on a token without a transfer fee. Null when Mesub could not tell; absent on events written before Mesub told it."
                      },
                      "mint": {
                        "type": "string",
                        "description": "The token's mint address."
                      },
                      "period_start": {
                        "type": "string",
                        "format": "date-time",
                        "description": "The start of the period paid for."
                      },
                      "period_end": {
                        "type": "string",
                        "format": "date-time",
                        "description": "Its end."
                      },
                      "signature": {
                        "type": "string",
                        "description": "The transaction."
                      }
                    }
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0001",
                "object": "event",
                "type": "subscription.renewed",
                "created_at": "2026-01-31T00:01:00.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 3,
                "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": "subscriber@example.com",
                  "external_id": "user_123",
                  "current_period_start": "2026-01-31T00:00:00.000Z",
                  "current_period_end": "2026-03-02T00:00:00.000Z",
                  "next_charge_at": "2026-03-02T00:00:00.000Z",
                  "next_retry_at": null,
                  "retry_deadline": null,
                  "next_retry_number": null,
                  "retries_allowed": null,
                  "access_until": "2026-03-02T00: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": {
                  "amount": "9990000",
                  "amount_received": "9990000",
                  "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                  "period_start": "2026-01-31T00:00:00.000Z",
                  "period_end": "2026-03-02T00:00:00.000Z",
                  "signature": "5wHu1qwD4kLDLoREEPBpKvGvhbxWFxFFfr5nQq7dxAsRRDXvHRqpNvfsCvC1qzjWQhqmPtBb8LnZjKmvpehJqSWk"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.payment_failed": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.payment_failed",
        "operationId": "webhook_subscription_payment_failed",
        "description": "A charge missed.\n\n`detail` says what comes next. On a plan with an end date, the plan can end before the next retry: `detail.next_retry_at` is then null and `detail.retries_left` is 0, and the subscription ends at the plan's end with `subscription.ended`.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-payment-failed",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.payment_failed"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "The event's own fields.",
                    "required": [
                      "reason",
                      "amount",
                      "mint",
                      "period_start",
                      "period_end",
                      "next_retry_at",
                      "retry_deadline",
                      "retries_left",
                      "retry_mode"
                    ],
                    "properties": {
                      "reason": {
                        "type": "string",
                        "description": "Why the charge did not pay, as the attempt's `reason`: an open family, in snake case. Keep a default branch."
                      },
                      "amount": {
                        "type": "string",
                        "description": "What was asked for, in the token's smallest unit."
                      },
                      "mint": {
                        "type": "string",
                        "description": "The token's mint address."
                      },
                      "period_start": {
                        "type": ["string", "null"],
                        "format": "date-time",
                        "description": "The start of the period that was due."
                      },
                      "period_end": {
                        "type": ["string", "null"],
                        "format": "date-time",
                        "description": "Its end."
                      },
                      "next_retry_at": {
                        "type": ["string", "null"],
                        "format": "date-time",
                        "description": "When Mesub retries on its own. Null on Free, when no retry is left, and when the plan ends before the retry."
                      },
                      "retry_deadline": {
                        "type": ["string", "null"],
                        "format": "date-time",
                        "description": "On Free only: when retries by hand close, or the plan's end date when that comes first."
                      },
                      "retries_left": {
                        "type": "integer",
                        "description": "How many retries the missed period still gets. 0 when none is left, and when the plan ends before the next one."
                      },
                      "retry_mode": {
                        "type": ["string", "null"],
                        "enum": ["scheduled", "manual", null],
                        "description": "Who retries: Mesub on its own, or you by hand. Null when no retry is left."
                      }
                    }
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0001",
                "object": "event",
                "type": "subscription.payment_failed",
                "created_at": "2026-01-31T00:01:00.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 3,
                "data": {
                  "id": "cmg1x0abc0001",
                  "status": "unpaid",
                  "paused": false,
                  "end_reason": null,
                  "late_reason": "insufficient_balance",
                  "stop_reason": null,
                  "access": true,
                  "payment_status": "late",
                  "blocked_since": null,
                  "blocked_reason": null,
                  "plan": "pro",
                  "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                  "email": "subscriber@example.com",
                  "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": "2026-02-01T00:01:00.000Z",
                  "retry_deadline": null,
                  "next_retry_number": 1,
                  "retries_allowed": 3,
                  "access_until": "2026-02-03T00:05:00.000Z",
                  "closable": false,
                  "closable_from": null,
                  "created_at": "2026-01-01T00:00:00.000Z",
                  "confirmed_at": "2026-01-01T00:00:05.000Z"
                },
                "detail": {
                  "reason": "insufficient_balance",
                  "amount": "9990000",
                  "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                  "period_start": "2026-01-31T00:00:00.000Z",
                  "period_end": "2026-03-02T00:00:00.000Z",
                  "next_retry_at": "2026-02-01T00:01:00.000Z",
                  "retry_deadline": null,
                  "retries_left": 3,
                  "retry_mode": "scheduled"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.stopped": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.stopped",
        "operationId": "webhook_subscription_stopped",
        "description": "Mesub stopped charging after missed payments. `data.stop_reason` says why: `out_of_retries`, `retry_deadline_passed` or `authority_closed`.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-stopped",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.stopped"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "The event's own fields.",
                    "required": ["reason"],
                    "properties": {
                      "reason": {
                        "type": "string",
                        "description": "Why it stopped: the last charge's `reason` when its retries ran out, or `retry_deadline_passed` on Free, when the missed period ended with no retry by hand that paid. An open family, in snake case: keep a default branch."
                      }
                    }
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0001",
                "object": "event",
                "type": "subscription.stopped",
                "created_at": "2026-02-04T00:04:00.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 3,
                "data": {
                  "id": "cmg1x0abc0001",
                  "status": "stopped",
                  "paused": false,
                  "end_reason": null,
                  "late_reason": "insufficient_balance",
                  "stop_reason": "out_of_retries",
                  "access": false,
                  "payment_status": "none",
                  "blocked_since": null,
                  "blocked_reason": null,
                  "plan": "pro",
                  "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                  "email": "subscriber@example.com",
                  "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": null,
                  "closable": false,
                  "closable_from": null,
                  "created_at": "2026-01-01T00:00:00.000Z",
                  "confirmed_at": "2026-01-01T00:00:05.000Z"
                },
                "detail": {
                  "reason": "insufficient_balance"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.cancelled": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.cancelled",
        "operationId": "webhook_subscription_cancelled",
        "description": "The customer cancelled. Access runs to the period's end.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-cancelled",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.cancelled"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "Empty on this event.",
                    "properties": {}
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "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": "subscriber@example.com",
                  "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": {}
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.resumed": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.resumed",
        "operationId": "webhook_subscription_resumed",
        "description": "A cancellation was undone.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-resumed",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.resumed"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "Empty on this event.",
                    "properties": {}
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0001",
                "object": "event",
                "type": "subscription.resumed",
                "created_at": "2026-01-20T00:00:00.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 3,
                "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": "subscriber@example.com",
                  "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": {}
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.ended": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.ended",
        "operationId": "webhook_subscription_ended",
        "description": "It is over. `data.end_reason` says why.\n\nA cancellation that reached its end (its expiry: the end of the period it paid for) sends this event with `end_reason: \"cancelled\"`, once per cancellation, once a read of the chain after the expiry shows the cancellation still in place: usually within half an hour of the expiry. `detail.changed_at` is the expiry, to the millisecond, and the moment `data` describes, `status: ended`. The `subscription.access_revoked` of that expiry is sent at the expiry itself, from Mesub's records, so it comes first; a resume signed in the wallet before the expiry and seen late by Mesub can still follow that `access_revoked` (then `subscription.resumed` and `subscription.access_granted`, and no `subscription.ended`). The subscription can still be closed afterwards, which sends this event again with `end_reason: \"closed\"` (or `\"cancelled\"` when Mesub finds it closed outside Mesub).\n\nOn a plan with an end date, a subscription still running or late is ended at the plan's end, within minutes of it, whatever period it paid for: this event then carries `end_reason: \"plan_ended\"`.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-ended",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.ended"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes. For a cancellation that reached its end, when Mesub wrote the event, never before an event written earlier; `detail.changed_at` is then the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "The event's own fields: empty, except on a cancellation reaching its end.",
                    "properties": {
                      "changed_at": {
                        "type": "string",
                        "format": "date-time",
                        "description": "On a cancellation that reached its end only: its expiry, to the millisecond, however much later Mesub read the chain and wrote the event, and the moment `data` describes. Absent otherwise."
                      },
                      "end_reason": {
                        "type": "string",
                        "enum": ["cancelled"],
                        "description": "On a cancellation that reached its end only: `cancelled`, as `data.end_reason` says it. Absent otherwise, and on an event written before Mesub added it."
                      }
                    }
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0001",
                "object": "event",
                "type": "subscription.ended",
                "created_at": "2026-01-31T00:01:00.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 3,
                "data": {
                  "id": "cmg1x0abc0001",
                  "status": "ended",
                  "paused": false,
                  "end_reason": "plan_ended",
                  "late_reason": null,
                  "stop_reason": null,
                  "access": false,
                  "payment_status": "none",
                  "blocked_since": null,
                  "blocked_reason": null,
                  "plan": "pro",
                  "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                  "email": "subscriber@example.com",
                  "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": null,
                  "closable": true,
                  "closable_from": null,
                  "created_at": "2026-01-01T00:00:00.000Z",
                  "confirmed_at": "2026-01-01T00:00:05.000Z"
                },
                "detail": {}
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.expired": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.expired",
        "operationId": "webhook_subscription_expired",
        "description": "A checkout was started and never signed.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-expired",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.expired"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "Empty on this event.",
                    "properties": {}
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0001",
                "object": "event",
                "type": "subscription.expired",
                "created_at": "2026-01-01T00:30:00.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 1,
                "data": {
                  "id": "cmg1x0abc0001",
                  "status": "expired",
                  "paused": false,
                  "end_reason": null,
                  "late_reason": null,
                  "stop_reason": null,
                  "access": false,
                  "payment_status": "none",
                  "blocked_since": null,
                  "blocked_reason": null,
                  "plan": "pro",
                  "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                  "email": "subscriber@example.com",
                  "external_id": "user_123",
                  "current_period_start": null,
                  "current_period_end": null,
                  "next_charge_at": null,
                  "next_retry_at": null,
                  "retry_deadline": null,
                  "next_retry_number": null,
                  "retries_allowed": null,
                  "access_until": null,
                  "closable": false,
                  "closable_from": null,
                  "created_at": "2026-01-01T00:00:00.000Z",
                  "confirmed_at": null
                },
                "detail": {}
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.access_granted": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.access_granted",
        "operationId": "webhook_subscription_access_granted",
        "description": "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`.\n\nDrive the customer's access from `subscription.access_granted` and `subscription.access_revoked` alone: they are sent only when access actually changes, and every change sends one. The other events say why. Apply them in the order of `sequence`, since deliveries may arrive in any order: the last one applied says whether the customer has access. `detail.changed_at` is the moment access started, and `data.access` is true.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-access-granted",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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. Check it over the raw bytes received, before parsing them.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.access_granted"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "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."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "The event's own fields.",
                    "required": ["changed_at"],
                    "properties": {
                      "changed_at": {
                        "type": "string",
                        "format": "date-time",
                        "description": "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_reason": {
                        "type": ["string", "null"],
                        "enum": [
                          "puller_not_allowed",
                          "receiver_not_allowed",
                          "receiver_account_missing",
                          "receiver_account_frozen",
                          "receiver_account_needs_memo",
                          "receiver_account_confidential_only",
                          "mint_gone",
                          "mint_paused",
                          "mint_transfer_hook",
                          "mesub",
                          null
                        ],
                        "description": "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_until": {
                        "type": ["string", "null"],
                        "format": "date-time",
                        "description": "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."
                      }
                    }
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "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": "subscriber@example.com",
                  "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
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.access_revoked": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.access_revoked",
        "operationId": "webhook_subscription_access_revoked",
        "description": "Access ended: the subscription grants no access now, and did just before. It stopped, ended, failed a payment on Free, a cancellation reached its end, its plan ended, or a collection blocked on Mesub's or the merchant's side reached the end of the access it keeps (`access_until`). Sent at the change, or, for a date that passed with nothing else happening, a few seconds after it: `detail.changed_at` is then that date, to the millisecond.\n\nDrive the customer's access from `subscription.access_granted` and `subscription.access_revoked` alone: they are sent only when access actually changes, and every change sends one. The other events say why. Apply them in the order of `sequence`, since deliveries may arrive in any order: the last one applied says whether the customer has access. `data.access` is false.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-access-revoked",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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. Check it over the raw bytes received, before parsing them.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.access_revoked"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "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."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "The event's own fields.",
                    "required": ["changed_at"],
                    "properties": {
                      "changed_at": {
                        "type": "string",
                        "format": "date-time",
                        "description": "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_reason": {
                        "type": ["string", "null"],
                        "enum": [
                          "puller_not_allowed",
                          "receiver_not_allowed",
                          "receiver_account_missing",
                          "receiver_account_frozen",
                          "receiver_account_needs_memo",
                          "receiver_account_confidential_only",
                          "mint_gone",
                          "mint_paused",
                          "mint_transfer_hook",
                          "mesub",
                          null
                        ],
                        "description": "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_until": {
                        "type": ["string", "null"],
                        "format": "date-time",
                        "description": "The date access ended: the end of a paid period, of a cancellation, of the plan, or of the access a held subscription keeps (`blocked_reason`). Never after `changed_at`. Null when access ended with no date of its own, a stop for example. Absent on an event written before Mesub added it."
                      }
                    }
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0004",
                "object": "event",
                "type": "subscription.access_revoked",
                "created_at": "2026-01-31T00:00:04.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 4,
                "data": {
                  "id": "cmg1x0abc0001",
                  "status": "ended",
                  "paused": false,
                  "end_reason": "cancelled",
                  "late_reason": null,
                  "stop_reason": null,
                  "access": false,
                  "payment_status": "none",
                  "blocked_since": null,
                  "blocked_reason": null,
                  "plan": "pro",
                  "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                  "email": "subscriber@example.com",
                  "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": null,
                  "closable": true,
                  "closable_from": null,
                  "created_at": "2026-01-01T00:00:00.000Z",
                  "confirmed_at": "2026-01-01T00:00:05.000Z"
                },
                "detail": {
                  "changed_at": "2026-01-31T00:00:00.000Z",
                  "blocked_reason": null,
                  "access_until": "2026-01-31T00:00:00.000Z"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.paused": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.paused",
        "operationId": "webhook_subscription_paused",
        "description": "A seat was parked: the project now holds more subscribers than its tier allows (it moved to a smaller tier, or a subscription confirmed late filled it), and Mesub stopped charging the newest ones rather than any within the limit. Not a pause the subscriber asked for: nothing changed on chain, and the status stays as it was.\n\nThe subscription keeps access to the end of the period it paid for, and no further: `data.paused` is true, `data.next_charge_at` null. Its `subscription.access_revoked` comes when that access ends, or right after this event, with a higher `sequence`, when it had already ended. The project moving back to a tier with room sends `subscription.unpaused`.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-paused",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.paused"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "Empty on this event.",
                    "properties": {}
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0001",
                "object": "event",
                "type": "subscription.paused",
                "created_at": "2026-01-11T00:00:00.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 3,
                "data": {
                  "id": "cmg1x0abc0001",
                  "status": "active",
                  "paused": true,
                  "end_reason": null,
                  "late_reason": null,
                  "stop_reason": null,
                  "access": true,
                  "payment_status": "none",
                  "blocked_since": null,
                  "blocked_reason": null,
                  "plan": "pro",
                  "wallet": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
                  "email": "subscriber@example.com",
                  "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": null,
                  "created_at": "2026-01-01T00:00:00.000Z",
                  "confirmed_at": "2026-01-01T00:00:05.000Z"
                },
                "detail": {}
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "subscription.unpaused": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "subscription.unpaused",
        "operationId": "webhook_subscription_unpaused",
        "description": "A parked seat is back: the project's tier has room for it again, and Mesub charges it again, at the end of the period it paid for, or at once when that period is over. `data.paused` is false.\n\nIts `subscription.access_granted` follows, with a higher `sequence`, when its access had ended while it was parked.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/subscription-unpaused",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: the same in every delivery of it, to every endpoint and on every retry, and on [Retrieve an event](/reference/events/retrieve). `webhook-id` names the delivery, not the event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["subscription.unpaused"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
                  },
                  "detail": {
                    "type": "object",
                    "description": "Empty on this event.",
                    "properties": {}
                  },
                  "test": {
                    "type": "boolean",
                    "description": "True on a test delivery of this event, sent by hand from the dashboard. Absent otherwise."
                  }
                }
              },
              "example": {
                "id": "evt_cmg1x0evt0001",
                "object": "event",
                "type": "subscription.unpaused",
                "created_at": "2026-01-13T00:00:00.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 4,
                "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": "subscriber@example.com",
                  "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": {}
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    },
    "test": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "test",
        "operationId": "webhook_test",
        "description": "You sent one from the dashboard, with no event picked. It carries a made-up subscription whose id is `sub_test`.\n\nMesub posts this to every endpoint of the project subscribed to it. Answer with a 2xx within 10 seconds.",
        "x-mesub": {
          "href": "reference/webhooks/test",
          "guide": {
            "label": "Webhooks",
            "href": "docs/webhooks"
          }
        },
        "parameters": [
          {
            "name": "webhook-id",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-timestamp",
            "in": "header",
            "required": true,
            "description": "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.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "webhook-signature",
            "in": "header",
            "required": true,
            "description": "`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.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "object",
                  "type",
                  "created_at",
                  "network",
                  "api_version",
                  "data",
                  "detail"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "The event's id: `evt_test` on a test, which stands for no event."
                  },
                  "object": {
                    "type": "string",
                    "enum": ["event"],
                    "description": "Always `event`."
                  },
                  "type": {
                    "type": "string",
                    "enum": ["test"],
                    "description": "The event's name."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the event happened: the moment `data` describes."
                  },
                  "network": {
                    "type": "string",
                    "enum": ["devnet", "mainnet-beta"],
                    "description": "The Solana network the subscription is on."
                  },
                  "api_version": {
                    "type": "string",
                    "enum": ["2026-10-02"],
                    "description": "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."
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Always 1: a test delivery stands alone."
                  },
                  "data": {
                    "$ref": "#/components/schemas/Subscription",
                    "description": "A made-up subscription, `sub_test`, the same on every test."
                  },
                  "detail": {
                    "type": "object",
                    "description": "Empty on this event.",
                    "properties": {}
                  },
                  "test": {
                    "type": "boolean",
                    "enum": [true],
                    "description": "True on a delivery sent by hand from the dashboard."
                  }
                }
              },
              "example": {
                "id": "evt_test",
                "object": "event",
                "type": "test",
                "created_at": "2026-01-10T09:30:00.000Z",
                "network": "mainnet-beta",
                "api_version": "2026-10-02",
                "sequence": 1,
                "data": {
                  "id": "sub_test",
                  "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": "11111111111111111111111111111111",
                  "email": "subscriber@example.com",
                  "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:00.000Z"
                },
                "detail": {},
                "test": true
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Anything else, a redirect included, counts as a failure and is retried."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "The project's API key, which starts with `SUB_`. Keep it on your server."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "The body of every error.",
        "required": ["statusCode", "message", "error", "code", "retryable", "request_id"],
        "properties": {
          "statusCode": {
            "type": "integer",
            "description": "The HTTP status, repeated."
          },
          "message": {
            "description": "A sentence for a person. It may be reworded any day: do not branch on it. A validation error answers a list of reasons.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "error": {
            "type": "string",
            "description": "The status's name."
          },
          "code": {
            "type": "string",
            "description": "What a program reads. A code is never renamed or reused, but new ones are added: keep a default branch."
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether the same request, sent again unchanged, may succeed later."
          },
          "request_id": {
            "type": "string",
            "pattern": "^req_[0-9A-Za-z]{24}$",
            "description": "The id of the request, as `Mesub-Request-Id` says it. Quote it to Mesub support. It names this HTTP request, and has nothing to do with the `requestId` of an agent's consent flow."
          }
        }
      },
      "Project": {
        "type": "object",
        "required": ["id"],
        "properties": {
          "id": {
            "type": "string",
            "description": "The id of the project the API key belongs to."
          }
        }
      },
      "AccessAttempt": {
        "type": "object",
        "description": "One charge, as you may show it to the customer it concerns.",
        "required": ["outcome", "reason", "amount", "attempted_at", "signature"],
        "properties": {
          "outcome": {
            "type": "string",
            "enum": ["paid", "skipped", "rejected", "blocked"],
            "description": "`skipped`: nothing was sent because the chain said it could not work. `rejected`: refused for a reason that is the customer's, by the program or by the check made before sending (then `signature` is null). `blocked`: not tried, and never the customer's doing."
          },
          "reason": {
            "type": ["string", "null"],
            "description": "Why it did not pay, null on a paid one. Short and stable, in snake case, and an open family rather than a closed list: Mesub adds reasons, so keep a default branch. Among them: `insufficient_balance` (the wallet held less than the period costs), `approval_revoked`, `authority_closed` (the wallet's subscription authority closed for good), `token_account_frozen`, `plan_ended`, `plan_removed`, `plan_replaced`, `closed_elsewhere` (the subscription account closed outside Mesub), `cancelled` (cancelled, and past the end of the period paid for), and, on a `blocked` one, the plan's setup (`puller_not_allowed`, `receiver_not_allowed`, `receiver_account_missing`, `receiver_account_frozen`, `receiver_account_needs_memo`, `receiver_account_confidential_only`, `mint_gone`, `mint_paused`, `mint_transfer_hook`). A refusal Mesub does not name is `program_<N>` (the program's or the token program's error code N) or `solana_<N>` (Solana's own). `late_reason` on the subscription is the reading of it a program should branch on."
          },
          "amount": {
            "type": "string",
            "description": "In the token's smallest unit, as a string."
          },
          "attempted_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it was tried."
          },
          "signature": {
            "type": ["string", "null"],
            "description": "The transaction. Null when nothing was sent."
          }
        }
      },
      "AccessAnswer": {
        "type": "object",
        "title": "With a plan",
        "description": "Whether the customer has access to the plan, and what explains the answer.",
        "required": [
          "wallet",
          "plan",
          "subscription_id",
          "access",
          "status",
          "paused",
          "end_reason",
          "late_reason",
          "stop_reason",
          "payment_status",
          "blocked_since",
          "blocked_reason",
          "subscribed_since",
          "first_subscribed_at",
          "current_period_end",
          "cancelled_at",
          "access_until",
          "closable",
          "closable_from",
          "next_charge_at",
          "next_retry_at",
          "retry_deadline",
          "revalidate_after"
        ],
        "properties": {
          "wallet": {
            "type": ["string", "null"],
            "description": "The wallet the answer is about. Asked by `wallet`, that wallet. Asked by `external_id` or `email`, the one that pays, or null when the customer has nothing on this plan."
          },
          "plan": {
            "type": "string",
            "description": "The plan's slug."
          },
          "subscription_id": {
            "type": ["string", "null"],
            "description": "The subscription this answer is about: the one whose `status`, `access` and dates these are. Use it to [cancel](/reference/manage/cancel), [resume](/reference/manage/resume), [close](/reference/manage/close) or [retry](/reference/subscriptions/retry) it without listing the customer's subscriptions first. When the customer has several on the plan, the one in force: among their wallets, the most recent that grants access, else the most recent; on one wallet, its latest, never a checkout left pending beside a subscription that runs, nor one left to expire. Null when the customer has nothing on this plan, with `status` `none`."
          },
          "access": {
            "type": "boolean",
            "description": "The only field a guard needs. False from the plan's end date on, whatever period was paid for."
          },
          "status": {
            "type": "string",
            "enum": [
              "none",
              "pending",
              "active",
              "unpaid",
              "cancelled",
              "stopped",
              "ended",
              "failed",
              "superseded"
            ],
            "description": "Where the subscription stands. `none` for a customer who never subscribed to this plan. Mesub may add a status later: keep a default branch. 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: gate on `access`."
          },
          "paused": {
            "type": "boolean",
            "description": "True on a seat parked over the project's subscriber limit: the status stays as it was, nothing is charged, and access runs to the end of the period already paid."
          },
          "end_reason": {
            "type": ["string", "null"],
            "enum": [
              "cancelled",
              "plan_removed",
              "plan_replaced",
              "plan_ended",
              "closed_elsewhere",
              "closed",
              null
            ],
            "description": "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_reason": {
            "type": ["string", "null"],
            "enum": ["insufficient_balance", "approval_revoked", "authority_closed", null],
            "description": "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."
          },
          "stop_reason": {
            "type": ["string", "null"],
            "enum": ["out_of_retries", "retry_deadline_passed", "authority_closed", null],
            "description": "Why it stopped. Set only when `status` is `stopped`. Null on every other status, and on a subscription that stopped before Mesub recorded why.\n\n- `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.\n- `retry_deadline_passed`: on the Free tier, the last day to pay the missed payment passed.\n- `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."
          },
          "payment_status": {
            "type": "string",
            "enum": ["paid", "late", "none"],
            "description": "`late` while a charge missed and a retry is pending. `none` where nothing is being billed."
          },
          "blocked_since": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "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_reason": {
            "type": ["string", "null"],
            "enum": [
              "puller_not_allowed",
              "receiver_not_allowed",
              "receiver_account_missing",
              "receiver_account_frozen",
              "receiver_account_needs_memo",
              "receiver_account_confidential_only",
              "mint_gone",
              "mint_paused",
              "mint_transfer_hook",
              "mesub",
              null
            ],
            "description": "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."
          },
          "subscribed_since": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "The start of the current subscription."
          },
          "first_subscribed_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "The start of the very first one. Differs for a customer who left and came back."
          },
          "current_period_end": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "The end of the period last paid for."
          },
          "cancelled_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "When they asked to stop, not when access ends."
          },
          "access_until": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "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."
          },
          "closable": {
            "type": "boolean",
            "description": "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_from": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "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`."
          },
          "next_charge_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "The next charge. 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_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "The next retry of a missed charge. Null when the plan ends before it."
          },
          "retry_deadline": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "On Free only: when retries by hand close for the missed period, or the plan's end date when that comes first."
          },
          "attempts": {
            "type": "array",
            "description": "The last charges, newest first, five at most. Present only when asked with `attempts=true`.",
            "items": {
              "$ref": "#/components/schemas/AccessAttempt"
            }
          },
          "revalidate_after": {
            "type": "integer",
            "minimum": 10,
            "maximum": 300,
            "description": "Seconds this answer stays true. The SDK caches it for that long."
          }
        }
      },
      "AccessList": {
        "type": "object",
        "title": "Without a plan",
        "description": "Every plan of the project the customer has anything on, sorted by slug. Maybe none.",
        "required": ["plans", "revalidate_after"],
        "properties": {
          "plans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccessAnswer"
            },
            "description": "One answer per plan, as with `plan`."
          },
          "revalidate_after": {
            "type": "integer",
            "description": "Seconds this list stays true: the soonest of its plans'."
          }
        }
      },
      "Plan": {
        "type": "object",
        "description": "A plan as your server reads it, to build a pricing page of your own.",
        "required": [
          "slug",
          "name",
          "description",
          "project_name",
          "logo_url",
          "amount",
          "amount_display",
          "amount_usd",
          "decimals",
          "symbol",
          "mint",
          "token",
          "period_hours",
          "network",
          "status",
          "available",
          "ends_at"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "How your code names the plan."
          },
          "name": {
            "type": "string",
            "description": "The plan's name as you filled it, or its slug when you filled none."
          },
          "description": {
            "type": ["string", "null"]
          },
          "project_name": {
            "type": "string",
            "description": "The project's name."
          },
          "logo_url": {
            "type": ["string", "null"],
            "description": "The project's logo."
          },
          "amount": {
            "type": "string",
            "description": "The price per period, in the token's smallest unit, as a string."
          },
          "amount_display": {
            "type": "string",
            "description": "The price as a person counts it: `9.99`."
          },
          "amount_usd": {
            "type": ["string", "null"],
            "description": "The price in US dollars, as a decimal string: `9.99`. An estimate, counted at the latest price Mesub knows for the token. Null when Mesub has no price for it."
          },
          "decimals": {
            "type": "integer",
            "description": "The token's decimals."
          },
          "symbol": {
            "type": ["string", "null"],
            "description": "The token's symbol, for a token Mesub vouches for: USDC, USDT or PYUSD. Null for any other token, whose symbol is under `token`."
          },
          "mint": {
            "type": "string",
            "description": "The token's mint address."
          },
          "token": {
            "type": "object",
            "description": "What Mesub found for the token, by its exact mint. Nothing in it comes from you. A symbol is free text on chain, and a token that copies a known one is a common scam: show a token that is not `verified` as unverified, and keep its `mint` in view.",
            "required": ["symbol", "name", "logo_url", "verified"],
            "properties": {
              "symbol": {
                "type": ["string", "null"],
                "description": "The token's symbol. Null when none was found, and on an unverified token whose symbol is that of a stablecoin Mesub vouches for."
              },
              "name": {
                "type": ["string", "null"],
                "description": "The token's name. Null when none was found, and on an unverified token whose name is that of a stablecoin Mesub vouches for."
              },
              "logo_url": {
                "type": ["string", "null"],
                "description": "The token's logo. Null when none was found."
              },
              "verified": {
                "type": "boolean",
                "description": "True for a token Mesub vouches for, and for one Jupiter marks as verified."
              }
            }
          },
          "period_hours": {
            "type": "integer",
            "description": "The length of a period, in hours. 720 is thirty days."
          },
          "network": {
            "type": "string",
            "enum": ["devnet", "mainnet-beta"],
            "description": "The Solana cluster the plan is on."
          },
          "status": {
            "type": "string",
            "enum": ["active", "sunset"],
            "description": "`sunset`: closed to new subscribers, its subscribers still paying until `ends_at`."
          },
          "available": {
            "type": "boolean",
            "description": "Whether it takes new subscribers now: active, before its end date, and the project not full."
          },
          "ends_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "The plan's end date: when it stops charging, and when access stops for every subscriber, whatever period they paid for. Set on every sunset plan. Null when it has no end."
          }
        }
      },
      "Subscription": {
        "type": "object",
        "description": "One subscription to one of your project's plans.",
        "required": [
          "id",
          "status",
          "paused",
          "end_reason",
          "late_reason",
          "stop_reason",
          "access",
          "payment_status",
          "blocked_since",
          "blocked_reason",
          "plan",
          "wallet",
          "email",
          "external_id",
          "current_period_start",
          "current_period_end",
          "next_charge_at",
          "next_retry_at",
          "retry_deadline",
          "next_retry_number",
          "retries_allowed",
          "access_until",
          "closable",
          "closable_from",
          "created_at",
          "confirmed_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "unpaid",
              "cancelled",
              "stopped",
              "ended",
              "failed",
              "expired",
              "superseded"
            ],
            "description": "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."
          },
          "paused": {
            "type": "boolean",
            "description": "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_reason": {
            "type": ["string", "null"],
            "enum": [
              "cancelled",
              "plan_removed",
              "plan_replaced",
              "plan_ended",
              "closed_elsewhere",
              "closed",
              null
            ],
            "description": "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_reason": {
            "type": ["string", "null"],
            "enum": ["insufficient_balance", "approval_revoked", "authority_closed", null],
            "description": "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."
          },
          "stop_reason": {
            "type": ["string", "null"],
            "enum": ["out_of_retries", "retry_deadline_passed", "authority_closed", null],
            "description": "Why it stopped. Set only when `status` is `stopped`. Null on every other status, and on a subscription that stopped before Mesub recorded why.\n\n- `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.\n- `retry_deadline_passed`: on the Free tier, the last day to pay the missed payment passed.\n- `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."
          },
          "access": {
            "type": "boolean",
            "description": "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_status": {
            "type": "string",
            "enum": ["paid", "late", "none"],
            "description": "`late` while a charge missed and a retry is pending. `none` where nothing is being billed."
          },
          "blocked_since": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "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_reason": {
            "type": ["string", "null"],
            "enum": [
              "puller_not_allowed",
              "receiver_not_allowed",
              "receiver_account_missing",
              "receiver_account_frozen",
              "receiver_account_needs_memo",
              "receiver_account_confidential_only",
              "mint_gone",
              "mint_paused",
              "mint_transfer_hook",
              "mesub",
              null
            ],
            "description": "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."
          },
          "plan": {
            "type": ["string", "null"],
            "description": "The plan's slug."
          },
          "wallet": {
            "type": "string",
            "description": "The wallet that pays."
          },
          "email": {
            "type": ["string", "null"],
            "description": "Where the customer's notices go, as given at creation."
          },
          "external_id": {
            "type": ["string", "null"],
            "description": "Your own id for the customer, as given at creation."
          },
          "current_period_start": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "The start of the period last paid for. Null before the first."
          },
          "current_period_end": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "The end of that period."
          },
          "next_charge_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "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_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "The next retry on a late one. Null when the plan ends before it."
          },
          "retry_deadline": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "On Free only: when retries by hand close for the missed period, or the plan's end date when that comes first."
          },
          "next_retry_number": {
            "type": ["integer", "null"],
            "description": "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_allowed": {
            "type": ["integer", "null"],
            "description": "How many retries the missed period gets. Null unless Mesub retries on its own."
          },
          "access_until": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "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."
          },
          "closable": {
            "type": "boolean",
            "description": "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_from": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "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_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it was reserved."
          },
          "confirmed_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "When its first payment landed."
          }
        }
      },
      "SubscriptionList": {
        "type": "object",
        "required": ["data", "has_more"],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Subscription"
            },
            "description": "Newest first. Maybe empty."
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether another page follows."
          }
        }
      },
      "Attempt": {
        "type": "object",
        "description": "One charge of a subscription.",
        "required": [
          "id",
          "attempted_at",
          "outcome",
          "reason",
          "amount",
          "amount_received",
          "amount_usd",
          "signature",
          "retry",
          "retry_number",
          "retries_allowed",
          "period_start"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "attempted_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it was tried."
          },
          "outcome": {
            "type": "string",
            "enum": ["paid", "skipped", "rejected", "blocked"],
            "description": "`skipped`: nothing was sent because the chain said it could not work. `rejected`: refused for a reason that is the customer's, by the program or by the check made before sending (then `signature` is null). `blocked`: not tried, and never the customer's doing."
          },
          "reason": {
            "type": ["string", "null"],
            "description": "Why it did not pay, null on a paid one. Short and stable, in snake case, and an open family rather than a closed list: Mesub adds reasons, so keep a default branch. Among them: `insufficient_balance` (the wallet held less than the period costs), `approval_revoked`, `authority_closed` (the wallet's subscription authority closed for good), `token_account_frozen`, `plan_ended`, `plan_removed`, `plan_replaced`, `closed_elsewhere` (the subscription account closed outside Mesub), `cancelled` (cancelled, and past the end of the period paid for), and, on a `blocked` one, the plan's setup (`puller_not_allowed`, `receiver_not_allowed`, `receiver_account_missing`, `receiver_account_frozen`, `receiver_account_needs_memo`, `receiver_account_confidential_only`, `mint_gone`, `mint_paused`, `mint_transfer_hook`). A refusal Mesub does not name is `program_<N>` (the program's or the token program's error code N) or `solana_<N>` (Solana's own). `late_reason` on the subscription is the reading of it a program should branch on."
          },
          "amount": {
            "type": "string",
            "description": "In the token's smallest unit, as a string. What a paid one moved, what any other asked for."
          },
          "amount_received": {
            "type": ["string", "null"],
            "description": "On a paid one, what the receiving wallet got of `amount`, in the token's smallest unit, as a string: `amount` less the fee a Token-2022 token withholds on every transfer, which goes to the token's issuer. The same as `amount` on a token without a transfer fee. Null on any other outcome, and on a payment Mesub recorded before it kept this figure."
          },
          "amount_usd": {
            "type": ["string", "null"],
            "description": "`amount` in US dollars, as a decimal string, at the token's price at the instant of the charge. Written once. Null until Mesub has priced it, and for good when it found no price."
          },
          "signature": {
            "type": ["string", "null"],
            "description": "The transaction. Null when nothing was sent."
          },
          "retry": {
            "type": "boolean",
            "description": "Whether the subscription had already failed on this period."
          },
          "retry_number": {
            "type": ["integer", "null"],
            "description": "Which retry of the plan's it was: 1 is the first after the missed charge. Null on a first try."
          },
          "retries_allowed": {
            "type": ["integer", "null"],
            "description": "Out of how many, as recorded when it ran."
          },
          "period_start": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "The start of the period it was for."
          }
        }
      },
      "AttemptList": {
        "type": "object",
        "required": ["data", "has_more", "paid"],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attempt"
            },
            "description": "Newest first. Maybe empty."
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether another page follows."
          },
          "paid": {
            "type": "object",
            "description": "Counted over every paid charge since the subscription began, not over the page.",
            "required": ["count", "amount"],
            "properties": {
              "count": {
                "type": "integer"
              },
              "amount": {
                "type": "string",
                "description": "The sum, in the token's smallest unit, as a string."
              }
            }
          }
        }
      },
      "Costs": {
        "type": "object",
        "description": "What subscribing costs the wallet in SOL, in lamports, as strings.",
        "required": ["rent", "fee", "total"],
        "properties": {
          "rent": {
            "type": "object",
            "description": "A deposit the customer gets back when the subscription is closed.",
            "required": ["subscription", "authority", "total"],
            "properties": {
              "subscription": {
                "type": "string"
              },
              "authority": {
                "type": ["string", "null"],
                "description": "Null when the customer already has an authority for that token."
              },
              "total": {
                "type": "string"
              }
            }
          },
          "fee": {
            "type": "object",
            "description": "Spent, not returned.",
            "required": ["signatures", "per_signature", "priority", "total"],
            "properties": {
              "signatures": {
                "type": "integer"
              },
              "per_signature": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "total": {
                "type": "string"
              }
            }
          },
          "total": {
            "type": "string"
          }
        }
      },
      "SubscribeResult": {
        "type": "object",
        "required": [
          "subscription",
          "transaction",
          "last_valid_block_height",
          "transaction_expires_at",
          "costs",
          "terms"
        ],
        "properties": {
          "subscription": {
            "type": "object",
            "required": ["id", "status"],
            "properties": {
              "id": {
                "type": "string",
                "description": "Keep it: every later call names it."
              },
              "status": {
                "type": "string",
                "enum": ["pending"]
              }
            }
          },
          "transaction": {
            "type": "string",
            "description": "Base64, signed by nobody. The wallet signs it without sending it."
          },
          "last_valid_block_height": {
            "type": "string",
            "description": "The block height after which the transaction can no longer land."
          },
          "transaction_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "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."
          },
          "costs": {
            "$ref": "#/components/schemas/Costs"
          },
          "terms": {
            "type": "object",
            "required": ["message", "expires_at"],
            "properties": {
              "message": {
                "type": "string",
                "description": "Plain text, one fact per line. The wallet signs it as is, before the transaction."
              },
              "expires_at": {
                "type": "string",
                "format": "date-time",
                "description": "Five minutes after the terms were handed out. Submit before it."
              }
            }
          }
        }
      },
      "Confirmed": {
        "type": "object",
        "required": ["subscription"],
        "properties": {
          "subscription": {
            "$ref": "#/components/schemas/Subscription"
          },
          "reason": {
            "type": "string",
            "description": "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."
          },
          "reason_code": {
            "type": "string",
            "description": "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:\n\n- `not_our_transaction`: the signature is not of a transaction Mesub co-signed over terms the wallet signed for this subscription.\n- `transaction_failed`: the transaction landed and the chain refused it: nothing it held happened.\n- `transaction_expired`: its blockhash ran out before it landed: it never will.\n- `nothing_landed`: on a submit, no subscription stands at its address yet: it stays `pending`.\n- `subscription_not_on_chain`: on a cancel or a resume, the subscription no longer exists on chain.\n- `subscription_mismatch`: what landed names another wallet, plan, amount or billing period than the subscription reserved, which is now `failed`.\n- `dev_not_running`: Mesub's own Dev plan landed cancelled or part paid, and is now `failed`.\n- `earlier_subscription_running`: a comeback paid while the subscription it came back over ran again: Mesub was told and sorts it out.\n- `subscription_remade`: on a submit, it landed, and the subscription was closed and made again without Mesub since: now `failed`.\n- `cancellation_not_landed`: the subscription stands on chain with no cancellation.\n- `resume_not_landed`: the subscription stands on chain still cancelled.\n- `close_not_landed`: the subscription still stands on chain.\n- `subscription_changed`: the subscription moved on while the cancellation was read, and was left as it is.\n- `subscription_not_cancelled`: on a resume, the subscription is no longer cancelled: nothing to resume.\n- `cancelled_subscription_remade`: on a resume, the subscription cancelled was closed and made again without Mesub since: it stays `cancelled`.\n- `resume_unverified`: on a resume, the subscription on chain cannot be told to be the one cancelled: it stays `cancelled`.",
            "examples": [
              "not_our_transaction",
              "transaction_failed",
              "transaction_expired",
              "nothing_landed",
              "subscription_not_on_chain",
              "subscription_mismatch",
              "dev_not_running",
              "earlier_subscription_running",
              "subscription_remade",
              "cancellation_not_landed",
              "resume_not_landed",
              "close_not_landed",
              "subscription_changed",
              "subscription_not_cancelled",
              "cancelled_subscription_remade",
              "resume_unverified"
            ]
          }
        },
        "dependentRequired": {
          "reason": ["reason_code"],
          "reason_code": ["reason"]
        }
      },
      "UnsignedTransaction": {
        "type": "object",
        "required": ["transaction", "last_valid_block_height", "transaction_expires_at"],
        "properties": {
          "transaction": {
            "type": "string",
            "description": "Base64, signed by nobody. The subscription's own wallet signs and sends it."
          },
          "last_valid_block_height": {
            "type": "string",
            "description": "The block height after which the transaction can no longer land."
          },
          "transaction_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "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."
          }
        }
      },
      "Event": {
        "type": "object",
        "description": "An event about a subscription: the very body its webhooks carry.",
        "required": [
          "id",
          "object",
          "type",
          "created_at",
          "network",
          "api_version",
          "data",
          "detail"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The event's id, the one its webhook bodies carry."
          },
          "object": {
            "type": "string",
            "enum": ["event"],
            "description": "Always `event`."
          },
          "type": {
            "type": "string",
            "enum": [
              "subscription.created",
              "subscription.renewal_upcoming",
              "subscription.renewed",
              "subscription.payment_failed",
              "subscription.stopped",
              "subscription.cancelled",
              "subscription.resumed",
              "subscription.ended",
              "subscription.expired",
              "subscription.access_granted",
              "subscription.access_revoked",
              "subscription.paused",
              "subscription.unpaused"
            ],
            "description": "The event's name, as its webhook is named."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the event happened: the moment `data` describes, but for an access event or a cancellation reaching its end, written when Mesub wrote the event, whose `detail.changed_at` is that moment."
          },
          "network": {
            "type": "string",
            "enum": ["devnet", "mainnet-beta"],
            "description": "The Solana network the subscription is on."
          },
          "api_version": {
            "type": "string",
            "enum": ["2026-10-02"],
            "description": "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."
          },
          "sequence": {
            "type": "integer",
            "minimum": 1,
            "description": "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](/reference/subscriptions/retrieve) or [Check access](/reference/access/check), rather than applying its `data`."
          },
          "data": {
            "$ref": "#/components/schemas/Subscription",
            "description": "The subscription as it stood right after the change that raised this event, as [Retrieve a subscription](/reference/subscriptions/retrieve) 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`."
          },
          "detail": {
            "type": "object",
            "additionalProperties": true,
            "description": "The event's own fields, as its webhook describes them under `detail`."
          }
        }
      },
      "EventList": {
        "type": "object",
        "required": ["data", "has_more", "available_since"],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            },
            "description": "Newest first. Maybe empty."
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether another page follows."
          },
          "available_since": {
            "type": "string",
            "format": "date-time",
            "description": "When Mesub began keeping events in this form. Events before it are not served, whatever the window: their webhooks carried the older shape."
          }
        }
      }
    },
    "parameters": {
      "MesubVersion": {
        "name": "Mesub-Version",
        "in": "header",
        "required": false,
        "description": "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`.",
        "schema": {
          "type": "string",
          "enum": ["2026-10-02"]
        }
      }
    },
    "headers": {
      "MesubVersion": {
        "description": "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.",
        "required": true,
        "schema": {
          "type": "string",
          "enum": ["2026-10-02"]
        }
      },
      "MesubRequestId": {
        "description": "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.",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^req_[0-9A-Za-z]{24}$"
        }
      },
      "RateLimitLimit": {
        "description": "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.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "RateLimitRemaining": {
        "description": "What is left of them in the current minute.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimitReset": {
        "description": "Seconds until the count starts again.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before sending the same request again.",
        "required": true,
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RetryAfterWithSomeCodes": {
        "description": "Seconds to wait before sending the same request again. Sent with the codes that ask to wait: their `when` says so.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      }
    }
  }
}
