{
  "openapi": "3.1.0",
  "info": {
    "title": "gasrescue API",
    "version": "1.0.0",
    "summary": "Send or swap stablecoins from a wallet that has no gas.",
    "description": "The wallet signs, gasrescue submits the transaction and pays the network fee. Network fee and service fee come out of the amount. Flow: POST /api/quote or /api/swap-quote, show the quote to the user, GET /api/transfers/{id}/sign, sign every step with the sender's wallet, POST /api/transfers/{id}/submit. gasrescue never holds keys. Limits: $2 to $1,000 per transfer (Tron at least $10), 10 transfers per wallet in 24 hours, 20 requests per minute per IP. An MCP server for agents: npx -y gasrescue-mcp. Guide: https://gasrescue.io/agents"
  },
  "servers": [{ "url": "https://gasrescue.io" }],
  "paths": {
    "/api/config": {
      "get": {
        "operationId": "getConfig",
        "summary": "Chains, tokens per chain, swap options, fees, limits and the contracts and fee wallets of every chain.",
        "responses": { "200": { "description": "Configuration", "content": { "application/json": { "schema": { "type": "object" } } } } }
      }
    },
    "/api/wallet-check": {
      "post": {
        "operationId": "checkWallet",
        "summary": "Read-only: stablecoins and gas of one address on every chain it is valid for, and whether a token is frozen by its issuer.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["address"], "properties": { "address": { "$ref": "#/components/schemas/Address" } } } } } },
        "responses": {
          "200": { "description": "Per chain: tokens held, gas balance, stuck, frozen", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/balances": {
      "post": {
        "operationId": "getBalances",
        "summary": "What a wallet holds of every token gasrescue can send on one chain.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["chain", "address"], "properties": { "chain": { "$ref": "#/components/schemas/Chain" }, "address": { "$ref": "#/components/schemas/Address" } } } } } },
        "responses": { "200": { "description": "Balances", "content": { "application/json": { "schema": { "type": "object" } } } }, "400": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/api/quote": {
      "post": {
        "operationId": "quoteSend",
        "summary": "Quote a send without gas. Free, valid for 10 minutes; nothing moves until it is signed.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "required": ["chain", "token", "sender", "recipient", "amount"],
            "properties": {
              "chain": { "$ref": "#/components/schemas/Chain" },
              "token": { "$ref": "#/components/schemas/Token" },
              "sender": { "$ref": "#/components/schemas/Address" },
              "recipient": { "$ref": "#/components/schemas/Address" },
              "amount": { "$ref": "#/components/schemas/Amount" }
            }
          } } }
        },
        "responses": { "200": { "$ref": "#/components/responses/Transfer" }, "400": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/api/swap-quote": {
      "post": {
        "operationId": "quoteSwap",
        "summary": "Quote a swap of a stablecoin into the gas coin or another stablecoin, without gas. The result goes back to the sender. Valid for 2 minutes. Not on Tron.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "required": ["chain", "token", "tokenOut", "sender", "amount"],
            "properties": {
              "chain": { "$ref": "#/components/schemas/Chain" },
              "token": { "$ref": "#/components/schemas/Token" },
              "tokenOut": { "type": "string", "description": "ETH, SOL, HYPE or another stablecoin; see swap.out in /api/config." },
              "sender": { "$ref": "#/components/schemas/Address" },
              "amount": { "$ref": "#/components/schemas/Amount" }
            }
          } } }
        },
        "responses": { "200": { "$ref": "#/components/responses/Transfer" }, "400": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/api/transfers/{id}": {
      "get": {
        "operationId": "getTransfer",
        "summary": "Status of a quote or transfer: quoted, submitting, sent, confirmed, failed or expired.",
        "parameters": [{ "$ref": "#/components/parameters/Id" }],
        "responses": { "200": { "$ref": "#/components/responses/Transfer" }, "400": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/api/transfers/{id}/sign": {
      "get": {
        "operationId": "getSigningRequest",
        "summary": "What the sender's wallet has to sign.",
        "description": "kind evm-typed-data: sign every steps[].typedData with eth_signTypedData_v4, in order. For an authorization, binding is the order whose EIP-712 hash is the nonce, so recipient, fee and minimum can be checked before signing. kind tron-transactions: sign every steps[].transaction (its txID) with the sender's key. kind solana-transaction or solana-versioned-transaction: sign the base64 transaction with the sender's key.",
        "parameters": [{ "$ref": "#/components/parameters/Id" }],
        "responses": { "200": { "description": "Signing request", "content": { "application/json": { "schema": { "type": "object", "properties": { "kind": { "type": "string" }, "steps": { "type": "array", "items": { "type": "object" } }, "binding": { "type": ["object", "null"] }, "transaction": { "type": "string" }, "transfer": { "$ref": "#/components/schemas/Transfer" } } } } } }, "400": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/api/transfers/{id}/submit": {
      "post": {
        "operationId": "submitSignatures",
        "summary": "Submit the signatures. gasrescue sends the transaction and pays the gas.",
        "parameters": [{ "$ref": "#/components/parameters/Id" }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "properties": {
              "signatures": { "type": "array", "items": { "type": "string" }, "description": "EVM and Tron: one signature per step, in order." },
              "signedTransaction": { "type": "string", "description": "Solana: the signed transaction, base64." }
            }
          } } }
        },
        "responses": { "200": { "$ref": "#/components/responses/Transfer" }, "400": { "$ref": "#/components/responses/Error" } }
      }
    }
  },
  "components": {
    "parameters": {
      "Id": { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[0-9a-f]{24}$" } }
    },
    "schemas": {
      "Chain": { "type": "string", "enum": ["base", "arbitrum", "ethereum", "hyperevm", "robinhood", "solana", "tron"] },
      "Token": { "type": "string", "description": "USDC, USDT, USDT0 or USDG; see tokens per chain in /api/config." },
      "Address": { "type": "string", "description": "0x… on EVM chains, base58 on Solana, T… on Tron." },
      "Amount": { "type": "string", "description": "Decimal amount of the token, e.g. \"25\". Fees are deducted from it." },
      "Transfer": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "chain": { "type": "string" },
          "token": { "type": "string" },
          "sender": { "type": "string" },
          "recipient": { "type": "string" },
          "amount": { "type": "string" },
          "fee": { "type": "string", "description": "Network fee plus service fee, in the token." },
          "net": { "type": "string", "description": "What the recipient gets, or what is swapped." },
          "feePercent": { "type": "string" },
          "breakdown": { "type": "object" },
          "swap": { "type": ["object", "null"], "properties": { "tokenOut": { "type": "string" }, "out": { "type": "string" }, "minOut": { "type": "string" } } },
          "contract": { "type": ["string", "null"] },
          "feeRecipient": { "type": ["string", "null"] },
          "status": { "type": "string", "enum": ["quoted", "submitting", "sent", "confirmed", "failed", "expired"] },
          "tx": { "type": ["string", "null"] },
          "explorerUrl": { "type": ["string", "null"] },
          "error": { "type": ["string", "null"] },
          "expiresAt": { "type": "string", "format": "date-time" }
        }
      }
    },
    "responses": {
      "Transfer": { "description": "Transfer", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Transfer" } } } },
      "Error": { "description": "Refused, with a message for the user", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "retryable": { "type": "boolean" } } } } } }
    }
  }
}
