{
  "openapi": "3.1.0",
  "info": {
    "title": "Flowroute Swap API \u2014 Robinhood beta",
    "version": "0.6.0",
    "description": "Exact-input quotes and unsigned transaction preparation on Robinhood Chain. Access mode is reported by GET /capabilities. No gas-adjusted ranking, signing or broadcasting. Preparation checks a public account and returns only the next required wallet step. Bearer keys expire after 7 days. Responses may have partial coverage; empty routes do not prove absence of liquidity. V4 discovery uses the bounded catalog snapshot. Direct V2/V3 and no-hook V4 search plus the unfiltered KyberSwap route; optional source-filtered Kyber comparisons from the website are not included. Paid API: fresh quote 5 FLW Credits, cache hit 1, preparation 5. Errors and empty quotes are free. Each successful approval preparation is a separate paid operation. Reuse the same idempotency key when retrying an uncertain request."
  },
  "servers": [
    {
      "url": "https://www.flowroute.cloud",
      "description": "Robinhood beta"
    },
    {
      "url": "http://127.0.0.1:5372",
      "description": "Local development"
    }
  ],
  "security": [
    {
      "BearerKey": []
    }
  ],
  "paths": {
    "/api/v1/quote": {
      "post": {
        "operationId": "quoteExactInput",
        "summary": "Compare exact-input routes, ordered by token output",
        "description": "6 requests/minute and 200/day shared per wallet and per key; global 30/minute and 600/day. Cached requests count. Token metadata, quoting and preparation share these quotas. One concurrent operation per wallet, two globally across instances. Up to 600 RPC reads/operation, reserved in batches of 16. UTC daily RPC budgets: 10,000/wallet and key, and 20,000/global. Invalid bodies and unauthorized requests do not consume request quota. Admitted requests, including busy/source failures, consume quota. Keys, counters, quote cache and preparation grants are shared in durable server storage. Same-origin browser access only; server clients need no Origin header. Do not put keys in URLs.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "example": {
                "chainId": 4663,
                "tokenIn": "ETH",
                "tokenOut": "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
                "amountIn": "10000000000000000",
                "maxRoutes": 5
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quotes and source coverage (routes can be empty)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, pair or amount",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Browser origin is not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "API disabled on this host or unknown endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Use POST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 1024 bytes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Use application/json",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Token metadata unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request quota, concurrency or work budget exceeded; see Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "503": {
            "description": "Sources or service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "Quote timed out or request cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits or maximum charge below the operation cost. No work is charged."
          }
        },
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "UUID v4; reuse for retries of the same operation."
          },
          {
            "in": "header",
            "name": "X-FLW-Max-Credits",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 30
            },
            "description": "Explicit maximum charge. Use 5 to permit a fresh quote or preparation; 1 permits cache-only quotes."
          }
        ]
      }
    },
    "/api/v1/prepare": {
      "post": {
        "operationId": "prepareWalletStep",
        "summary": "Refresh a selected route and prepare the next unsigned wallet step",
        "description": "Only quoteId/routeId, account and slippage are accepted. Routes and transaction targets are retained and validated server-side. Requests share quote quotas and concurrency. Preparations are never cached. Recipient is pinned to account. Direct paths are re-quoted; Kyber can reroute. Minimum is at least the original quote output after requested slippage, including in calldata. After an approval confirms, prepare again (or quote again if expired). The client must verify the current wallet account, chain, freshness and returned amounts before signing. There is no signing or send endpoint.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrepareRequest"
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Invalid body, pair or amount",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Browser origin is not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "API disabled on this host or unknown endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Use POST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 1024 bytes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Use application/json",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Insufficient token/ETH balance, rejected simulation, or amount too small.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request quota, concurrency or work budget exceeded; see Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "503": {
            "description": "Route could not be safely prepared. No transaction is returned on errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "Quote timed out or request cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "One unsigned step with successful simulation of that step only. An approval response does not mean the subsequent swap has been simulated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrepareResponse"
                }
              }
            }
          },
          "409": {
            "description": "Quote/route unavailable to this key, expired preparation, or output fell below the original floor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits or maximum charge below the operation cost. No work is charged."
          }
        },
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "UUID v4; reuse for retries of the same operation."
          },
          {
            "in": "header",
            "name": "X-FLW-Max-Credits",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 30
            },
            "description": "Explicit maximum charge. Use 5 to permit a fresh quote or preparation; 1 permits cache-only quotes."
          }
        ]
      }
    },
    "/api/v1/capabilities": {
      "get": {
        "operationId": "getCapabilities",
        "summary": "Supported chain, endpoints, access mode and beta limits",
        "security": [],
        "description": "Static discovery; does not make RPC reads or consume authenticated quote quota. No query parameters.",
        "responses": {
          "200": {
            "description": "Supported chain, endpoints, access mode and beta limits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chains": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "access": {
                      "type": "string",
                      "enum": [
                        "invite-only",
                        "public-beta"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tokens": {
      "get": {
        "operationId": "getTokens",
        "summary": "Curated Robinhood token starting list",
        "security": [],
        "description": "Static discovery; does not make RPC reads or consume authenticated quote quota. No query parameters.",
        "responses": {
          "200": {
            "description": "Curated Robinhood token starting list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chainId": {
                      "const": 4663
                    },
                    "complete": {
                      "const": false
                    },
                    "tokens": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CatalogToken"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/token": {
      "post": {
        "operationId": "resolveToken",
        "summary": "Resolve current token decimals before converting an input amount",
        "description": "Robinhood only. Shares quote/prepare request, concurrency and RPC budgets. No transaction is built or sent.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenRequest"
              },
              "example": {
                "chainId": 4663,
                "token": "ETH"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Current token decimals and metadata from a pinned block",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chainId": {
                      "const": 4663
                    },
                    "token": {
                      "$ref": "#/components/schemas/CatalogToken"
                    },
                    "snapshotBlock": {
                      "type": "string"
                    },
                    "work": {
                      "type": "object"
                    },
                    "usage": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, pair or amount",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Browser origin is not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "API disabled on this host or unknown endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Use POST",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 1024 bytes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Use application/json",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Token metadata unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request quota, concurrency or work budget exceeded; see Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "503": {
            "description": "Sources or service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "Quote timed out or request cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Read this key and its wallet account usage",
        "description": "Requires an active key. Does not consume a quote request; shared ingress protection still applies. No query parameters.",
        "responses": {
          "200": {
            "description": "Key/account counters with limits and reset timestamps",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "usage": {
                      "type": "object"
                    },
                    "limits": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "A server-issued fr_dev_ key; keep it secret."
      }
    },
    "schemas": {
      "QuoteRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "chainId",
          "tokenIn",
          "tokenOut",
          "amountIn"
        ],
        "properties": {
          "chainId": {
            "const": 4663
          },
          "tokenIn": {
            "type": "string",
            "description": "ETH or an ERC-20 contract. ETH/WETH wrapping is not supported."
          },
          "tokenOut": {
            "type": "string",
            "description": "ETH or an ERC-20 contract."
          },
          "amountIn": {
            "type": "string",
            "pattern": "^[1-9][0-9]{0,38}$",
            "description": "Positive integer base units, at most 2^128-1. No exponents or leading zeroes."
          },
          "maxRoutes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "default": 5,
            "description": "Response limit only. All returned candidates are ranked first."
          }
        }
      },
      "QuoteResponse": {
        "type": "object",
        "required": [
          "requestId",
          "quoteId",
          "chainId",
          "routes",
          "sources",
          "ranking",
          "gasIncluded",
          "executionAvailable",
          "coverage",
          "usage"
        ],
        "properties": {
          "requestId": {
            "type": "string"
          },
          "quoteId": {
            "type": "string"
          },
          "chainId": {
            "const": 4663
          },
          "input": {
            "$ref": "#/components/schemas/Asset"
          },
          "output": {
            "$ref": "#/components/schemas/Asset"
          },
          "amountIn": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Exact base-unit amount; never parse as a floating-point number."
          },
          "amountInFormatted": {
            "type": "string"
          },
          "snapshotBlock": {
            "type": "string"
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "completedAt": {
            "type": "string",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "End of response reuse window, not an execution guarantee."
          },
          "ranking": {
            "const": "amountOut"
          },
          "gasIncluded": {
            "const": false
          },
          "executionAvailable": {
            "const": false,
            "description": "The API never executes or broadcasts transactions. preparationAvailable is separate."
          },
          "totalRoutes": {
            "type": "integer"
          },
          "returnedRoutes": {
            "type": "integer"
          },
          "cached": {
            "type": "boolean"
          },
          "routes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Route"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "status": {
                  "enum": [
                    "quoted",
                    "partial",
                    "unavailable",
                    "no-route"
                  ]
                },
                "quotes": {
                  "type": "integer"
                }
              }
            }
          },
          "coverage": {
            "type": "object",
            "properties": {
              "partial": {
                "type": "boolean"
              },
              "searchTruncated": {
                "type": "boolean"
              },
              "workLimited": {
                "type": "boolean"
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "work": {
            "type": "object",
            "properties": {
              "rpcCalls": {
                "type": "integer"
              }
            }
          },
          "usage": {
            "type": "object",
            "properties": {
              "minuteRemaining": {
                "type": "integer"
              },
              "dayRemaining": {
                "type": "integer"
              },
              "resetAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "preparationAvailable": {
            "type": "boolean"
          },
          "prepareUntil": {
            "type": "string",
            "format": "date-time",
            "description": "Selection retained for up to 2 minutes, only for the key and routes that received this response. May be evicted earlier on capacity or restart. Prepare always refreshes price."
          }
        }
      },
      "Asset": {
        "type": "object",
        "required": [
          "address",
          "native",
          "symbol",
          "decimals"
        ],
        "properties": {
          "address": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "native": {
            "type": "boolean"
          },
          "symbol": {
            "type": "string"
          },
          "decimals": {
            "type": "integer",
            "minimum": 0,
            "maximum": 36
          }
        }
      },
      "Route": {
        "type": "object",
        "required": [
          "id",
          "source",
          "provider",
          "protocol",
          "amountOut",
          "amountOutFormatted",
          "snapshotBlock",
          "splitOrMerged",
          "paths",
          "segments"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "protocol": {
            "enum": [
              "v2",
              "v3",
              "v4",
              "aggregator"
            ]
          },
          "amountOut": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Exact base-unit amount; never parse as a floating-point number."
          },
          "amountOutFormatted": {
            "type": "string"
          },
          "snapshotBlock": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pinned block for direct quotes; null for aggregator provider snapshots."
          },
          "observedAt": {
            "type": "string",
            "format": "date-time"
          },
          "splitOrMerged": {
            "type": "boolean"
          },
          "paths": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object",
              "properties": {
                "amountIn": {
                  "type": "string",
                  "pattern": "^[0-9]+$",
                  "description": "Exact base-unit amount; never parse as a floating-point number."
                },
                "hops": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Hop"
                  }
                }
              }
            }
          },
          "segments": {
            "type": [
              "array",
              "null"
            ],
            "description": "Aggregator graph groups. These are not necessarily independent full input-to-output paths. Input allocations are not supplied.",
            "items": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Hop"
              }
            }
          }
        }
      },
      "Hop": {
        "type": "object",
        "properties": {
          "tokenIn": {
            "type": "string"
          },
          "tokenOut": {
            "type": "string"
          },
          "poolId": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "feePips": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Pool fee in millionths, where known. May exclude hook/protocol charges."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "requestId": {
            "type": "string"
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "PrepareRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "chainId",
          "quoteId",
          "routeId",
          "account",
          "slippageBps"
        ],
        "properties": {
          "chainId": {
            "const": 4663
          },
          "quoteId": {
            "type": "string",
            "format": "uuid"
          },
          "routeId": {
            "type": "string",
            "pattern": "^route-[1-9][0-9]?$"
          },
          "account": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$",
            "description": "Public sender address, also the recipient. Reserved zero/1/2/native alias are rejected."
          },
          "slippageBps": {
            "type": "integer",
            "minimum": 10,
            "maximum": 200,
            "description": "Explicit 0.1% to 2%. Original route output at this slippage sets a floor that preparation never lowers."
          }
        }
      },
      "UnsignedTransaction": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "chainId",
          "from",
          "to",
          "data",
          "value",
          "gas"
        ],
        "properties": {
          "chainId": {
            "const": "0x1237"
          },
          "from": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "to": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "data": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]+$"
          },
          "value": {
            "type": "string",
            "pattern": "^0x[0-9a-f]+$"
          },
          "gas": {
            "type": "string",
            "pattern": "^0x[0-9a-f]+$"
          }
        }
      },
      "PrepareResponse": {
        "type": "object",
        "required": [
          "requestId",
          "quoteId",
          "routeId",
          "chainId",
          "account",
          "recipient",
          "stage",
          "status",
          "amountIn",
          "amountOut",
          "minimumAmountOut",
          "originalMinimumAmountOut",
          "transaction",
          "simulation",
          "preparedAt",
          "expiresAt",
          "gasEstimate",
          "warnings",
          "usage"
        ],
        "properties": {
          "quoteId": {
            "type": "string"
          },
          "routeId": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          },
          "chainId": {
            "const": 4663
          },
          "account": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "recipient": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "input": {
            "$ref": "#/components/schemas/Asset"
          },
          "output": {
            "$ref": "#/components/schemas/Asset"
          },
          "amountIn": {
            "type": "string",
            "pattern": "^[0-9]+$"
          },
          "originalAmountOut": {
            "type": "string",
            "pattern": "^[0-9]+$"
          },
          "amountOut": {
            "type": "string",
            "pattern": "^[0-9]+$"
          },
          "amountOutFormatted": {
            "type": "string"
          },
          "minimumAmountOut": {
            "type": "string",
            "pattern": "^[0-9]+$"
          },
          "minimumAmountOutFormatted": {
            "type": "string"
          },
          "originalMinimumAmountOut": {
            "type": "string",
            "pattern": "^[0-9]+$"
          },
          "slippageBps": {
            "type": "integer"
          },
          "stage": {
            "enum": [
              "reset",
              "approve",
              "permit",
              "swap"
            ]
          },
          "status": {
            "enum": [
              "approval-required",
              "swap-ready"
            ]
          },
          "routePolicy": {
            "enum": [
              "pinned-path",
              "provider-refreshed"
            ],
            "description": "Direct path and V4 allocations stay fixed. KyberSwap obtains a fresh provider route."
          },
          "routeChanged": {
            "type": "boolean"
          },
          "route": {
            "$ref": "#/components/schemas/Route"
          },
          "preparedAt": {
            "type": "string",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Refresh after this time (at most 60 seconds), even if the on-chain deadline is later. Never reuse a prepared approval after it has confirmed."
          },
          "swapDeadline": {
            "type": "integer"
          },
          "swapDeadlineEnforcement": {
            "enum": [
              "router",
              "client-required"
            ],
            "description": "Kyber V2 outer calldata does not expose a verified on-chain deadline; the client must honor expiresAt. Approval transactions have no automatic expiry (except expiring Permit2 allowance)."
          },
          "transaction": {
            "$ref": "#/components/schemas/UnsignedTransaction"
          },
          "approval": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "token": {
                "type": "string",
                "pattern": "^0x[a-fA-F0-9]{40}$"
              },
              "spender": {
                "type": "string",
                "pattern": "^0x[a-fA-F0-9]{40}$"
              },
              "amount": {
                "type": "string",
                "pattern": "^[0-9]+$"
              },
              "expiration": {
                "type": "integer"
              }
            },
            "description": "Exact amount for approve/permit, zero for reset; null for swap."
          },
          "simulation": {
            "type": "object",
            "properties": {
              "status": {
                "const": "passed"
              },
              "scope": {
                "enum": [
                  "swap",
                  "reset",
                  "approve",
                  "permit"
                ]
              },
              "block": {
                "const": "pending"
              }
            }
          },
          "gasEstimate": {
            "type": "object",
            "properties": {
              "gasLimit": {
                "type": "string",
                "pattern": "^[0-9]+$"
              },
              "gasPriceWei": {
                "type": "string",
                "pattern": "^[0-9]+$"
              },
              "feeWei": {
                "type": "string",
                "pattern": "^[0-9]+$"
              },
              "scope": {
                "const": "returned-step-only"
              },
              "method": {
                "const": "buffered RPC estimate"
              }
            }
          },
          "nextAction": {
            "type": "string"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "work": {
            "type": "object",
            "properties": {
              "rpcCalls": {
                "type": "integer"
              }
            }
          },
          "usage": {
            "type": "object",
            "properties": {
              "minuteRemaining": {
                "type": "integer"
              },
              "dayRemaining": {
                "type": "integer"
              },
              "resetAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "CatalogToken": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "native": {
            "type": "boolean"
          },
          "symbol": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "decimals": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 36
          }
        }
      },
      "TokenRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "chainId",
          "token"
        ],
        "properties": {
          "chainId": {
            "type": "integer",
            "const": 4663
          },
          "token": {
            "type": "string",
            "description": "ETH for native Ether; contract address for ERC20 tokens."
          }
        }
      }
    }
  }
}
