{
  "openapi": "3.1.0",
  "info": {
    "title": "Drip Tools API",
    "version": "1.0.0",
    "summary": "Swap mainnet tokens for testnet tokens.",
    "description": "Public REST API of [Drip Tools](https://drip.tools): pay with a mainnet token, receive testnet tokens. Guides and the full reference: <https://docs.drip.tools>.\n\n- **No auth.** Requests are rate-limited per IP (600/min), `POST /quote` more tightly (10/min per IP, 30/min per `sender`).\n- **Amounts** in quotes are base-unit integer strings; order-history amounts are human-readable decimal strings.\n- **Native coin** = token address `0x0000000000000000000000000000000000000000` on every chain family.\n- **`chainId` types vary**: a string in `/chains`, `/quotes` and order objects; a number in `POST /quote` and `/prices`.\n- **Timestamps** are epoch seconds."
  },
  "externalDocs": {
    "description": "Drip Tools developer docs",
    "url": "https://docs.drip.tools"
  },
  "servers": [
    {
      "url": "https://drip.tools/api"
    }
  ],
  "tags": [
    {
      "name": "Discovery",
      "description": "Chains, tokens, TO-token rates."
    },
    {
      "name": "Quote",
      "description": "Signed quotes."
    },
    {
      "name": "Prices",
      "description": "FROM-token prices (Chainlink)."
    },
    {
      "name": "Orders",
      "description": "Tracking payments and payouts."
    }
  ],
  "paths": {
    "/chains": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "operationId": "getChains",
        "summary": "List chains, tokens and pay targets",
        "description": "Every supported chain, its tokens, and where to pay. FROM chains (mainnet) expose `contracts.depository`; TO chains (testnet) carry no contract you interact with. `chainId` is a **string** here. Cacheable for 15 s.",
        "parameters": [
          {
            "name": "unwrap",
            "in": "query",
            "required": false,
            "description": "Return one flat array `[...testnet, ...mainnet]` instead of `{ testnet, mainnet }`.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ testnet, mainnet }`, or a flat array with `unwrap=true`.",
            "headers": {
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "title": "Grouped",
                      "required": [
                        "testnet",
                        "mainnet"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "testnet": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Chain"
                          }
                        },
                        "mainnet": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Chain"
                          }
                        }
                      }
                    },
                    {
                      "type": "array",
                      "title": "Flat (unwrap=true)",
                      "items": {
                        "$ref": "#/components/schemas/Chain"
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/quotes": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "operationId": "getRates",
        "summary": "TO-token USD rates and liquidity",
        "description": "Read-only price list of the TO tokens (not the quote endpoint \u2014 that is `POST /quote`). With neither parameter: every enabled TO token. With both: the single matching object.",
        "parameters": [
          {
            "name": "chainId",
            "in": "query",
            "required": false,
            "description": "TO chain id. Pass together with `symbol`.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]+$"
            }
          },
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "description": "TO token symbol. Pass together with `chainId`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "An array of rates, or one rate when both parameters are given.",
            "headers": {
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Rate"
                      }
                    },
                    {
                      "$ref": "#/components/schemas/Rate"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Only one of `chainId` / `symbol` given (`Missing chainId or symbol`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such TO token on that chain (`No quote found for symbol \u2026`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/WarmingUp"
          }
        }
      }
    },
    "/prices": {
      "get": {
        "tags": [
          "Prices"
        ],
        "operationId": "getPrices",
        "summary": "Latest FROM-token prices",
        "description": "The latest Chainlink price of every enabled FROM token, or a filtered subset. `chainId` is a **number** here. An unknown chain or token returns `[]`. Cacheable for 5 s.",
        "parameters": [
          {
            "name": "chainId",
            "in": "query",
            "required": false,
            "description": "Only tokens of this FROM chain.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "address",
            "in": "query",
            "required": false,
            "description": "Only this token (`0x\u2026`, base58 SPL mint, `0x0\u20260` for native).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of prices.",
            "headers": {
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Price"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed `address` (`Invalid address`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/WarmingUp"
          }
        }
      }
    },
    "/prices/at": {
      "get": {
        "tags": [
          "Prices"
        ],
        "operationId": "getPriceAt",
        "summary": "Historical FROM-token price",
        "description": "The price the feed showed at a given moment \u2014 the latest feed round at or before `ts`. Cacheable for 60 s.",
        "parameters": [
          {
            "name": "chainId",
            "in": "query",
            "required": true,
            "description": "FROM chain id.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "address",
            "in": "query",
            "required": true,
            "description": "Token address (`0x0\u20260` for native).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ts",
            "in": "query",
            "required": true,
            "description": "Epoch seconds.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The feed round at or before `ts`.",
            "headers": {
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PriceAt"
                }
              }
            }
          },
          "400": {
            "description": "`chainId, address and ts are required`, `Invalid address` or `Invalid ts`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`No price recorded at or before ts` (history starts when the token was listed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/quote": {
      "post": {
        "tags": [
          "Quote"
        ],
        "operationId": "createQuote",
        "summary": "Price and sign a swap",
        "description": "Prices a swap, locks the exact TO amounts and returns a backend-signed quote, valid **180 s**. Pay **exactly** `fromTotal` from `sender` before `deadline`. `chainId` / `toChainId` are **numbers** here. Minimum order value: $1 single, $5 multisend. Rate-limited to 10 quotes/min per IP and 30/min per `sender`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "examples": {
                "single": {
                  "value": {
                    "chainId": 8453,
                    "fromToken": "0x0000000000000000000000000000000000000000",
                    "sender": "0x9246ef7F2b7fEcE8AfD8a72271A76Cc2A8d8A3E5",
                    "toChainId": 421614,
                    "toToken": "0x0000000000000000000000000000000000000000",
                    "recipients": [
                      "0x2F4edA5e7EEa6eb634C7FBb0acAFA04b8938F63A"
                    ],
                    "toAmounts": [
                      "5000000000000000000"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The signed quote. `X-RateLimit-*` describe the per-sender bucket (30/min).",
            "headers": {
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request or its amounts were refused; `code` says why. A body that is not valid JSON is rejected by the parser with a plain `{ error }` and no `code`.",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/QuoteError400"
                    },
                    {
                      "$ref": "#/components/schemas/Error"
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "The FROM chain has no pay target configured (`NO_VAULT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteError500"
                }
              }
            }
          },
          "503": {
            "description": "A price is unavailable, quoting is paused, or the backend is warming up.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/QuoteError503"
                    },
                    {
                      "$ref": "#/components/schemas/WarmingUp"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/order": {
      "get": {
        "tags": [
          "Orders"
        ],
        "operationId": "getOrder",
        "summary": "One order, or all orders of one payment",
        "description": "Pass **one** of `from_tx_hash` or `id`:\n\n- `from_tx_hash=<ref>` \u2192 `{ data: Order[], refund }`. `ref` is the EVM tx hash, the Solana signature, or on TON the `quoteId` (or the treasury-side hash once honoured).\n- `from_tx_hash=<ref>&paginate=1[&limit][&cursor]` \u2192 summary + a keyset page of recipients.\n- `id=<order_id>` \u2192 one order object, or `null` with **200** if it is not indexed yet (keep polling). Ids are case-sensitive.\n\n`chainId` is a **string** in order objects.",
        "parameters": [
          {
            "name": "from_tx_hash",
            "in": "query",
            "required": false,
            "description": "Payment reference: EVM tx hash, Solana signature, or TON `quoteId` / treasury-side hash.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "paginate",
            "in": "query",
            "required": false,
            "description": "With `from_tx_hash`: return a summary plus a keyset page of recipients.",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "With `paginate=1`: page size, default 100, max 1000. `all` = the whole remainder; `0` = summary only.",
            "schema": {
              "oneOf": [
                {
                  "type": "integer",
                  "minimum": 0
                },
                {
                  "type": "string",
                  "enum": [
                    "all"
                  ]
                }
              ]
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "With `paginate=1`: `next_cursor` from the previous page.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]+$"
            }
          },
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Order id (case-sensitive).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Depends on the mode \u2014 see the description.",
            "headers": {
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/PaymentOrders"
                    },
                    {
                      "$ref": "#/components/schemas/RecipientsPage"
                    },
                    {
                      "$ref": "#/components/schemas/Order"
                    },
                    {
                      "type": "null",
                      "description": "`?id=` not indexed yet."
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Neither parameter given (`Provide order id or from_tx_hash`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/orders": {
      "get": {
        "tags": [
          "Orders"
        ],
        "operationId": "getOrders",
        "summary": "A payer's order history",
        "description": "Keyset-paginated history, newest first. One row per order, or with `view=batches` one row per payment (multisend collapsed), including payments that could not become an order (`isRefund: true`). Resend the filters with every page.",
        "parameters": [
          {
            "$ref": "#/components/parameters/AddressList"
          },
          {
            "name": "view",
            "in": "query",
            "required": false,
            "description": "`batches` = one row per payment. Otherwise one row per order.",
            "schema": {
              "type": "string",
              "enum": [
                "batches"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1\u2013100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "`next_cursor` from a prior response \u2014 the next (older) page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since_cursor",
            "in": "query",
            "required": false,
            "description": "Poll for rows newer than this cursor (`latest_item_cursor`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "`view=batches` only: aggregate status filter; `refund` lists only refund rows.",
            "schema": {
              "type": "string",
              "enum": [
                "processing",
                "partial",
                "completed",
                "failed",
                "refund"
              ]
            }
          },
          {
            "name": "to_chain_id",
            "in": "query",
            "required": false,
            "description": "`view=batches` only: TO chain filter.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]+$"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "`view=batches` only: prefix search over order ids / FROM tx hashes (at least 2 characters).",
            "schema": {
              "type": "string",
              "minLength": 2
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of order objects, or of batch rows with `view=batches`.",
            "headers": {
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/OrderHistory"
                    },
                    {
                      "$ref": "#/components/schemas/BatchHistory"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`Incorrect address`, or `status must be one of: processing, partial, completed, failed, refund`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/orders/status": {
      "post": {
        "tags": [
          "Orders"
        ],
        "operationId": "getOrderStatuses",
        "summary": "Bulk status by order id",
        "description": "Lightweight status for order ids you already hold. Unknown ids are omitted; `ids: []` \u2192 `{ \"data\": [] }`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "ids"
                ],
                "additionalProperties": false,
                "properties": {
                  "ids": {
                    "type": "array",
                    "maxItems": 500,
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "examples": {
                "ids": {
                  "value": {
                    "ids": [
                      "orderId1",
                      "orderId2"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Status of each known id.",
            "headers": {
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "OrderStatuses",
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OrderStatus"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`ids must be an array and is required`, or `too many ids (max 500)`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/orders/load": {
      "get": {
        "tags": [
          "Orders"
        ],
        "operationId": "getOrdersLoad",
        "summary": "Dispense-queue depth and ETA",
        "description": "Queue depth and drain rate per TO chain, for estimating how long a payout will take. With `ref`, adds your position in line.",
        "parameters": [
          {
            "name": "ref",
            "in": "query",
            "required": false,
            "description": "An order id or a FROM tx hash (or the TON treasury-side hash).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Queue stats; `ref` only when the parameter was passed.",
            "headers": {
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrdersLoad"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/orders/stream": {
      "get": {
        "tags": [
          "Orders"
        ],
        "operationId": "streamOrders",
        "summary": "Server-Sent Events on order changes",
        "description": "A Server-Sent Events feed that fires whenever an order or refund of the given payer identities changes. **Notification only** \u2014 re-fetch with `GET /order` or `GET /orders`. Not rate-limited.\n\n- `: connected` \u2014 comment sent on open.\n- `event: orders-changed` with `data: {\"txs\":[\"<from tx hash>\", \u2026]}` \u2014 the FROM tx hashes whose orders changed.\n- `: ping` \u2014 keep-alive comment every 25 s.",
        "parameters": [
          {
            "$ref": "#/components/parameters/AddressList"
          }
        ],
        "responses": {
          "200": {
            "description": "An open `text/event-stream`.",
            "headers": {
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE frames: comments `: connected` / `: ping`, and `orders-changed` events whose `data` is JSON `{ \"txs\": string[] }`."
                },
                "examples": {
                  "changed": {
                    "value": ": connected\n\nevent: orders-changed\ndata: {\"txs\":[\"0x<from tx>\"]}\n\n: ping\n\n"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed `address` (`Incorrect address`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`Too many subscribers` \u2014 fall back to polling.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Chain": {
        "type": "object",
        "description": "A supported chain. FROM chains (`isTestnet: false`) are mainnets you pay on; TO chains (`isTestnet: true`) are testnets you receive on.",
        "required": [
          "chainId",
          "name",
          "displayName",
          "explorer",
          "isTestnet",
          "isEnabled",
          "rpc",
          "tokens"
        ],
        "properties": {
          "chainId": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Chain id, **as a string**. Non-EVM chains use synthetic ids (Solana mainnet `1399811149`, TON mainnet `1667471768`)."
          },
          "name": {
            "type": "string",
            "description": "Internal slug (`arbitrum`, `solana_mainnet`, \u2026). Use `displayName` for labels."
          },
          "displayName": {
            "type": "string",
            "description": "Human-readable chain name."
          },
          "explorer": {
            "type": "string",
            "format": "uri",
            "description": "Block-explorer base URL."
          },
          "isTestnet": {
            "type": "boolean",
            "description": "`false` = FROM (mainnet) side; `true` = TO (testnet) side."
          },
          "isEnabled": {
            "type": "boolean",
            "description": "Disabled chains are still listed but not swappable."
          },
          "isPaused": {
            "type": "boolean",
            "const": true,
            "description": "Present, and `true`, only while the chain is temporarily paused: still listed, but `POST /quote` refuses it. Comes with `pausedReason`."
          },
          "pausedReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Operator free text; present only together with `isPaused`."
          },
          "family": {
            "type": "string",
            "enum": [
              "solana",
              "ton",
              "bitcoin"
            ],
            "description": "Present only on non-EVM chains. Absent = EVM."
          },
          "cluster": {
            "type": "string",
            "enum": [
              "mainnet-beta",
              "testnet",
              "devnet"
            ],
            "description": "Solana only: `mainnet-beta` (FROM), `testnet` / `devnet` (TO)."
          },
          "network": {
            "type": "string",
            "enum": [
              "testnet",
              "testnet4",
              "signet"
            ],
            "description": "Bitcoin testnets only."
          },
          "rpc": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "Ordered public RPC endpoints: `rpc[0]` is the primary, the rest are fallbacks."
          },
          "tokens": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Token"
            }
          },
          "contracts": {
            "type": "object",
            "description": "FROM chains only. Absent on TO chains, and on a FROM chain where swaps are not enabled.",
            "required": [
              "depository"
            ],
            "additionalProperties": false,
            "properties": {
              "depository": {
                "type": "string",
                "description": "The pay target: the depository contract (EVM `0x\u2026`) or the treasury account (Solana base58, TON `UQ\u2026`)."
              }
            }
          }
        },
        "additionalProperties": false,
        "dependentRequired": {
          "isPaused": [
            "pausedReason"
          ]
        },
        "allOf": [
          {
            "if": {
              "required": [
                "family"
              ],
              "properties": {
                "family": {
                  "const": "solana"
                }
              }
            },
            "then": {
              "required": [
                "cluster"
              ]
            },
            "else": {
              "not": {
                "required": [
                  "cluster"
                ]
              }
            }
          },
          {
            "if": {
              "required": [
                "family"
              ],
              "properties": {
                "family": {
                  "const": "bitcoin"
                }
              }
            },
            "then": {
              "required": [
                "network"
              ]
            },
            "else": {
              "not": {
                "required": [
                  "network"
                ]
              }
            }
          },
          {
            "if": {
              "properties": {
                "isTestnet": {
                  "const": true
                }
              }
            },
            "then": {
              "not": {
                "required": [
                  "contracts"
                ]
              }
            }
          }
        ]
      },
      "Token": {
        "type": "object",
        "required": [
          "symbol",
          "displayName",
          "decimals",
          "address",
          "priceFeed"
        ],
        "properties": {
          "symbol": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "decimals": {
            "type": "integer",
            "minimum": 0
          },
          "address": {
            "type": "string",
            "description": "`0x0000000000000000000000000000000000000000` = the native coin on every family; an ERC-20 `0x\u2026` address, or a base58 SPL mint on Solana."
          },
          "priceFeed": {
            "description": "Informational: the oracle feed the price comes from \u2014 a feed address, `{ chainId, address }` when the feed lives on another chain, or `0x` on TO tokens (not priced).",
            "oneOf": [
              {
                "type": "string",
                "pattern": "^0x([0-9a-fA-F]{40})?$"
              },
              {
                "type": "object",
                "required": [
                  "chainId",
                  "address"
                ],
                "additionalProperties": false,
                "properties": {
                  "chainId": {
                    "type": "integer",
                    "description": "Feed chain id \u2014 a **number** here."
                  },
                  "address": {
                    "type": "string",
                    "pattern": "^0x[0-9a-fA-F]{40}$"
                  }
                }
              }
            ]
          },
          "quoteDecimals": {
            "type": "integer",
            "minimum": 0,
            "description": "Bitcoin testnet tokens only: how many decimal places a TO amount may have."
          },
          "minTo": {
            "type": "string",
            "pattern": "^[0-9]+(\\.[0-9]+)?$",
            "description": "Bitcoin testnet tokens only: per-recipient minimum, in whole tokens."
          }
        },
        "additionalProperties": false
      },
      "Rate": {
        "type": "object",
        "description": "USD rate and dispensable liquidity of one TO token. The response carries a few more bookkeeping fields, not listed here.",
        "required": [
          "chainName",
          "chainId",
          "symbol",
          "address",
          "decimals",
          "price",
          "balance",
          "available"
        ],
        "properties": {
          "chainName": {
            "type": "string"
          },
          "chainId": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "TO chain id, **as a string**."
          },
          "symbol": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "decimals": {
            "type": "integer",
            "minimum": 0
          },
          "price": {
            "type": "number",
            "description": "USD rate of the TO token (a plain number) \u2014 the one `POST /quote` prices with."
          },
          "balance": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "What can be dispensed right now, **base units**. Orders above it are refused with `NO_LIQUIDITY`."
          },
          "available": {
            "type": "boolean",
            "description": "`false` when the token cannot be dispensed right now (`NO_TO_PRICE` at quote time)."
          }
        }
      },
      "Price": {
        "type": "object",
        "description": "Latest Chainlink answer for one FROM token. USD = `price / 10^priceDecimals`. `price`, `priceDecimals`, `updatedAt` and `observedAt` are `null` only for a token that has never been priced (`available: false`).",
        "required": [
          "chainId",
          "chainName",
          "symbol",
          "address",
          "tokenDecimals",
          "price",
          "priceDecimals",
          "updatedAt",
          "observedAt",
          "available"
        ],
        "properties": {
          "chainId": {
            "type": "integer",
            "description": "FROM chain id, **as a number**."
          },
          "chainName": {
            "type": "string",
            "description": "Label only."
          },
          "symbol": {
            "type": "string",
            "description": "Label only."
          },
          "address": {
            "type": "string",
            "description": "Token address in canonical form (lowercased `0x\u2026`, or a base58 mint)."
          },
          "tokenDecimals": {
            "type": "integer",
            "minimum": 0,
            "description": "The token's own decimals (for converting `fromTotal`)."
          },
          "price": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9]+$",
            "description": "The feed's latest answer, an integer string."
          },
          "priceDecimals": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Scale of `price` (8 for every feed today)."
          },
          "updatedAt": {
            "type": [
              "integer",
              "null"
            ],
            "description": "When the feed last updated (epoch s). `POST /quote` refuses a feed older than 26 hours with `NO_FROM_PRICE`."
          },
          "observedAt": {
            "type": [
              "integer",
              "null"
            ],
            "description": "When the backend read it (epoch s; refreshed about every minute)."
          },
          "available": {
            "type": "boolean",
            "description": "`false` only for a token that has never been priced. A stale feed keeps `true` and its last price."
          }
        },
        "additionalProperties": false
      },
      "PriceAt": {
        "type": "object",
        "description": "The latest feed round at or before the requested `ts`.",
        "required": [
          "chainId",
          "address",
          "price",
          "priceDecimals",
          "updatedAt",
          "observedAt"
        ],
        "properties": {
          "chainId": {
            "type": "integer",
            "description": "FROM chain id, **as a number**."
          },
          "address": {
            "type": "string"
          },
          "price": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Base-unit integer string."
          },
          "priceDecimals": {
            "type": "integer"
          },
          "updatedAt": {
            "type": "integer",
            "description": "Feed round time (epoch s), at or before `ts`."
          },
          "observedAt": {
            "type": "integer",
            "description": "When the backend recorded the round (epoch s)."
          }
        },
        "additionalProperties": false
      },
      "QuoteRequest": {
        "type": "object",
        "required": [
          "chainId",
          "fromToken",
          "sender",
          "toChainId",
          "toToken",
          "recipients",
          "toAmounts"
        ],
        "properties": {
          "chainId": {
            "type": "integer",
            "minimum": 1,
            "description": "FROM (mainnet) chain id, **as a number**. An enabled FROM chain \u2014 an EVM id, or `1399811149` (Solana) / `1667471768` (TON)."
          },
          "fromToken": {
            "type": "string",
            "minLength": 1,
            "description": "FROM token as listed in `/chains`: an `0x\u2026` ERC-20 address, a base58 SPL mint, or `0x0\u20260` for the native coin."
          },
          "sender": {
            "type": "string",
            "minLength": 1,
            "description": "The wallet that will pay. Bound into the signature \u2014 the payer **must** match. EVM `0x\u2026` (any case), Solana base58, TON any form."
          },
          "toChainId": {
            "type": "integer",
            "minimum": 1,
            "description": "TO (testnet) chain id, **as a number**."
          },
          "toToken": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "TO token as listed in `/chains`; `0x0\u20260` = native (also on Solana / TON / Bitcoin testnets)."
          },
          "recipients": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10000,
            "items": {
              "type": "string",
              "minLength": 1
            },
            "description": "Recipient addresses on the TO chain, delivered exactly as sent (EVM in any case). 1 = single swap, >1 = multisend."
          },
          "toAmounts": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10000,
            "items": {
              "type": "string",
              "pattern": "^[0-9]+$"
            },
            "description": "Base-unit integer strings of the TO token, one per recipient. No decimals, hex or exponents. Each must sit on the token precision grid and at or above its per-recipient minimum."
          }
        },
        "additionalProperties": false
      },
      "QuoteResponse": {
        "type": "object",
        "description": "A signed quote, valid **180 seconds** (`expiry` = `issuedAt` + 180 = `deadline`). Pay **exactly** `fromTotal` from `sender` to `vault` before `deadline`: on EVM via the depository `pay` call, on Solana / TON as a transfer to the treasury with `quoteId` as the memo / comment.",
        "required": [
          "quoteId",
          "signer",
          "vault",
          "issuedAt",
          "expiry",
          "deadline",
          "fromToken",
          "fromTotal",
          "fromAmounts",
          "toAmounts",
          "usdTotal",
          "signature"
        ],
        "properties": {
          "quoteId": {
            "type": "string",
            "pattern": "^0x[0-9a-f]{64}$",
            "description": "32 random bytes \u2014 the only quote reference carried on-chain."
          },
          "signer": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$",
            "description": "Address that produced the EIP-712 signature."
          },
          "vault": {
            "type": "string",
            "description": "Pay target = the FROM chain's `contracts.depository`, canonical form (EVM lowercased)."
          },
          "issuedAt": {
            "type": "integer",
            "description": "Epoch seconds."
          },
          "expiry": {
            "type": "integer",
            "description": "End of the validity window (epoch s): `issuedAt` + 180."
          },
          "deadline": {
            "type": "integer",
            "description": "Passed to the `pay` call; equals `expiry`. A Solana / TON payment landing after it is not honoured."
          },
          "fromToken": {
            "type": "string",
            "description": "The FROM token in canonical form."
          },
          "fromTotal": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "**Total FROM to pay**, base units \u2014 pay exactly this. Equals the sum of `fromAmounts`."
          },
          "fromAmounts": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[0-9]+$",
              "description": "Base-unit integer string."
            },
            "description": "Per-recipient FROM breakdown, base units."
          },
          "toAmounts": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[0-9]+$",
              "description": "Base-unit integer string."
            },
            "description": "Echo of the requested TO amounts, delivered exactly regardless of later price moves."
          },
          "usdTotal": {
            "type": "string",
            "pattern": "^[0-9]+\\.[0-9]{2}$",
            "description": "Human USD total, 2 decimals."
          },
          "signature": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{130}$",
            "description": "65-byte EIP-712 signature."
          }
        },
        "additionalProperties": false
      },
      "OrderSide": {
        "type": "object",
        "description": "One side of an order. `from` = the payment (payer, FROM tx); `to` = the payout (recipient, payout tx).",
        "required": [
          "chainId",
          "displayName",
          "symbol",
          "amount",
          "address",
          "tokenAddress",
          "txHash",
          "ts",
          "explorer",
          "family",
          "cluster"
        ],
        "properties": {
          "chainId": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Chain id, **as a string**."
          },
          "displayName": {
            "type": "string"
          },
          "symbol": {
            "type": "string"
          },
          "amount": {
            "type": "string",
            "pattern": "^[0-9]+(\\.[0-9]+)?$",
            "description": "Human-readable decimal string (padded to 8 places)."
          },
          "address": {
            "type": "string",
            "description": "`from`: the payer (EVM checksummed `0x\u2026`, Solana base58, TON `UQ\u2026`). `to`: the recipient."
          },
          "tokenAddress": {
            "type": [
              "string",
              "null"
            ]
          },
          "txHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "`from`: the payment tx (EVM hash, Solana signature, TON treasury-side hash as 64 hex). `to`: the payout tx, `null` until dispensed."
          },
          "ts": {
            "type": [
              "integer",
              "null"
            ],
            "description": "`from`: payment time; `to`: completion time (epoch s)."
          },
          "explorer": {
            "type": [
              "string",
              "null"
            ],
            "description": "Block-explorer base URL, for building links."
          },
          "family": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "solana",
              "ton",
              "bitcoin",
              null
            ],
            "description": "`null` = EVM."
          },
          "cluster": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "mainnet-beta",
              "testnet",
              "devnet",
              null
            ],
            "description": "Solana only."
          }
        },
        "additionalProperties": false
      },
      "Order": {
        "type": "object",
        "description": "One order = one recipient of a payment.",
        "required": [
          "status",
          "id",
          "from",
          "to"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "completed",
              "failed",
              "pending"
            ],
            "description": "`processing` \u2192 `completed` | `failed`. `pending` is reserved and not produced today."
          },
          "id": {
            "type": "string",
            "description": "The order id \u2014 **case-sensitive**. Use with `/order?id=` and `/orders/status`."
          },
          "from": {
            "$ref": "#/components/schemas/OrderSide"
          },
          "to": {
            "$ref": "#/components/schemas/OrderSide"
          }
        },
        "additionalProperties": false
      },
      "RefundReason": {
        "type": "string",
        "enum": [
          "unknown_quote",
          "unattributed",
          "ambiguous_memo",
          "expired",
          "bad_signature",
          "failed_order"
        ]
      },
      "PaymentSide": {
        "type": "object",
        "description": "The FROM payment as seen by a refund or a history batch row.",
        "required": [
          "chainId",
          "displayName",
          "symbol",
          "amount",
          "address",
          "txHash",
          "ts"
        ],
        "properties": {
          "chainId": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Chain id as a **string**."
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ]
          },
          "symbol": {
            "type": [
              "string",
              "null"
            ]
          },
          "amount": {
            "type": "string",
            "pattern": "^[0-9]+(\\.[0-9]+)?$",
            "description": "Human-readable decimal string (not base units)."
          },
          "address": {
            "type": "string",
            "description": "The payer."
          },
          "txHash": {
            "type": "string",
            "description": "The original payment tx."
          },
          "ts": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "Refund": {
        "type": "object",
        "description": "Some or all of a payment is being returned: a payment that could not become an order, or the failed slices of a partial batch.",
        "required": [
          "status",
          "reason",
          "from",
          "refundTxHash",
          "refundedAt"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "refund_pending",
              "refunded"
            ],
            "description": "`refund_pending` (recorded, not yet returned) or `refunded` (return tx sent)."
          },
          "reason": {
            "$ref": "#/components/schemas/RefundReason"
          },
          "from": {
            "$ref": "#/components/schemas/PaymentSide",
            "description": "The returned amount + payer + original payment tx."
          },
          "refundTxHash": {
            "type": [
              "string",
              "null"
            ]
          },
          "refundedAt": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "StatusCounts": {
        "type": "object",
        "required": [
          "completed",
          "processing",
          "failed"
        ],
        "properties": {
          "completed": {
            "type": "integer",
            "minimum": 0
          },
          "processing": {
            "type": "integer",
            "minimum": 0
          },
          "failed": {
            "type": "integer",
            "minimum": 0
          }
        },
        "additionalProperties": false
      },
      "BatchRow": {
        "description": "A history row with `view=batches`: one payment (multisend collapsed), or a payment that could not become an order (`isRefund: true`).",
        "oneOf": [
          {
            "type": "object",
            "title": "Payment batch",
            "required": [
              "status",
              "statusCounts",
              "refund",
              "recipients",
              "id",
              "from",
              "to"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "processing",
                  "completed",
                  "failed",
                  "partial"
                ],
                "description": "Aggregate: `partial` = some paid, some failed."
              },
              "statusCounts": {
                "$ref": "#/components/schemas/StatusCounts"
              },
              "refund": {
                "oneOf": [
                  {
                    "type": "object",
                    "required": [
                      "status",
                      "reason"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "status": {
                        "type": "string",
                        "enum": [
                          "refund_pending",
                          "refunded"
                        ]
                      },
                      "reason": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/RefundReason"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    }
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "recipients": {
                "type": "integer",
                "minimum": 1
              },
              "id": {
                "type": "string",
                "description": "The batch's first order id."
              },
              "from": {
                "$ref": "#/components/schemas/PaymentSide",
                "description": "Amount padded to 18 places."
              },
              "to": {
                "type": "object",
                "required": [
                  "chainId",
                  "displayName",
                  "symbol",
                  "amount",
                  "address",
                  "txHash",
                  "ts"
                ],
                "properties": {
                  "chainId": {
                    "type": "string",
                    "pattern": "^[0-9]+$",
                    "description": "Chain id as a **string**."
                  },
                  "displayName": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "symbol": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "amount": {
                    "type": "string",
                    "pattern": "^[0-9]+(\\.[0-9]+)?$",
                    "description": "Human-readable decimal string (not base units)."
                  },
                  "address": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Single swaps only; `null` for a multisend."
                  },
                  "txHash": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "`null` while anything is still processing."
                  },
                  "ts": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  }
                },
                "additionalProperties": false
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "title": "Refund-only row",
            "required": [
              "status",
              "isRefund",
              "reason",
              "recipients",
              "id",
              "from",
              "to"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "refund_pending",
                  "refunded"
                ]
              },
              "isRefund": {
                "type": "boolean",
                "const": true
              },
              "reason": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/RefundReason"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "recipients": {
                "type": "integer",
                "const": 0
              },
              "id": {
                "type": "null",
                "description": "No order id \u2014 look the payment up by its FROM tx hash."
              },
              "from": {
                "$ref": "#/components/schemas/PaymentSide"
              },
              "to": {
                "type": "null"
              }
            },
            "additionalProperties": false
          }
        ]
      },
      "Pagination": {
        "type": "object",
        "description": "Keyset pagination of `GET /orders`. Cursors are opaque.",
        "required": [
          "has_next_page",
          "next_cursor",
          "latest_item_cursor"
        ],
        "properties": {
          "has_next_page": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next (older) page."
          },
          "latest_item_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `since_cursor` to poll for newer rows."
          }
        },
        "additionalProperties": false
      },
      "RecipientsPagination": {
        "type": "object",
        "description": "Keyset pagination of a paginated `GET /order`.",
        "required": [
          "has_next_page",
          "next_cursor"
        ],
        "properties": {
          "has_next_page": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page."
          }
        },
        "additionalProperties": false
      },
      "PaymentOrders": {
        "type": "object",
        "description": "`GET /order?from_tx_hash=` \u2014 every order of one payment.",
        "required": [
          "data",
          "refund"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Order"
            },
            "description": "One order per recipient; empty until the payment is indexed \u2014 keep polling."
          },
          "refund": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Refund"
              },
              {
                "type": "null"
              }
            ],
            "description": "`null` unless some or all of the payment is being returned."
          }
        },
        "additionalProperties": false
      },
      "BatchSummary": {
        "type": "object",
        "required": [
          "recipients",
          "statusCounts",
          "fromTotal",
          "toTotal",
          "completedTs",
          "lastPayout"
        ],
        "properties": {
          "recipients": {
            "type": "integer",
            "minimum": 0
          },
          "statusCounts": {
            "$ref": "#/components/schemas/StatusCounts"
          },
          "fromTotal": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9]+(\\.[0-9]+)?$",
            "description": "Human decimal; `null` when nothing is indexed."
          },
          "toTotal": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9]+(\\.[0-9]+)?$",
            "description": "Human decimal; `null` when nothing is indexed."
          },
          "completedTs": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Set once nothing is left `processing`."
          },
          "lastPayout": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "txHash",
                  "ts"
                ],
                "additionalProperties": false,
                "properties": {
                  "txHash": {
                    "type": "string"
                  },
                  "ts": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "The last payout; needs at least one `completed` order (`null` for an all-failed batch)."
          }
        },
        "additionalProperties": false
      },
      "RecipientsPage": {
        "type": "object",
        "description": "`GET /order?from_tx_hash=\u2026&paginate=1` \u2014 aggregate summary plus a keyset page of recipients.",
        "required": [
          "summary",
          "data",
          "pagination",
          "refund"
        ],
        "properties": {
          "summary": {
            "$ref": "#/components/schemas/BatchSummary"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Order"
            },
            "description": "Order objects, in recipient order."
          },
          "pagination": {
            "$ref": "#/components/schemas/RecipientsPagination"
          },
          "refund": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Refund"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "additionalProperties": false
      },
      "OrderHistory": {
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Order"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        },
        "additionalProperties": false
      },
      "BatchHistory": {
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BatchRow"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        },
        "additionalProperties": false
      },
      "OrderStatus": {
        "type": "object",
        "required": [
          "status",
          "id",
          "to"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "completed",
              "failed",
              "pending"
            ]
          },
          "id": {
            "type": "string"
          },
          "to": {
            "type": "object",
            "required": [
              "txHash",
              "ts"
            ],
            "additionalProperties": false,
            "properties": {
              "txHash": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "ts": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          }
        },
        "additionalProperties": false
      },
      "ChainLoad": {
        "type": "object",
        "required": [
          "depth",
          "rate",
          "measured",
          "stalled",
          "minEtaSec"
        ],
        "properties": {
          "depth": {
            "type": "integer",
            "minimum": 0,
            "description": "Recipients still awaiting dispense on this TO chain."
          },
          "rate": {
            "type": "number",
            "description": "Drain rate, recipients/sec."
          },
          "measured": {
            "type": "boolean",
            "description": "`true` = measured from recent completions; `false` = static fallback."
          },
          "stalled": {
            "type": "boolean"
          },
          "minEtaSec": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "OrdersLoad": {
        "type": "object",
        "description": "Dispense-queue depth and drain rate. Rough ETA \u2248 `max(minEtaSec, depth / rate)`.",
        "required": [
          "updatedAt",
          "ratePerSec",
          "minEtaSec",
          "chains"
        ],
        "properties": {
          "updatedAt": {
            "type": "integer",
            "description": "Epoch seconds (0 right after a restart)."
          },
          "ratePerSec": {
            "type": "number"
          },
          "minEtaSec": {
            "type": "number"
          },
          "chains": {
            "type": "object",
            "description": "Keyed by TO chain id (string). An idle chain is absent.",
            "propertyNames": {
              "type": "string",
              "pattern": "^[0-9]+$"
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/ChainLoad"
            }
          },
          "ref": {
            "description": "Only when `ref` was passed: your position on its TO chain, or `null` if the ref is not indexed yet.",
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "chainId",
                  "depth",
                  "statusCounts"
                ],
                "additionalProperties": false,
                "properties": {
                  "chainId": {
                    "type": "string",
                    "pattern": "^[0-9]+$",
                    "description": "Chain id as a **string**."
                  },
                  "depth": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Recipients ahead of (and including) your batch; only shrinks."
                  },
                  "statusCounts": {
                    "$ref": "#/components/schemas/StatusCounts"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "additionalProperties": false
      },
      "Error": {
        "type": "object",
        "description": "Plain error: a human-readable message, no code.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "QuoteError400": {
        "type": "object",
        "description": "`POST /quote` refused the request (HTTP 400).",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "BAD_FROM_CHAIN",
              "BAD_TO_CHAIN",
              "BAD_ADDRESS",
              "BAD_FROM_TOKEN",
              "BAD_TO_TOKEN",
              "BAD_ARRAYS",
              "EMPTY",
              "LENGTH_MISMATCH",
              "TOO_MANY",
              "BAD_RECIPIENT",
              "BAD_TO_AMOUNT",
              "TO_TOO_SMALL",
              "TO_TOO_PRECISE",
              "FROM_ROUNDS_TO_ZERO",
              "BELOW_SOL_RENT_MIN",
              "BELOW_MIN_TOTAL",
              "NO_LIQUIDITY"
            ]
          }
        },
        "additionalProperties": false
      },
      "QuoteError500": {
        "type": "object",
        "description": "`POST /quote`: the FROM chain has no pay target configured (HTTP 500).",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "NO_VAULT"
            ]
          }
        },
        "additionalProperties": false
      },
      "QuoteError503": {
        "type": "object",
        "description": "`POST /quote`: a price is missing or stale, or quoting is paused (HTTP 503). Retry later.",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "NO_FROM_PRICE",
              "NO_TO_PRICE",
              "SIGNING_DISABLED"
            ]
          }
        },
        "additionalProperties": false
      },
      "RateLimited": {
        "type": "object",
        "description": "Per-minute allowance exhausted (IP or sender). Comes with a `Retry-After` header.",
        "required": [
          "error",
          "retryAfterSec"
        ],
        "properties": {
          "error": {
            "type": "string",
            "const": "RATE_LIMITED"
          },
          "retryAfterSec": {
            "type": "integer",
            "minimum": 1,
            "description": "Whole seconds until the next request passes."
          }
        },
        "additionalProperties": false
      },
      "WarmingUp": {
        "type": "object",
        "description": "The backend just restarted and is loading prices / balances (about a minute). Not a rate limit; comes with `Retry-After`.",
        "required": [
          "error",
          "retryAfterSec",
          "waitingFor"
        ],
        "properties": {
          "error": {
            "type": "string",
            "const": "WARMING_UP"
          },
          "retryAfterSec": {
            "type": "integer",
            "minimum": 1
          },
          "waitingFor": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "balances",
                "prices"
              ]
            }
          }
        },
        "additionalProperties": false
      }
    },
    "parameters": {
      "AddressList": {
        "name": "address",
        "in": "query",
        "required": true,
        "description": "The payer identity, or up to 10 identities comma-separated (`0x\u2026`, Solana base58, TON address). EVM addresses match case-insensitively; Solana and TON must be exact (`UQ\u2026` form for TON).",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "X-RateLimit-Scope": {
        "description": "`anonymous` for public callers.",
        "schema": {
          "type": "string"
        }
      },
      "X-RateLimit-Limit": {
        "description": "Size of the last bucket checked, per minute: 600 (per IP) on most routes; on `POST /quote` with a `sender`, 30 (per sender).",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in that bucket.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "Retry-After": {
        "description": "Whole seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "responses": {
      "RateLimited": {
        "description": "Over the per-minute allowance. Sleep `Retry-After` seconds, then retry. (The edge also answers `429` with an empty, non-JSON body and no `Retry-After` on floods \u2014 back off a second or two.)",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimited"
            },
            "examples": {
              "limited": {
                "value": {
                  "error": "RATE_LIMITED",
                  "retryAfterSec": 6
                }
              }
            }
          }
        }
      },
      "WarmingUp": {
        "description": "Warming up after a restart \u2014 retry after `Retry-After`.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/WarmingUp"
            },
            "examples": {
              "warming": {
                "value": {
                  "error": "WARMING_UP",
                  "retryAfterSec": 5,
                  "waitingFor": [
                    "prices"
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
