{
  "openapi": "3.1.0",
  "info": {
    "title": "TgPay Crypto Merchant API",
    "version": "2026-11-03",
    "summary": "Merchant payments API of the TgPay Crypto wallet (@tgpaycryptobot).",
    "description": "The Merchant API of TgPay Crypto (@tgpaycryptobot): create\ninvoices, refund them, send payouts and checks, manage recurring\nsubscriptions, and receive signed webhooks.\n\nHuman-readable documentation: https://docs.tgpaycrypto.com/crypto-pay-api-reference/\n\n## Conventions\n\n- **Authentication** — send your token in `TgPayCrypto-API-Token`.\n  `TgCryptoPay-API-Token` and `Crypto-Pay-API-Token` are accepted aliases,\n  in that order. The first non-empty header wins. Full-access primary\n  tokens have all scopes; restricted tokens need the operation's scope.\n- **Envelope** — application success is HTTP 200 with `{ok: true, result}`;\n  application errors are `{ok: false, error: {code, name}}`, with `code`\n  equal to the HTTP status. Branch on `error.name`. Transport/framework\n  rejections, such as malformed typed query parameters, may lack this envelope.\n- **Amounts** — requests use decimal strings in whole-coin or fiat units,\n  never JSON numbers. Crypto input is rounded to the asset's minor unit,\n  with midpoint rounding away from zero; the result must be positive.\n  Fiat input must represent a positive exact number of cents, at most\n  10^13 whole fiat units. Responses use fixed-point strings; balance and\n  fee amounts can be zero. `*_minor` values are integer strings.\n  `getCurrencies` supplies asset decimals. Send exact asset precision.\n- **POST parameters** — JSON object, form-urlencoded body, or URL query\n  parameters; body values override query values, including null. Repeated\n  form/query keys use the last value. Multipart is rejected. Money-moving\n  methods have no GET aliases. Nested `items` and `recipients` must be\n  JSON arrays in a JSON body; JSON-encoded strings do not work for them.\n  Required fields must be present after merging query and body.\n  Optional null values generally behave as omitted; per-field rules apply.\n  Integers accept integer strings; booleans also accept 1/0 and the strings\n  true/false, 1/0, yes/no, on/off (case-insensitive, surrounding whitespace ignored).\n  Text limits are measured in UTF-16 code units by the server.\n- **Idempotency** — `spend_id` is required for transfers and batch items,\n  optional for checks and refunds. Empty/null optional keys disable replay.\n  Keys are scoped per app and operation; transfer and transferBatch share\n  a namespace. After validation and access gates, the same parsed request\n  replays its stored success. Different input returns `409 idempotency_conflict`;\n  an unfinished claim returns `409 idempotency_in_progress`. Amount strings\n  and recipient spelling are not normalized for the request hash: keep them\n  identical on retries. Replays are snapshots, not refreshed objects; use\n  list methods to obtain current status. createInvoice and plan creation\n  have no spend_id idempotency.\n- **Pagination** — invoices, checks and transfers default to offset 0,\n  count 100; the server clamps offset to at least 0 and count to 1..1000.\n  Subscriptions clamp count to 1..500. These lists return `{items: [...]}`,\n  newest first. getSubscriptionPlans returns all plans without pagination.\n- **Timestamps** — ISO-8601 strings; nullable timestamps are explicitly null.\n- **Rate limits** — per app across its tokens: createInvoice/createCheck\n  60/min, refundInvoice/transfer 30/min, transferBatch/updateApp 10/min,\n  createSubscriptionPlan 20/min. Additional request limits may apply.\n  On `429 rate_limited`, increase the delay before retrying.\n- **Maintenance** — POST methods return `503 maintenance`; GET methods\n  are not blocked by maintenance.\n\n## Shared application errors\n\n| HTTP | error.name | When |\n|---|---|---|\n| 401 | unauthorized | Missing/invalid/revoked/expired token, inactive app or blocked owner |\n| 403 | scope_required | Token lacks the operation's scope |\n| 400 | invalid_request | Invalid body, field type, length or numeric bounds |\n| 429 | rate_limited | Request limit reached; increase the delay before retrying |\n| 503 | maintenance | Maintenance blocks POST methods |\n| 500 | internal_error | Unexpected server error |\n\ntransfer, transferBatch, createCheck and refundInvoice can also return\n`503 merchant_payouts_paused` or `403 verification_required` when outgoing\napp funds are restricted. transfer, transferBatch and createCheck enforce\nthe owner's payout mode (`403 payouts_disabled` / `recipient_not_allowed`)\nand rolling daily payout ceiling (`403 daily_cap_exceeded`). Refunds do\nnot use those payout-mode/ceiling gates. getMe reports the mode and ceiling;\nthe owner changes them in the app, not through this API.\n"
  },
  "externalDocs": {
    "description": "Merchant API reference (developer docs)",
    "url": "https://docs.tgpaycrypto.com/crypto-pay-api-reference/"
  },
  "servers": [
    {
      "url": "https://app.tgpaycrypto.com/pay/api",
      "description": "Production"
    },
    {
      "url": "https://testnet.tgpaycrypto.com/pay/api",
      "description": "Testnet — coins with no value, its own bot @tgpaycrypto_testnet_bot and its own apps and tokens"
    }
  ],
  "security": [
    {
      "apiToken": []
    },
    {
      "apiTokenLegacy": []
    },
    {
      "apiTokenCompat": []
    }
  ],
  "tags": [
    {
      "name": "app",
      "description": "App identity, balance, currencies, rates, stats."
    },
    {
      "name": "invoices",
      "description": "Payment requests and refunds."
    },
    {
      "name": "checks",
      "description": "Claimable crypto checks funded from the app balance, including named recipient lists."
    },
    {
      "name": "transfers",
      "description": "Payouts to Telegram users from the app balance."
    },
    {
      "name": "subscriptions",
      "description": "Extension: recurring billing — plans and subscriptions."
    }
  ],
  "paths": {
    "/getMe": {
      "get": {
        "tags": [
          "app"
        ],
        "operationId": "getMe",
        "summary": "App and token info",
        "description": "Scope: none — any valid token.\n\nReturns app identity, webhook settings, this token's scopes and label, and\nread-only payout controls. scopes and token_name are null for the primary token.\n",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "$ref": "#/components/schemas/AppInfo"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/getBalance": {
      "get": {
        "tags": [
          "app"
        ],
        "operationId": "getBalance",
        "summary": "App balances per asset",
        "description": "Scope: `read`.\n\nOne row per enabled asset, including zero balances. available is spendable\napp balance. onhold includes unclaimed check funds and pending username\ntransfers. deleteCheck refunds a check's unclaimed remainder; pending\ntransfers return to the app when processed for expiry. amount_minor is\navailable in minor units, not available plus onhold.\n",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BalanceRow"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/getCurrencies": {
      "get": {
        "tags": [
          "app"
        ],
        "operationId": "getCurrencies",
        "summary": "Supported currencies",
        "description": "Scope: none — any valid token.\n\nThe authoritative list of crypto assets (`is_blockchain: true`) and\ninvoice-pricing fiats (`is_fiat: true`), with per-currency\n`decimals`. Treat this as the source of truth for what's live and\nfor amount math.\n",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Currency"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/getExchangeRates": {
      "get": {
        "tags": [
          "app"
        ],
        "operationId": "getExchangeRates",
        "summary": "Crypto→fiat exchange rates",
        "description": "Scope: none — any valid token.\n\nReturns indicative crypto-to-fiat quotes. is_valid=true does not guarantee\nthat a quote is current. If quotes are unavailable, the result may be an\nempty array. Use the invoice's recorded amounts and rates to reconcile\nan actual payment.\n",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ExchangeRate"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/getStats": {
      "get": {
        "tags": [
          "app"
        ],
        "operationId": "getStats",
        "summary": "App volume/conversion stats",
        "description": "Scope: `read`.\n\nDefaults to the last 24 hours. Naive datetimes are read as UTC; a\n`Z` suffix is accepted.\n\nSpecific errors: `400 invalid_date` (unparsable `start_at`/`end_at`).\n",
        "parameters": [
          {
            "name": "start_at",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Window start (default now − 24 h)."
          },
          {
            "name": "end_at",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Window end (default now)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "$ref": "#/components/schemas/AppStats"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/createInvoice": {
      "post": {
        "tags": [
          "invoices"
        ],
        "operationId": "createInvoice",
        "summary": "Create an invoice",
        "description": "Scope: `invoices`. Rate limit: 60/min per app.\n\nCrypto mode (default) fixes `asset`; fiat mode fixes `fiat` and the\npayer picks one of `accepted_assets` at pay time. Invoices are paid\nfrom the payer's in-app wallet balance; a payer may also fund the\ninvoice from an external wallet (their deposit lands in their own\nwallet). Automatic payment requires sufficient available balance, an\nunexpired payment request and invoice, and no outgoing-payment restriction.\nConfirm the invoice is `paid` before fulfilling the order. The platform fee comes out of the\nmerchant's leg and is reported in `fee_amount`.\n\nFiat invoices convert at the fresh pay-time rate rounded up in the\nmerchant's favor — unless a live `rate_lock_seconds` stamp applies.\nIf no fresh rate is available, payment fails payer-side; the\nmerchant is never settled at a stale rate.\n\nSpecific errors:\n\n| HTTP | `error.name` | when |\n|---|---|---|\n| 400 | `invalid_currency` | missing asset in crypto mode; fiat mode without fiat or with non-empty asset; open_amount on a fiat invoice |\n| 400 | `invalid_amount` | missing/unparsable/non-positive amount; `amount`+`open_amount` together; fiat amount not an exact number of cents or > 10^13 |\n| 400 | `unknown_asset` | unknown/disabled `asset`, `swap_to`, or `accepted_assets` entry |\n| 400 | `unsupported_fiat` | `fiat` not supported |\n| 400 | `paid_btn_url_required` | `paid_btn_name` set without a valid http(s) `paid_btn_url` |\n| 400 | `rate_lock_fiat_only` | positive `rate_lock_seconds` on a crypto-priced invoice |\n| 409 | `ratelock_disabled` | rate-lock currently unavailable platform-side |\n| 409 | `rate_unavailable` | no fresh rate for an accepted asset — retry |\n\nCrypto mode ignores fiat and accepted_assets. A positive rate lock is\nfiat-only; zero disables it. A lapsed lock reverts to fresh pay-time rates.\nCreation has no spend_id replay protection. Optional payer-facing text\nmay be withheld on the payment screen while under review.\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateInvoiceRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/CreateInvoiceRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "$ref": "#/components/schemas/Invoice"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "currency_type",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/currency_type"
            }
          },
          {
            "name": "asset",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/asset"
            }
          },
          {
            "name": "fiat",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/fiat"
            }
          },
          {
            "name": "accepted_assets",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "type": "string",
              "description": "Comma-separated values."
            }
          },
          {
            "name": "amount",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/amount"
            }
          },
          {
            "name": "open_amount",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/open_amount"
            }
          },
          {
            "name": "description",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/description"
            }
          },
          {
            "name": "hidden_message",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/hidden_message"
            }
          },
          {
            "name": "payload",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/payload"
            }
          },
          {
            "name": "allow_comments",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/allow_comments"
            }
          },
          {
            "name": "allow_anonymous",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/allow_anonymous"
            }
          },
          {
            "name": "paid_btn_name",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/paid_btn_name"
            }
          },
          {
            "name": "paid_btn_url",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/paid_btn_url"
            }
          },
          {
            "name": "swap_to",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/swap_to"
            }
          },
          {
            "name": "expires_in",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/expires_in"
            }
          },
          {
            "name": "rate_lock_seconds",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateInvoiceRequest/properties/rate_lock_seconds"
            }
          }
        ]
      }
    },
    "/getInvoices": {
      "get": {
        "tags": [
          "invoices"
        ],
        "operationId": "getInvoices",
        "summary": "List invoices",
        "description": "Scope: `read`. Newest first.\n\nstatus=active excludes invoices past their deadline; status=expired\nincludes both stored expired rows and active rows past the deadline.\nThe asset filter matches the stored crypto asset, including the settlement\nasset of a paid fiat invoice. Deleted invoices are absent. Non-numeric\nentries in invoice_ids are ignored; no valid ids means an empty list.\n",
        "parameters": [
          {
            "name": "asset",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by crypto asset code."
          },
          {
            "name": "fiat",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by fiat code."
          },
          {
            "name": "invoice_ids",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated invoice ids."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "paid",
                "expired"
              ]
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "type": "object",
                      "required": [
                        "items"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Invoice"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/deleteInvoice": {
      "post": {
        "tags": [
          "invoices"
        ],
        "operationId": "deleteInvoice",
        "summary": "Delete an unpaid invoice",
        "description": "Scope: `invoices`.\n\nCancels an **unpaid** invoice (active or expired). Result: `true`.\n\nSpecific errors: `404 invoice_not_found` (unknown id or another\napp's invoice); `409 invoice_already_paid` (paid invoices can't be\ndeleted — money moved).\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "invoice_id"
                ],
                "properties": {
                  "invoice_id": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "maximum": 9223372036854775807,
                        "minimum": 1
                      },
                      {
                        "type": "string",
                        "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                      }
                    ],
                    "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
                  }
                }
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "invoice_id"
                ],
                "properties": {
                  "invoice_id": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "maximum": 9223372036854775807,
                        "minimum": 1
                      },
                      {
                        "type": "string",
                        "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                      }
                    ],
                    "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "invoice_id",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "oneOf": [
                {
                  "type": "integer",
                  "maximum": 9223372036854775807,
                  "minimum": 1
                },
                {
                  "type": "string",
                  "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                }
              ],
              "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
            }
          }
        ]
      }
    },
    "/refundInvoice": {
      "post": {
        "tags": [
          "invoices"
        ],
        "operationId": "refundInvoice",
        "summary": "Refund a paid invoice",
        "description": "Scope: `refunds`. Rate limit: 30/min\nper app.\n\nReturns a paid invoice's face amount — or part of it — from the app\nbalance to the payer, anonymous payers included, without revealing\nthem. The platform fee is not returned. Invoice `status` stays\n`paid`; progress is tracked in the cumulative `refunded_amount` /\n`refunded_minor`, and `refunded_at` is stamped once fully refunded.\nTriggers the opt-in `refund_completed` webhook.\n\nSpecific errors:\n\n| HTTP | `error.name` | when |\n|---|---|---|\n| 404 | `invoice_not_found` | unknown id / another app's invoice |\n| 400 | `invalid_amount` | unparsable or non-positive `amount` |\n| 409 | `invoice_not_paid` | invoice isn't `paid` |\n| 409 | `already_refunded` | nothing left to refund |\n| 409 | `amount_too_big` | `amount` exceeds the unrefunded remainder |\n| 409 | `insufficient_funds` | app balance can't cover the refund |\n| 409 | `idempotency_conflict` / `idempotency_in_progress` | see Conventions → Idempotency |\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RefundInvoiceRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/RefundInvoiceRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK — the updated invoice with cumulative `refunded_*` fields.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "$ref": "#/components/schemas/Invoice"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "invoice_id",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/RefundInvoiceRequest/properties/invoice_id"
            }
          },
          {
            "name": "amount",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/RefundInvoiceRequest/properties/amount"
            }
          },
          {
            "name": "spend_id",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/RefundInvoiceRequest/properties/spend_id"
            }
          }
        ]
      }
    },
    "/createCheck": {
      "post": {
        "tags": [
          "checks"
        ],
        "operationId": "createCheck",
        "summary": "Create a check",
        "description": "Scope: `checks`. Rate limit: 60/min per app. Shared payout restrictions apply.\n\nWithout recipients, creates a single-use check for amount. With recipients,\ncreates one claim per named recipient and funds the sum of their amounts;\ntop-level amount may be omitted, or is validated then ignored. Named lists\ncannot be combined with a pin. Amounts are locked immediately and included\nin getBalance.onhold. The unclaimed remainder returns on deletion or expiry.\nThe app owner cannot claim their own check. Password and expiry are optional.\n\nKnown recipient usernames bind to their user ids at creation; unknown\nusernames remain claimable by the matching user when they join. Numeric\nrecipient ids need not already exist. A pin_to_username must resolve to an\nexisting available user; pin_to_user_id takes precedence over it.\n\ncheck_activated fires on a single-use claim, or once per named seat with\nactivated_slot. check_expired currently fires only for expired named lists.\nOptional non-empty spend_id enables stored-response replay.\n\nSpecific errors: `400 unknown_asset`, `400 invalid_amount`,\n`400 invalid_request` (including non-positive pin_to_user_id),\n`400 invalid_recipient` (malformed named recipient or pin username),\n`400 invalid_pinned_user` (named list combined with pin),\n`400 invalid_slots` (more than 100 recipients), `400 self_claim`,\n`400 duplicate_recipient`, `404 user_not_found` (unresolved/unavailable pin),\n`400 amount_too_small` / `amount_too_big` (total outside payout USD bounds),\n`409 insufficient_funds`, `409 idempotency_conflict` / `idempotency_in_progress`.\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCheckRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "asset",
                  "amount"
                ],
                "properties": {
                  "asset": {
                    "type": "string",
                    "maxLength": 16
                  },
                  "amount": {
                    "$ref": "#/components/schemas/AmountInput"
                  },
                  "pin_to_user_id": {
                    "anyOf": [
                      {
                        "oneOf": [
                          {
                            "type": "integer",
                            "maximum": 9223372036854775807,
                            "minimum": 1
                          },
                          {
                            "type": "string",
                            "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                          }
                        ],
                        "description": "Restrict claiming to this Telegram user id."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "pin_to_username": {
                    "anyOf": [
                      {
                        "type": "string",
                        "maxLength": 64,
                        "description": "Restrict by @username (leading @ optional, case-insensitive); ignored when pin_to_user_id is also given. Must belong to an existing user of the platform."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "spend_id": {
                    "anyOf": [
                      {
                        "type": "string",
                        "maxLength": 64,
                        "description": "Optional replay key; empty/null disables idempotency."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "password": {
                    "anyOf": [
                      {
                        "type": "string",
                        "maxLength": 64,
                        "description": "Optional claim password; empty/null means no password. Never returned."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "expires_in": {
                    "anyOf": [
                      {
                        "oneOf": [
                          {
                            "type": "integer",
                            "maximum": 9223372036854775807,
                            "minimum": 0
                          },
                          {
                            "type": "string",
                            "pattern": "^\\s*(?:\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)|[+-]?0+)\\s*$"
                          }
                        ],
                        "description": "Seconds until expiry; zero, null or omitted means no explicit deadline. Choose a duration that produces a representable expiry timestamp."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "$ref": "#/components/schemas/Check"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "asset",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateCheckRequest/properties/asset"
            }
          },
          {
            "name": "amount",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateCheckRequest/properties/amount"
            }
          },
          {
            "name": "pin_to_user_id",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateCheckRequest/properties/pin_to_user_id"
            }
          },
          {
            "name": "pin_to_username",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateCheckRequest/properties/pin_to_username"
            }
          },
          {
            "name": "spend_id",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateCheckRequest/properties/spend_id"
            }
          },
          {
            "name": "password",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateCheckRequest/properties/password"
            }
          },
          {
            "name": "expires_in",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreateCheckRequest/properties/expires_in"
            }
          }
        ]
      }
    },
    "/deleteCheck": {
      "post": {
        "tags": [
          "checks"
        ],
        "operationId": "deleteCheck",
        "summary": "Cancel an active check",
        "description": "Scope: `checks`.\n\nCancels a stored-active check and refunds only its unclaimed remainder.\nResult: true. Specific errors: `404 check_not_found` (unknown or another\napp's check), `409 check_not_active` (already drained, deleted or expired).\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "check_id"
                ],
                "properties": {
                  "check_id": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "maximum": 9223372036854775807,
                        "minimum": 1
                      },
                      {
                        "type": "string",
                        "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                      }
                    ],
                    "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
                  }
                }
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "check_id"
                ],
                "properties": {
                  "check_id": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "maximum": 9223372036854775807,
                        "minimum": 1
                      },
                      {
                        "type": "string",
                        "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                      }
                    ],
                    "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "check_id",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "oneOf": [
                {
                  "type": "integer",
                  "maximum": 9223372036854775807,
                  "minimum": 1
                },
                {
                  "type": "string",
                  "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                }
              ],
              "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
            }
          }
        ]
      }
    },
    "/getChecks": {
      "get": {
        "tags": [
          "checks"
        ],
        "operationId": "getChecks",
        "summary": "List checks",
        "description": "Scope: `read`. Newest first. Deleted checks are excluded. status=active\nselects checks that can still be claimed, status=activated fully claimed\nchecks, status=expired (extension) checks whose lifetime ran out. Use\nrecipient claimed_at for named-list reconciliation.\nUse only the documented status values and comma-separated numeric check_ids.\nNamed lists include all recipients; activated_at is the final claim.\n",
        "parameters": [
          {
            "name": "asset",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by asset code."
          },
          {
            "name": "check_ids",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated check ids."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "activated",
                "expired"
              ]
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "type": "object",
                      "required": [
                        "items"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Check"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/transfer": {
      "post": {
        "tags": [
          "transfers"
        ],
        "operationId": "transfer",
        "summary": "Pay out to a Telegram user",
        "description": "Scope: `payouts`. Rate limit: 30/min per app. Shared payout restrictions apply.\n\nProvide exactly one non-null recipient: user_id or username. A numeric id\nmust belong to an existing available user. A known username settles at once;\nan unknown valid username creates a pending payout held in escrow. Forward\ninvite_url to the recipient. On claim it becomes completed; expiry processing\nreturns unclaimed funds to the app and sets returned. Read expires_at from\nthe response and track final status via getTransfers or opt-in terminal\nwebhooks. The public API sends to Telegram recipients, not on-chain addresses.\n\nspend_id is required. Retrying replays the original response, even if a\npending payout has since completed. Platform amount limits are expressed\nin USD; handle amount_too_small and amount_too_big. disable_send_notification suppresses only the immediate payout\nnotification; a later escrow claim still notifies the recipient.\n\nSpecific errors: `400 unknown_asset`, `400 invalid_request` (missing/both\nrecipients, non-positive user_id, invalid field type or length),\n`400 invalid_recipient` (malformed username), `404 user_not_found` (unknown\nnumeric id or unavailable/blocked account), `400 invalid_amount`,\n`400 amount_too_small` / `amount_too_big`, `409 insufficient_funds`,\n`409 idempotency_conflict` / `idempotency_in_progress`.\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransferRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TransferRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "$ref": "#/components/schemas/Transfer"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "user_id",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/TransferRequest/properties/user_id"
            }
          },
          {
            "name": "asset",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/TransferRequest/properties/asset"
            }
          },
          {
            "name": "amount",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/TransferRequest/properties/amount"
            }
          },
          {
            "name": "spend_id",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/TransferRequest/properties/spend_id"
            }
          },
          {
            "name": "comment",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/TransferRequest/properties/comment"
            }
          },
          {
            "name": "disable_send_notification",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/TransferRequest/properties/disable_send_notification"
            }
          },
          {
            "name": "username",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/TransferRequest/properties/username"
            }
          }
        ]
      }
    },
    "/transferBatch": {
      "post": {
        "tags": [
          "transfers"
        ],
        "operationId": "transferBatch",
        "summary": "Batch payouts",
        "description": "Scope: `payouts`. Rate limit: 10/min per app. Shared payout restrictions apply.\n\nSubmit 1..100 JSON items. The whole batch is parsed first: malformed rows\nreturn `400 invalid_request`, repeated spend_id returns `400 duplicate_spend_id`.\nThe payout mode and daily ceiling can reject the whole batch with 403 before\nany row executes. After those checks, items execute independently in order;\nan application error on one row does not undo earlier rows. Each item uses\nthe same spend_id namespace as transfer. HTTP 200 / top-level ok=true may\ninclude failed items: inspect every item's ok and error. Pending username\npayouts are successful items even though they have not yet been claimed.\nPer-item application errors match transfer. An unexpected server failure\ncan still abort the call after earlier rows committed; retry with the same\nspend_ids and unchanged item parameters.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "items"
                ],
                "properties": {
                  "items": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "items": {
                      "$ref": "#/components/schemas/TransferRequest"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK — one entry per input item, in order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "type": "object",
                      "required": [
                        "items"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/TransferBatchItemResult"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/getTransfers": {
      "get": {
        "tags": [
          "transfers"
        ],
        "operationId": "getTransfers",
        "summary": "List transfers",
        "description": "Scope: `read`. Newest first. `spend_id` is an exact-match filter —\nlook up a payout by your own idempotency key.\n\nNon-numeric transfer_ids entries are ignored; no valid ids returns no rows.\nOnly Telegram transfers are listed; on-chain app payouts have a separate webhook payload.\n",
        "parameters": [
          {
            "name": "asset",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by asset code."
          },
          {
            "name": "transfer_ids",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated transfer ids."
          },
          {
            "name": "spend_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "completed",
                "returned"
              ]
            },
            "description": "Case-insensitive transfer state filter."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "type": "object",
                      "required": [
                        "items"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Transfer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/createSubscriptionPlan": {
      "post": {
        "tags": [
          "subscriptions"
        ],
        "operationId": "createSubscriptionPlan",
        "summary": "Create a subscription plan",
        "description": "Scope: `subscriptions`. Rate limit: 20/min per app.\n\nThe plan is an **immutable snapshot** — the mandate a user approves\nis exactly this amount per this period; to change the price, create\na new plan and archive the old one. Users approve via the plan's\n`mini_app_subscribe_url`; the first period is charged at approval.\nRenewals bill automatically; a failed renewal puts the subscription\ninto `grace` with retries inside the platform grace window, then\n`expired`.\n\nSpecific errors: `503 subs_disabled` (feature currently unavailable\nplatform-side); `400 unknown_asset`; `400 invalid_amount`;\n`409 invalid_period` (below the platform minimum),\n`409 too_many_plans` (active plan limit reached; archive one first).\nValues outside 1..365 return `400 invalid_request` before service validation.\nCreation has no spend_id replay protection.\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePlanRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/CreatePlanRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "$ref": "#/components/schemas/Plan"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreatePlanRequest/properties/name"
            }
          },
          {
            "name": "asset",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreatePlanRequest/properties/asset"
            }
          },
          {
            "name": "amount",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreatePlanRequest/properties/amount"
            }
          },
          {
            "name": "period_days",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/CreatePlanRequest/properties/period_days"
            }
          }
        ]
      }
    },
    "/getSubscriptionPlans": {
      "get": {
        "tags": [
          "subscriptions"
        ],
        "operationId": "getSubscriptionPlans",
        "summary": "List subscription plans",
        "description": "Scope: `read`. Newest first. Each plan carries\n`active_subscribers` — the count of live (`active` + `grace`)\nsubscriptions.\n\nReturns all plans, including archived plans; there are no pagination inputs.\n",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "type": "object",
                      "required": [
                        "items"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PlanWithStats"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/archiveSubscriptionPlan": {
      "post": {
        "tags": [
          "subscriptions"
        ],
        "operationId": "archiveSubscriptionPlan",
        "summary": "Archive a plan",
        "description": "Scope: `subscriptions`.\n\nStops **new** approvals; live subscriptions keep renewing on their\nown snapshot (end them per-row via `cancelSubscription`). Archiving\nis irreversible via the API.\n\nSpecific errors: `404 plan_not_found`.\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "plan_id"
                ],
                "properties": {
                  "plan_id": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "maximum": 9223372036854775807,
                        "minimum": 1
                      },
                      {
                        "type": "string",
                        "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                      }
                    ],
                    "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
                  }
                }
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "plan_id"
                ],
                "properties": {
                  "plan_id": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "maximum": 9223372036854775807,
                        "minimum": 1
                      },
                      {
                        "type": "string",
                        "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                      }
                    ],
                    "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK — the updated plan (`archived: true`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "$ref": "#/components/schemas/Plan"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "plan_id",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "oneOf": [
                {
                  "type": "integer",
                  "maximum": 9223372036854775807,
                  "minimum": 1
                },
                {
                  "type": "string",
                  "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                }
              ],
              "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
            }
          }
        ]
      }
    },
    "/getSubscriptions": {
      "get": {
        "tags": [
          "subscriptions"
        ],
        "operationId": "getSubscriptions",
        "summary": "List subscriptions",
        "description": "Scope: `read`. Newest first. Merchants read `current_period_end` as\nthe access deadline.\n",
        "parameters": [
          {
            "name": "plan_id",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Filter by plan."
          },
          {
            "name": "user_id",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Filter by subscriber Telegram id."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "grace",
                "cancelled",
                "expired"
              ]
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "type": "object",
                      "required": [
                        "items"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Subscription"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/cancelSubscription": {
      "post": {
        "tags": [
          "subscriptions"
        ],
        "operationId": "cancelSubscription",
        "summary": "Cancel a subscription",
        "description": "Scope: `subscriptions`.\n\nStops renewals (`cancelled_by: \"merchant\"`). The already-paid\nperiod stays usable until `current_period_end`; the status flips to\n`cancelled` immediately. The subscriber is notified; the opt-in\n`subscription_cancelled` webhook fires. A cancelled subscriber may\nre-approve later via the plan link.\n\nSpecific errors: `404 sub_not_found` (unknown id / another app's\nsubscription); `409 sub_not_active` (already cancelled or expired).\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subscription_id"
                ],
                "properties": {
                  "subscription_id": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "maximum": 9223372036854775807,
                        "minimum": 1
                      },
                      {
                        "type": "string",
                        "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                      }
                    ],
                    "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
                  }
                }
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "subscription_id"
                ],
                "properties": {
                  "subscription_id": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "maximum": 9223372036854775807,
                        "minimum": 1
                      },
                      {
                        "type": "string",
                        "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                      }
                    ],
                    "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK — the updated subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "$ref": "#/components/schemas/Subscription"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "subscription_id",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "oneOf": [
                {
                  "type": "integer",
                  "maximum": 9223372036854775807,
                  "minimum": 1
                },
                {
                  "type": "string",
                  "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                }
              ],
              "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
            }
          }
        ]
      }
    },
    "/updateApp": {
      "post": {
        "tags": [
          "app"
        ],
        "operationId": "updateApp",
        "summary": "Update app name and webhook configuration",
        "description": "Scope: `webhooks`. Rate limit: 10/min per app. Returns AppInfo.\n\nSpecific errors: `400 invalid_name` (name resembles a link or handle),\n`400 app_name_reserved` (protected brand), `400 invalid_webhook_events`\n(unknown event name, including invoice_paid). Invalid name length, URL\nor malformed event input returns `400 invalid_request`.\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAppRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAppRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "result"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "result": {
                      "$ref": "#/components/schemas/AppInfo"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/UpdateAppRequest/properties/name"
            }
          },
          {
            "name": "webhook_url",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "$ref": "#/components/schemas/UpdateAppRequest/properties/webhook_url"
            }
          },
          {
            "name": "webhook_events",
            "in": "query",
            "required": false,
            "description": "Alternative to this field in the body; body wins. Required-field rules apply after merging.",
            "schema": {
              "type": "string",
              "description": "Comma-separated values. A JSON-encoded array string is also accepted."
            }
          }
        ]
      }
    }
  },
  "webhooks": {
    "invoice_paid": {
      "post": {
        "summary": "An invoice was paid (delivered when a webhook URL is configured)",
        "description": "invoice_paid is delivered when the app has a webhook URL. Other event\ntypes require opt-in via updateApp.webhook_events or the app's settings.\n\nVerify TgPayCrypto-API-Signature (identical aliases: TgCryptoPay-API-Signature\nand Crypto-Pay-API-Signature): lowercase hex HMAC-SHA256 of the raw request\nbytes, using the raw SHA-256 digest of the app's current primary API token\nas the HMAC key. Restricted tokens do not sign webhooks. Token rotation\nchanges the signing key, including for retries of queued deliveries.\n\nA 2xx response received before the attempt times out acknowledges delivery.\nRespond promptly and process long tasks separately. Failed deliveries are\nretried with increasing delays, up to the platform's attempt limit. Delivery\nis not guaranteed; use getInvoices to reconcile payments after an outage.\nThe webhook URL is not automatically disabled on failure. Retries keep update_id and the stored payload, while\nrequest_date and the signature are regenerated per attempt. Deliveries use\nthe current configured URL and do not follow redirects. Dedupe by update_id.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "invoice_paid"
                      },
                      "payload": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ]
      }
    },
    "invoice_expired": {
      "post": {
        "summary": "An invoice passed its deadline unpaid (opt-in)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "invoice_expired"
                      },
                      "payload": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ]
      }
    },
    "check_activated": {
      "post": {
        "summary": "A check was claimed (opt-in)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "check_activated"
                      },
                      "payload": {
                        "$ref": "#/components/schemas/Check"
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "description": "Single-use check: one claim event. Named list: one event per claimed seat, with activated_slot; recipients is null in this payload. status remains active until the last seat is claimed.\n",
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ]
      }
    },
    "refund_completed": {
      "post": {
        "summary": "A refund was executed (opt-in)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "refund_completed"
                      },
                      "payload": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ]
      }
    },
    "subscription_activated": {
      "post": {
        "summary": "A user approved a plan (opt-in)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "subscription_activated"
                      },
                      "payload": {
                        "$ref": "#/components/schemas/Subscription"
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ]
      }
    },
    "subscription_charged": {
      "post": {
        "summary": "A subscription period was billed (opt-in)",
        "description": "`charge.kind` says which period: `initial` (approval charge),\n`renewal`, or `resubscribe` (a cancelled subscriber re-approved).\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "subscription_charged"
                      },
                      "payload": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/Subscription"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "charge": {
                                "$ref": "#/components/schemas/SubscriptionCharge"
                              }
                            },
                            "required": [
                              "charge"
                            ]
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ]
      }
    },
    "subscription_cancelled": {
      "post": {
        "summary": "A subscription was cancelled by either side (opt-in)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "subscription_cancelled"
                      },
                      "payload": {
                        "$ref": "#/components/schemas/Subscription"
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ]
      }
    },
    "subscription_expired": {
      "post": {
        "summary": "The grace window ran out unpaid (opt-in)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "subscription_expired"
                      },
                      "payload": {
                        "$ref": "#/components/schemas/Subscription"
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ]
      }
    },
    "transfer_completed": {
      "post": {
        "summary": "A payout completed (opt-in)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "transfer_completed"
                      },
                      "payload": {
                        "oneOf": [
                          {
                            "allOf": [
                              {
                                "$ref": "#/components/schemas/Transfer"
                              },
                              {
                                "properties": {
                                  "status": {
                                    "const": "completed"
                                  }
                                },
                                "not": {
                                  "required": [
                                    "kind"
                                  ]
                                }
                              }
                            ]
                          },
                          {
                            "allOf": [
                              {
                                "$ref": "#/components/schemas/WithdrawalPayoutWebhook"
                              },
                              {
                                "properties": {
                                  "status": {
                                    "const": "completed"
                                  }
                                }
                              }
                            ]
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ],
        "description": "Payload is a Transfer for Telegram payouts, or WithdrawalPayoutWebhook (kind=withdrawal) for on-chain payouts created through the app. The latter are absent from getTransfers.\n"
      }
    },
    "transfer_returned": {
      "post": {
        "summary": "A payout returned to the app (opt-in)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "transfer_returned"
                      },
                      "payload": {
                        "oneOf": [
                          {
                            "allOf": [
                              {
                                "$ref": "#/components/schemas/Transfer"
                              },
                              {
                                "properties": {
                                  "status": {
                                    "const": "returned"
                                  }
                                },
                                "not": {
                                  "required": [
                                    "kind"
                                  ]
                                }
                              }
                            ]
                          },
                          {
                            "allOf": [
                              {
                                "$ref": "#/components/schemas/WithdrawalPayoutWebhook"
                              },
                              {
                                "properties": {
                                  "status": {
                                    "const": "failed"
                                  }
                                }
                              }
                            ]
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ],
        "description": "Payload is a Transfer for Telegram payouts, or WithdrawalPayoutWebhook (kind=withdrawal) for on-chain payouts created through the app. The latter are absent from getTransfers.\n"
      }
    },
    "check_expired": {
      "post": {
        "summary": "A named-recipient check expired and its unclaimed funds returned (opt-in)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WebhookUpdate"
                  },
                  {
                    "properties": {
                      "update_type": {
                        "const": "check_expired"
                      },
                      "payload": {
                        "$ref": "#/components/schemas/Check"
                      }
                    }
                  }
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Return any 2xx promptly to acknowledge."
          }
        },
        "description": "Currently emitted only for named-recipient checks. recipients includes every seat and claimed_at. The current serializer reports status=active even though expiry has returned unclaimed funds; use update_type and expires_at. Ordinary check expiry does not emit this event.\n",
        "security": [],
        "parameters": [
          {
            "name": "TgPayCrypto-API-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "HMAC signature of the raw request bytes; see invoice_paid delivery conventions."
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiToken": {
        "type": "apiKey",
        "in": "header",
        "name": "TgPayCrypto-API-Token",
        "description": "The canonical token header. Shown once at app creation / rotation."
      },
      "apiTokenLegacy": {
        "type": "apiKey",
        "in": "header",
        "name": "TgCryptoPay-API-Token",
        "description": "The canonical header's name before 2026-09-07 — still accepted, so integrations built earlier keep working unchanged."
      },
      "apiTokenCompat": {
        "type": "apiKey",
        "in": "header",
        "name": "Crypto-Pay-API-Token",
        "description": "Accepted token header alias. The first non-empty canonical/legacy/compatibility header wins, in that order."
      }
    },
    "responses": {
      "Error": {
        "description": "Error — `{\"ok\": false, \"error\": {\"code\": <HTTP status>, \"name\":\n\"<machine_name>\"}}` with the same HTTP status on the wire. See the\noperation description for its specific error names and the API\ndescription for the common ones.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    },
    "schemas": {
      "Amount": {
        "type": "string",
        "description": "Non-negative fixed-point decimal string in whole-coin (or fiat) units, including zero balances and fees. Never a JSON number.",
        "examples": [
          "10.5"
        ],
        "pattern": "^[0-9]+(?:\\.[0-9]+)?$"
      },
      "AmountMinor": {
        "type": "string",
        "description": "Extension: the integer minor-unit amount as a string (wei-scale values overflow the JS Number type).",
        "examples": [
          "10500000"
        ],
        "pattern": "^[0-9]+$"
      },
      "ErrorEnvelope": {
        "type": "object",
        "required": [
          "ok",
          "error"
        ],
        "properties": {
          "ok": {
            "const": false
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "name"
            ],
            "properties": {
              "code": {
                "type": "integer",
                "description": "Duplicates the HTTP status (400/401/403/404/409/429/503/500)."
              },
              "name": {
                "type": "string",
                "description": "Machine-readable error name to branch on, e.g. \"insufficient_funds\"."
              }
            }
          }
        }
      },
      "AppInfo": {
        "type": "object",
        "required": [
          "app_id",
          "name",
          "payment_processing_bot_username",
          "webhook_url",
          "webhook_events",
          "scopes",
          "token_name",
          "daily_payout_max_usd_cents",
          "daily_payout_spent_usd_cents",
          "payout_mode"
        ],
        "properties": {
          "app_id": {
            "type": "string",
            "description": "The app public id, serialized as a string."
          },
          "name": {
            "type": "string"
          },
          "payment_processing_bot_username": {
            "type": "string",
            "description": "The Telegram bot whose Mini App processes payments."
          },
          "webhook_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "webhook_events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "invoice_expired",
                "check_activated",
                "refund_completed",
                "subscription_activated",
                "subscription_charged",
                "subscription_cancelled",
                "subscription_expired",
                "transfer_completed",
                "transfer_returned",
                "check_expired"
              ]
            },
            "description": "Selected extended webhook update types. invoice_paid is automatic when a URL is configured and is not listed here."
          },
          "scopes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "enum": [
                "read",
                "invoices",
                "refunds",
                "payouts",
                "checks",
                "subscriptions",
                "webhooks"
              ]
            },
            "description": "Extension: this token's scopes, sorted; null = full-access primary token."
          },
          "token_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Extension: the restricted token's label; null for the primary token."
          },
          "daily_payout_max_usd_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Owner-set rolling 24-hour payout ceiling in USD cents; 0 means no ceiling."
          },
          "daily_payout_spent_usd_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Spend currently counted toward the owner's rolling 24-hour payout limit, in USD cents. Includes transfers, checks and app withdrawals; invoice refunds are excluded."
          },
          "payout_mode": {
            "type": "string",
            "enum": [
              "off",
              "allowlist",
              "any"
            ],
            "description": "off disables API payouts; allowlist permits only owner-listed recipients; any permits any valid recipient, including bearer checks."
          }
        }
      },
      "BalanceRow": {
        "type": "object",
        "required": [
          "currency_code",
          "available",
          "onhold",
          "amount_minor"
        ],
        "properties": {
          "currency_code": {
            "type": "string",
            "examples": [
              "USDT"
            ]
          },
          "available": {
            "$ref": "#/components/schemas/Amount"
          },
          "onhold": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Amount"
              }
            ],
            "description": "Funds held in unclaimed app checks and pending username transfers; not spendable."
          },
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          }
        }
      },
      "Currency": {
        "type": "object",
        "required": [
          "code",
          "name",
          "decimals",
          "is_blockchain",
          "is_stablecoin",
          "is_fiat"
        ],
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "examples": [
              "Tether USD"
            ]
          },
          "decimals": {
            "type": "integer",
            "description": "Minor-unit exponent (crypto: per asset, up to 18; fiat: always 2)."
          },
          "is_blockchain": {
            "type": "boolean"
          },
          "is_stablecoin": {
            "type": "boolean"
          },
          "is_fiat": {
            "type": "boolean"
          }
        }
      },
      "ExchangeRate": {
        "type": "object",
        "required": [
          "is_valid",
          "is_crypto",
          "is_fiat",
          "source",
          "target",
          "rate"
        ],
        "properties": {
          "is_valid": {
            "type": "boolean",
            "description": "A true value does not guarantee that the quote is current."
          },
          "is_crypto": {
            "type": "boolean"
          },
          "is_fiat": {
            "type": "boolean"
          },
          "source": {
            "type": "string",
            "description": "Crypto asset code."
          },
          "target": {
            "type": "string",
            "description": "Fiat code."
          },
          "rate": {
            "type": "string",
            "description": "Fiat per 1 whole unit of source, as a fixed-point decimal string."
          }
        }
      },
      "AppStats": {
        "type": "object",
        "properties": {
          "volume": {
            "type": "number",
            "description": "USD value of invoices paid in the window, at current prices (best-effort)."
          },
          "conversion": {
            "type": "number",
            "description": "Paid-in-window / created-in-window percentage, rounded to 2 decimal places; 0 when no invoices were created. May exceed 100 if older invoices are paid in the window."
          },
          "unique_users_count": {
            "type": "integer",
            "description": "Distinct recorded payers of invoices paid in the window, including anonymous payments in this aggregate count."
          },
          "created_invoice_count": {
            "type": "integer"
          },
          "paid_invoice_count": {
            "type": "integer"
          },
          "start_at": {
            "type": "string",
            "format": "date-time"
          },
          "end_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "volume",
          "conversion",
          "unique_users_count",
          "created_invoice_count",
          "paid_invoice_count",
          "start_at",
          "end_at"
        ]
      },
      "CreateInvoiceRequest": {
        "type": "object",
        "properties": {
          "currency_type": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "crypto",
                  "fiat"
                ],
                "default": "crypto"
              },
              {
                "type": "null"
              }
            ]
          },
          "asset": {
            "anyOf": [
              {
                "type": "string",
                "description": "Required in crypto mode; non-empty asset is rejected in fiat mode.",
                "maxLength": 16
              },
              {
                "type": "null"
              }
            ]
          },
          "fiat": {
            "anyOf": [
              {
                "type": "string",
                "description": "Fiat mode: the pricing fiat (is_fiat rows of getCurrencies).",
                "maxLength": 8
              },
              {
                "type": "null"
              }
            ]
          },
          "accepted_assets": {
            "anyOf": [
              {
                "description": "Fiat mode: comma-separated asset codes or a JSON array. Omitted, null or empty list means all enabled assets.",
                "oneOf": [
                  {
                    "type": "string",
                    "examples": [
                      "USDT,GRAM"
                    ]
                  },
                  {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "amount": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AmountInput",
                "description": "Required unless open_amount. Crypto mode: in asset units; fiat mode: in fiat units, max 2 decimal places."
              },
              {
                "type": "null"
              }
            ]
          },
          "open_amount": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/BooleanInput",
                "description": "Extension, crypto mode only: no fixed amount — the payer enters it at pay time. Mutually exclusive with amount.",
                "default": false
              },
              {
                "type": "null"
              }
            ]
          },
          "description": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 1024,
                "description": "Shown to the payer."
              },
              {
                "type": "null"
              }
            ]
          },
          "hidden_message": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2048,
                "description": "Shown to the payer only after payment."
              },
              {
                "type": "null"
              }
            ]
          },
          "payload": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 4096,
                "description": "Opaque merchant data echoed back in the invoice object and webhook."
              },
              {
                "type": "null"
              }
            ]
          },
          "allow_comments": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/BooleanInput",
                "default": true
              },
              {
                "type": "null"
              }
            ]
          },
          "allow_anonymous": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/BooleanInput",
                "default": true
              },
              {
                "type": "null"
              }
            ]
          },
          "paid_btn_name": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "viewItem",
                  "openChannel",
                  "openBot",
                  "callback"
                ],
                "description": "Post-payment CTA button."
              },
              {
                "type": "null"
              }
            ]
          },
          "paid_btn_url": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 512,
                "description": "Required with paid_btn_name; must begin with lowercase http:// or https://. Without paid_btn_name it is ignored."
              },
              {
                "type": "null"
              }
            ]
          },
          "swap_to": {
            "anyOf": [
              {
                "type": "string",
                "description": "Auto-swap the received amount to this asset after payment, best-effort — unavailable pricing, tradability or capacity skips conversion and keeps the paid asset.",
                "maxLength": 16
              },
              {
                "type": "null"
              }
            ]
          },
          "expires_in": {
            "anyOf": [
              {
                "oneOf": [
                  {
                    "type": "integer",
                    "maximum": 2678400,
                    "minimum": 0
                  },
                  {
                    "type": "string",
                    "pattern": "^\\s*(?:\\+?0*(?:[1-9][0-9]{0,5}|1[0-9]{6}|2[0-5][0-9]{5}|26[0-6][0-9]{4}|267[0-7][0-9]{3}|2678[0-3][0-9]{2}|2678400)|[+-]?0+)\\s*$"
                  }
                ],
                "description": "Seconds until expiry, at most 31 days. Omitted, null or zero uses the default lifetime. Read expiration_date in the response for the actual deadline; null means no deadline."
              },
              {
                "type": "null"
              }
            ]
          },
          "rate_lock_seconds": {
            "anyOf": [
              {
                "oneOf": [
                  {
                    "type": "integer",
                    "maximum": 86400,
                    "minimum": 0
                  },
                  {
                    "type": "string",
                    "pattern": "^\\s*(?:\\+?0*(?:[1-9][0-9]{0,3}|[1-7][0-9]{4}|8[0-5][0-9]{3}|86[0-3][0-9]{2}|86400)|[+-]?0+)\\s*$"
                  }
                ],
                "description": "Fiat mode: positive value requests a rate lock, clamped to the platform window (minimum requested window 60 s). Zero/omitted/null disables the lock; expired locks revert to fresh rates."
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "allOf": [
          {
            "if": {
              "required": [
                "currency_type"
              ],
              "properties": {
                "currency_type": {
                  "const": "fiat"
                }
              }
            },
            "then": {
              "allOf": [
                {
                  "required": [
                    "fiat"
                  ],
                  "properties": {
                    "fiat": {
                      "not": {
                        "type": "null"
                      }
                    }
                  }
                },
                {
                  "required": [
                    "amount"
                  ],
                  "properties": {
                    "amount": {
                      "not": {
                        "type": "null"
                      }
                    }
                  }
                }
              ],
              "properties": {
                "asset": {
                  "enum": [
                    null,
                    ""
                  ]
                },
                "open_amount": {
                  "not": {
                    "oneOf": [
                      {
                        "const": true
                      },
                      {
                        "const": 1
                      },
                      {
                        "type": "string",
                        "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$"
                      }
                    ]
                  }
                },
                "fiat": {
                  "type": "string",
                  "minLength": 1
                }
              }
            },
            "else": {
              "allOf": [
                {
                  "required": [
                    "asset"
                  ],
                  "properties": {
                    "asset": {
                      "not": {
                        "type": "null"
                      }
                    }
                  }
                },
                {
                  "if": {
                    "required": [
                      "open_amount"
                    ],
                    "properties": {
                      "open_amount": {
                        "oneOf": [
                          {
                            "const": true
                          },
                          {
                            "const": 1
                          },
                          {
                            "type": "string",
                            "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$"
                          }
                        ]
                      }
                    }
                  },
                  "then": {
                    "properties": {
                      "amount": {
                        "type": "null"
                      }
                    }
                  },
                  "else": {
                    "required": [
                      "amount"
                    ],
                    "properties": {
                      "amount": {
                        "not": {
                          "type": "null"
                        }
                      }
                    }
                  }
                }
              ],
              "properties": {
                "asset": {
                  "type": "string",
                  "minLength": 1
                }
              }
            }
          }
        ]
      },
      "Invoice": {
        "type": "object",
        "description": "The invoice object — API results and invoice webhook payloads.",
        "properties": {
          "invoice_id": {
            "type": "integer"
          },
          "hash": {
            "type": "string",
            "description": "Public id (also inside pay_url)."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paid",
              "expired"
            ]
          },
          "currency_type": {
            "type": "string",
            "enum": [
              "crypto",
              "fiat"
            ]
          },
          "asset": {
            "type": [
              "string",
              "null"
            ],
            "description": "Crypto invoice: the fixed asset. Fiat invoice: always null here (see paid_asset)."
          },
          "fiat": {
            "type": [
              "string",
              "null"
            ]
          },
          "accepted_assets": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Fiat invoices: the concrete list the payer may pay with; null for crypto invoices."
          },
          "amount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Face amount: fiat units for fiat invoices, crypto units otherwise. Null for an unpaid open-amount invoice."
          },
          "amount_minor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Extension: crypto amount in minor units (string); null while a fiat/open-amount invoice is unpaid."
          },
          "paid_asset": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fiat invoices, once paid: the asset the payer chose."
          },
          "paid_amount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fiat invoices, once paid: the crypto amount actually paid."
          },
          "paid_fiat_rate": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fiat invoices, once paid: the fiat-per-asset rate used."
          },
          "paid_usd_rate": {
            "type": [
              "string",
              "null"
            ],
            "description": "USD price of the paid asset stamped at pay time."
          },
          "fee_asset": {
            "type": [
              "string",
              "null"
            ]
          },
          "fee_amount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Platform fee, charged to the merchant's leg — the authoritative number for reconciliation."
          },
          "fee": {
            "type": [
              "string",
              "null"
            ],
            "deprecated": true,
            "description": "Deprecated alias of fee_amount."
          },
          "usd_rate": {
            "type": [
              "string",
              "null"
            ],
            "deprecated": true,
            "description": "Deprecated alias of paid_usd_rate."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "hidden_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "payload": {
            "type": [
              "string",
              "null"
            ]
          },
          "allow_comments": {
            "type": "boolean"
          },
          "allow_anonymous": {
            "type": "boolean"
          },
          "comment": {
            "type": [
              "string",
              "null"
            ],
            "description": "The payer's comment (only if allowed)."
          },
          "paid_anonymously": {
            "type": "boolean"
          },
          "is_anonymous": {
            "type": "boolean",
            "description": "Alias of paid_anonymously."
          },
          "paid_btn_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "paid_btn_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "swap_to": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_swapped": {
            "type": "boolean"
          },
          "swapped_uid": {
            "type": [
              "string",
              "null"
            ]
          },
          "swapped_to": {
            "type": [
              "string",
              "null"
            ]
          },
          "swapped_rate": {
            "type": [
              "string",
              "null"
            ]
          },
          "swapped_output": {
            "type": [
              "string",
              "null"
            ]
          },
          "swapped_usd_rate": {
            "type": [
              "string",
              "null"
            ]
          },
          "swapped_usd_amount": {
            "type": [
              "string",
              "null"
            ]
          },
          "pay_url": {
            "type": "string",
            "description": "t.me deep link the payer opens."
          },
          "bot_invoice_url": {
            "type": "string",
            "description": "Alias of pay_url."
          },
          "mini_app_invoice_url": {
            "type": "string",
            "description": "Alias of pay_url."
          },
          "web_app_invoice_url": {
            "type": "string",
            "description": "Alias of pay_url."
          },
          "paid_by_user_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The payer's Telegram id — null when the payer paid anonymously."
          },
          "open_amount": {
            "type": "boolean",
            "description": "Extension: this is an open-amount (donation) invoice."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "paid_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "refunded_amount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cumulative refunded crypto amount in the settlement asset, including for fiat-priced invoices."
          },
          "refunded_minor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Extension: the same in minor units (string)."
          },
          "refunded_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Extension: stamped once fully refunded (status stays paid)."
          },
          "rate_lock_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Extension: rate-lock deadline; null when no lock was requested."
          },
          "rate_lock_rates": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "description": "Extension: {asset_code: rate_string} stamped at creation — the exact rates quoted while the lock is live."
          },
          "expiration_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Alias of expiration_date."
          }
        },
        "required": [
          "invoice_id",
          "hash",
          "status",
          "currency_type",
          "asset",
          "fiat",
          "accepted_assets",
          "amount",
          "amount_minor",
          "paid_asset",
          "paid_amount",
          "paid_fiat_rate",
          "paid_usd_rate",
          "fee_asset",
          "fee_amount",
          "fee",
          "usd_rate",
          "description",
          "hidden_message",
          "payload",
          "allow_comments",
          "allow_anonymous",
          "comment",
          "paid_anonymously",
          "is_anonymous",
          "paid_btn_name",
          "paid_btn_url",
          "swap_to",
          "is_swapped",
          "swapped_uid",
          "swapped_to",
          "swapped_rate",
          "swapped_output",
          "swapped_usd_rate",
          "swapped_usd_amount",
          "pay_url",
          "bot_invoice_url",
          "mini_app_invoice_url",
          "web_app_invoice_url",
          "paid_by_user_id",
          "open_amount",
          "created_at",
          "paid_at",
          "refunded_amount",
          "refunded_minor",
          "refunded_at",
          "rate_lock_until",
          "rate_lock_rates",
          "expiration_date",
          "expires_at"
        ]
      },
      "RefundInvoiceRequest": {
        "type": "object",
        "required": [
          "invoice_id"
        ],
        "properties": {
          "invoice_id": {
            "oneOf": [
              {
                "type": "integer",
                "maximum": 9223372036854775807,
                "minimum": 1
              },
              {
                "type": "string",
                "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
              }
            ],
            "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
          },
          "amount": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AmountInput",
                "description": "Refund in the crypto asset used to settle the invoice (paid_asset for fiat invoices), never fiat units. Omitted/null means the full unrefunded crypto remainder; partial refunds accumulate."
              },
              {
                "type": "null"
              }
            ]
          },
          "spend_id": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 64,
                "description": "Optional replay key; empty/null disables idempotency. Recommended for retrying refunds."
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "CreateCheckRequest": {
        "type": "object",
        "required": [
          "asset"
        ],
        "properties": {
          "asset": {
            "type": "string",
            "maxLength": 16
          },
          "amount": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AmountInput"
              },
              {
                "type": "null"
              }
            ]
          },
          "pin_to_user_id": {
            "anyOf": [
              {
                "oneOf": [
                  {
                    "type": "integer",
                    "maximum": 9223372036854775807,
                    "minimum": 1
                  },
                  {
                    "type": "string",
                    "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                  }
                ],
                "description": "Restrict claiming to this Telegram user id."
              },
              {
                "type": "null"
              }
            ]
          },
          "pin_to_username": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 64,
                "description": "Restrict by @username (leading @ optional, case-insensitive); ignored when pin_to_user_id is also given. Must belong to an existing user of the platform."
              },
              {
                "type": "null"
              }
            ]
          },
          "spend_id": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 64,
                "description": "Optional replay key; empty/null disables idempotency."
              },
              {
                "type": "null"
              }
            ]
          },
          "recipients": {
            "anyOf": [
              {
                "type": "array",
                "minItems": 1,
                "maxItems": 100,
                "items": {
                  "$ref": "#/components/schemas/CheckRecipientRequest"
                },
                "description": "JSON body only. One seat per recipient; sum funds the check. Mutually exclusive with pins."
              },
              {
                "type": "null"
              }
            ]
          },
          "password": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 64,
                "description": "Optional claim password; empty/null means no password. Never returned."
              },
              {
                "type": "null"
              }
            ]
          },
          "expires_in": {
            "anyOf": [
              {
                "oneOf": [
                  {
                    "type": "integer",
                    "maximum": 9223372036854775807,
                    "minimum": 0
                  },
                  {
                    "type": "string",
                    "pattern": "^\\s*(?:\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)|[+-]?0+)\\s*$"
                  }
                ],
                "description": "Seconds until expiry; zero, null or omitted means no explicit deadline. Choose a duration that produces a representable expiry timestamp."
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "anyOf": [
          {
            "required": [
              "amount"
            ],
            "properties": {
              "amount": {
                "not": {
                  "type": "null"
                }
              }
            }
          },
          {
            "required": [
              "recipients"
            ],
            "properties": {
              "recipients": {
                "not": {
                  "type": "null"
                }
              }
            }
          }
        ],
        "allOf": [
          {
            "if": {
              "required": [
                "recipients"
              ],
              "properties": {
                "recipients": {
                  "not": {
                    "type": "null"
                  }
                }
              }
            },
            "then": {
              "properties": {
                "pin_to_user_id": {
                  "type": "null"
                },
                "pin_to_username": {
                  "enum": [
                    null,
                    ""
                  ]
                }
              }
            }
          }
        ]
      },
      "Check": {
        "type": "object",
        "properties": {
          "check_id": {
            "type": "integer"
          },
          "hash": {
            "type": "string",
            "description": "Public id."
          },
          "asset": {
            "type": "string"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount",
            "description": "Original funded total for a named list, or the single-use amount; not the unclaimed remainder."
          },
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "bot_check_url": {
            "type": "string",
            "description": "t.me claim deep link."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "activated",
              "expired"
            ],
            "description": "activated once fully claimed; expired (extension) once the lifetime ran out and the unclaimed remainder returned to the app; otherwise active — still claimable."
          },
          "pin_to_user_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "activated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Time of the last claim once fully activated; null before then."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "recipients": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/CheckRecipient"
            },
            "description": "Named seats on create/getChecks and check_expired; null for ordinary checks and check_activated payloads."
          },
          "activated_slot": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CheckRecipient"
              }
            ],
            "description": "Present only on a named-seat check_activated webhook."
          }
        },
        "required": [
          "check_id",
          "hash",
          "asset",
          "amount",
          "amount_minor",
          "bot_check_url",
          "status",
          "pin_to_user_id",
          "created_at",
          "activated_at",
          "expires_at",
          "recipients"
        ]
      },
      "TransferRequest": {
        "type": "object",
        "required": [
          "asset",
          "amount",
          "spend_id"
        ],
        "properties": {
          "user_id": {
            "anyOf": [
              {
                "oneOf": [
                  {
                    "type": "integer",
                    "maximum": 9223372036854775807,
                    "minimum": 1
                  },
                  {
                    "type": "string",
                    "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                  }
                ],
                "description": "Recipient's Telegram user id."
              },
              {
                "type": "null"
              }
            ]
          },
          "asset": {
            "type": "string",
            "minLength": 2,
            "maxLength": 16
          },
          "amount": {
            "$ref": "#/components/schemas/AmountInput",
            "description": "Also subject to platform min/max USD bounds."
          },
          "spend_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Required idempotency key, unique per app."
          },
          "comment": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 1024,
                "description": "Shown to the recipient."
              },
              {
                "type": "null"
              }
            ]
          },
          "disable_send_notification": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/BooleanInput",
                "description": "Suppresses the immediate recipient notification only. A later escrow claim still sends a notification.",
                "default": false
              },
              {
                "type": "null"
              }
            ]
          },
          "username": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 64,
                "description": "Leading @ optional, case-insensitive. Normalized username: a letter followed by 3..31 letters, digits or underscores. Unknown username creates pending escrow."
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "oneOf": [
          {
            "allOf": [
              {
                "required": [
                  "user_id"
                ],
                "properties": {
                  "user_id": {
                    "not": {
                      "type": "null"
                    }
                  }
                }
              }
            ],
            "properties": {
              "username": {
                "enum": [
                  null,
                  ""
                ]
              }
            }
          },
          {
            "allOf": [
              {
                "required": [
                  "username"
                ],
                "properties": {
                  "username": {
                    "not": {
                      "type": "null"
                    }
                  }
                }
              }
            ],
            "properties": {
              "user_id": {
                "type": "null"
              },
              "username": {
                "type": "string",
                "minLength": 1
              }
            }
          }
        ]
      },
      "Transfer": {
        "type": "object",
        "properties": {
          "transfer_id": {
            "type": "integer"
          },
          "hash": {
            "type": "string"
          },
          "user_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Recipient id; null until a pending username payout is claimed, and on an unclaimed returned payout."
          },
          "asset": {
            "type": "string"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "spend_id": {
            "type": "string"
          },
          "comment": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "completed",
              "returned"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "recipient_username": {
            "type": [
              "string",
              "null"
            ],
            "description": "Normalized username for an originally pending payout, retained after claim/return. Null on immediate payouts."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Escrow expiry deadline; null on immediate payouts. Retained after claim/return."
          },
          "invite_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Bot invitation link on pending API results; null on terminal results/webhooks."
          }
        },
        "required": [
          "transfer_id",
          "hash",
          "user_id",
          "asset",
          "amount",
          "amount_minor",
          "spend_id",
          "comment",
          "status",
          "created_at",
          "completed_at",
          "recipient_username",
          "expires_at",
          "invite_url"
        ]
      },
      "TransferBatchItemResult": {
        "type": "object",
        "required": [
          "ok",
          "spend_id"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "spend_id": {
            "type": "string"
          },
          "result": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Transfer"
              }
            ],
            "description": "Present when ok is true."
          },
          "error": {
            "type": "object",
            "description": "Present when ok is false — same error names as single transfer.",
            "properties": {
              "code": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "name"
            ]
          }
        },
        "oneOf": [
          {
            "properties": {
              "ok": {
                "const": true
              }
            },
            "required": [
              "result"
            ],
            "not": {
              "required": [
                "error"
              ]
            }
          },
          {
            "properties": {
              "ok": {
                "const": false
              }
            },
            "required": [
              "error"
            ],
            "not": {
              "required": [
                "result"
              ]
            }
          }
        ]
      },
      "CreatePlanRequest": {
        "type": "object",
        "required": [
          "name",
          "asset",
          "amount",
          "period_days"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "asset": {
            "type": "string",
            "maxLength": 16
          },
          "amount": {
            "$ref": "#/components/schemas/AmountInput",
            "description": "The per-period charge."
          },
          "period_days": {
            "oneOf": [
              {
                "type": "integer",
                "maximum": 365,
                "minimum": 1
              },
              {
                "type": "string",
                "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,1}|[1-2][0-9]{2}|3[0-5][0-9]{1}|36[0-4]|365)\\s*$"
              }
            ],
            "description": "Additionally bounded server-side to the platform minimum — outside returns 409 invalid_period."
          }
        }
      },
      "Plan": {
        "type": "object",
        "properties": {
          "plan_id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "asset": {
            "type": "string"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "period_days": {
            "type": "integer"
          },
          "archived": {
            "type": "boolean",
            "description": "Archived plans accept no new approvals."
          },
          "mini_app_subscribe_url": {
            "type": "string",
            "description": "t.me deep link users open to approve."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "plan_id",
          "name",
          "asset",
          "amount",
          "amount_minor",
          "period_days",
          "archived",
          "mini_app_subscribe_url",
          "created_at"
        ]
      },
      "PlanWithStats": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Plan"
          },
          {
            "type": "object",
            "properties": {
              "active_subscribers": {
                "type": "integer",
                "description": "Count of live (active + grace) subscriptions on the plan."
              }
            },
            "required": [
              "active_subscribers"
            ]
          }
        ]
      },
      "Subscription": {
        "type": "object",
        "properties": {
          "subscription_id": {
            "type": "integer"
          },
          "plan_id": {
            "type": "integer"
          },
          "user_id": {
            "type": "integer",
            "description": "Subscriber's Telegram id."
          },
          "asset": {
            "type": "string",
            "description": "Plan snapshot."
          },
          "amount": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Amount"
              }
            ],
            "description": "Snapshot per-period amount."
          },
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "period_days": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "grace",
              "cancelled",
              "expired"
            ],
            "description": "grace = renewal failed, retrying inside the grace window."
          },
          "auto_renew": {
            "type": "boolean"
          },
          "period_no": {
            "type": "integer",
            "description": "Number of paid periods so far."
          },
          "current_period_start": {
            "type": "string",
            "format": "date-time"
          },
          "current_period_end": {
            "type": "string",
            "format": "date-time",
            "description": "The paid-through deadline — read this as the access deadline."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "cancelled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "cancelled_by": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "user",
              "merchant",
              null
            ]
          },
          "expired_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "subscription_id",
          "plan_id",
          "user_id",
          "asset",
          "amount",
          "amount_minor",
          "period_days",
          "status",
          "auto_renew",
          "period_no",
          "current_period_start",
          "current_period_end",
          "created_at",
          "cancelled_at",
          "cancelled_by",
          "expired_at"
        ]
      },
      "SubscriptionCharge": {
        "type": "object",
        "description": "The charge sub-object attached to subscription_charged webhook payloads.",
        "properties": {
          "period_no": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "initial",
              "renewal",
              "resubscribe"
            ]
          },
          "asset": {
            "type": "string"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "fee": {
            "$ref": "#/components/schemas/Amount"
          },
          "paid_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "period_no",
          "kind",
          "asset",
          "amount",
          "fee",
          "paid_at"
        ]
      },
      "WebhookUpdate": {
        "type": "object",
        "required": [
          "update_id",
          "update_type",
          "request_date",
          "payload"
        ],
        "properties": {
          "update_id": {
            "type": "integer",
            "description": "Stable across redeliveries of the same event — dedupe on it."
          },
          "update_type": {
            "type": "string",
            "enum": [
              "invoice_paid",
              "invoice_expired",
              "check_activated",
              "refund_completed",
              "subscription_activated",
              "subscription_charged",
              "subscription_cancelled",
              "subscription_expired",
              "transfer_completed",
              "transfer_returned",
              "check_expired"
            ]
          },
          "request_date": {
            "type": "string",
            "format": "date-time",
            "description": "Stamped per delivery attempt — UTC, ISO 8601 with microseconds (2026-08-11T12:00:00.123456Z)."
          },
          "payload": {
            "description": "The full object for the event type."
          }
        }
      },
      "UpdateAppRequest": {
        "type": "object",
        "description": "Omitted or null fields leave their settings unchanged. Empty object is a no-op.",
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string",
                "description": "Trimmed app name. Link/handle-shaped names and protected brand names are rejected.",
                "minLength": 1,
                "maxLength": 64
              },
              {
                "type": "null"
              }
            ]
          },
          "webhook_url": {
            "anyOf": [
              {
                "type": "string",
                "description": "Absolute public HTTPS URL, no embedded credentials; empty string clears it. Redirects are not followed.",
                "maxLength": 256
              },
              {
                "type": "null"
              }
            ]
          },
          "webhook_events": {
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "invoice_expired",
                    "check_activated",
                    "refund_completed",
                    "subscription_activated",
                    "subscription_charged",
                    "subscription_cancelled",
                    "subscription_expired",
                    "transfer_completed",
                    "transfer_returned",
                    "check_expired"
                  ]
                }
              },
              {
                "type": "string",
                "description": "Comma-separated or JSON-encoded array of extended event names. Empty string clears the selection."
              },
              {
                "type": "null"
              }
            ],
            "description": "Replace the extended-event selection; [] clears it. invoice_paid is automatic when a URL is set and must not be included."
          }
        }
      },
      "CheckRecipientRequest": {
        "type": "object",
        "required": [
          "amount"
        ],
        "properties": {
          "user_id": {
            "anyOf": [
              {
                "oneOf": [
                  {
                    "type": "integer",
                    "maximum": 9223372036854775807,
                    "minimum": 1
                  },
                  {
                    "type": "string",
                    "pattern": "^\\s*\\+?0*(?:[1-9][0-9]{0,17}|[1-8][0-9]{18}|9[0-1][0-9]{17}|92[0-1][0-9]{16}|922[0-2][0-9]{15}|9223[0-2][0-9]{14}|92233[0-6][0-9]{13}|922337[0-1][0-9]{12}|92233720[0-2][0-9]{10}|922337203[0-5][0-9]{9}|9223372036[0-7][0-9]{8}|92233720368[0-4][0-9]{7}|922337203685[0-3][0-9]{6}|9223372036854[0-6][0-9]{5}|92233720368547[0-6][0-9]{4}|922337203685477[0-4][0-9]{3}|9223372036854775[0-7][0-9]{2}|922337203685477580[0-6]|9223372036854775807)\\s*$"
                  }
                ],
                "description": "Integer or integer string; parsed value must be within 1..9223372036854775807."
              },
              {
                "type": "null"
              }
            ]
          },
          "username": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 64,
                "description": "Optional leading @, case-insensitive; normalized handle must start with a letter and contain 4..32 letters, digits or underscores."
              },
              {
                "type": "null"
              }
            ]
          },
          "amount": {
            "$ref": "#/components/schemas/AmountInput"
          }
        },
        "oneOf": [
          {
            "allOf": [
              {
                "required": [
                  "user_id"
                ],
                "properties": {
                  "user_id": {
                    "not": {
                      "type": "null"
                    }
                  }
                }
              }
            ],
            "properties": {
              "username": {
                "enum": [
                  null,
                  ""
                ]
              }
            }
          },
          {
            "allOf": [
              {
                "required": [
                  "username"
                ],
                "properties": {
                  "username": {
                    "not": {
                      "type": "null"
                    }
                  }
                }
              }
            ],
            "properties": {
              "user_id": {
                "type": "null"
              },
              "username": {
                "type": "string",
                "minLength": 1
              }
            }
          }
        ]
      },
      "CheckRecipient": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "username": {
            "type": [
              "string",
              "null"
            ]
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "claimed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "user_id",
          "username",
          "amount",
          "amount_minor",
          "claimed_at"
        ]
      },
      "AmountInput": {
        "type": "string",
        "minLength": 1,
        "maxLength": 40,
        "description": "Positive amount as a decimal string (not a JSON number). Surrounding whitespace and exponent notation are accepted. Crypto rounds to asset precision, midpoint away from zero, and must remain positive; fiat requires exact positive cents. Values must fit the server decimal parser and scaling arithmetic.",
        "pattern": "^\\s*\\+?(?=[0-9.]*[1-9])(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?\\s*$"
      },
      "BooleanInput": {
        "oneOf": [
          {
            "type": "boolean"
          },
          {
            "type": "integer",
            "enum": [
              0,
              1
            ]
          },
          {
            "type": "string",
            "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[01]|[Yy][Ee][Ss]|[Nn][Oo]|[Oo][Nn]|[Oo][Ff][Ff])\\s*$"
          }
        ]
      },
      "WithdrawalPayoutWebhook": {
        "type": "object",
        "properties": {
          "transfer_id": {
            "type": "integer",
            "description": "Numeric withdrawal id; a separate id namespace from user transfers. Use kind and hash to distinguish."
          },
          "hash": {
            "type": "string"
          },
          "user_id": {
            "type": "null"
          },
          "asset": {
            "type": "string"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "amount_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "spend_id": {
            "type": "string"
          },
          "comment": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "completed",
              "failed"
            ],
            "description": "completed on transfer_completed; failed (not returned) on transfer_returned."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "recipient_username": {
            "type": "null"
          },
          "expires_at": {
            "type": "null"
          },
          "invite_url": {
            "type": "null"
          },
          "kind": {
            "type": "string",
            "const": "withdrawal"
          },
          "fee_minor": {
            "$ref": "#/components/schemas/AmountMinor"
          },
          "network": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "txid": {
            "type": [
              "string",
              "null"
            ]
          },
          "withdrawal_id": {
            "type": "string",
            "format": "uuid"
          },
          "withdrawal_status": {
            "type": "string",
            "enum": [
              "confirmed",
              "failed",
              "rejected",
              "cancelled"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Failure detail when status=failed; null on completion."
          }
        },
        "required": [
          "transfer_id",
          "hash",
          "user_id",
          "asset",
          "amount",
          "amount_minor",
          "spend_id",
          "comment",
          "status",
          "created_at",
          "completed_at",
          "recipient_username",
          "expires_at",
          "invite_url",
          "kind",
          "fee_minor",
          "network",
          "address",
          "txid",
          "withdrawal_id",
          "withdrawal_status",
          "error"
        ],
        "description": "Webhook-only on-chain payout variant, created through the app. transfer/transferBatch cannot create it and getTransfers does not list it. Distinguish it by kind=withdrawal."
      }
    }
  }
}
