{
  "openapi": "3.1.0",
  "info": {
    "title": "Zecret Swap API",
    "version": "1.0.0",
    "summary": "Private swaps of any token, for agents.",
    "description": "Zecret Swap swaps any token privately, on 1Click, NEAR Intents' swap API, on the confidential route. It holds no funds and no keys.\n\nA swap in six steps: read the tokens, ask for a dry quote, ask for a live quote with your two addresses, **check the live quote**, send one transfer from your own wallet to the deposit address, then follow the swap with the status endpoint.\n\n**Check every live quote before you send.** The signature proves that 1Click produced the quote. The other checks prove that it is the swap you asked for. If any one fails, send nothing:\n\n1. The signature holds. It is ed25519, by ed25519:reYaWhvwu8Jzo3WUM3zhn6VrhuMEF4eADL17qtRVifc. See \"The signature\" for what it covers.\n2. The route is confidential: `route` is `basic` or `advanced`, and `quote.quoteRequest.confidentiality` says the same. Neither is signed, so each is a claim. Agreement between them is the most a client can check.\n3. The signed request is your swap: `dry` is false, `swapType` is EXACT_INPUT, `depositType` and `refundType` are ORIGIN_CHAIN, `recipientType` is DESTINATION_CHAIN, `originAsset` and `destinationAsset` are the two you chose and differ, `recipient` and `refundTo` are the two addresses you sent, and `amount` and `quote.quote.amountIn` are exactly the amount you asked for, in base units.\n4. The deposit address fits the origin chain, and nothing else is asked of you: there is no `depositMemo`, no `virtualChainRecipient`, no `virtualChainRefundRecipient` and no `customRecipientMsg`, and `depositMode` is absent or SIMPLE.\n5. `slippageTolerance` is the slippage you chose.\n6. The fees in `appFees` add up to 60 basis points or fewer, and not below zero. The signature does not cover them.\n7. `minAmountIn` is above zero and no more than `amountIn`. `minAmountOut` is above zero, no more than `amountOut`, and not below `amountOut` times (10000 minus the slippage) over 10000, less one unit. For a payout on Hyperliquid, 1Click takes the slippage before its payout fee: there, add `withdrawFee` (counted up to `amountOut`) to `amountOut` and to `minAmountOut` before you compare.\n8. `amountInFormatted` and `amountOutFormatted` are the base-unit amounts with each token's decimals applied.\n9. The signed dollar figures bound the swap: `amountInUsd` is no more than 50,000 dollars plus 2%, and `amountInUsd` less `amountOutUsd` less the payout network fee is no more than 3% of `amountInUsd`.\n10. `timestamp` is within 5 minutes of your clock.\n11. `quoteRequest.deadline` is at least 10 minutes, plus a margin for the chain, and at most 24 hours after `timestamp`. `quote.deadline` is no earlier than that, and within 14 days of `timestamp`.\n\n**A send** (`send: true`). Check the live quote as for a swap, with one change: `originAsset` and `destinationAsset` are the one token you chose. Also check that `amountIn` less `amountOut` less `withdrawFee` is no more than 3% of `amountIn`.\n\n**The signature.** The message is the base58 text of the SHA-256 of one JSON string. The JSON is a single flat object with its keys sorted, no spaces, and no field that is undefined. It holds these request fields: dry, swapType, slippageTolerance, originAsset, depositType, destinationAsset, amount, refundTo, refundType, recipient, recipientType, deadline, and, only when they are set and not zero, quoteWaitingTimeMs, referral, virtualChainRecipient, virtualChainRefundRecipient, customRecipientMsg. It holds these quote fields: amountIn, amountInFormatted, amountInUsd, minAmountIn, amountOut, amountOutFormatted, amountOutUsd, minAmountOut, and for a live quote, when they are set, depositAddress, depositMemo, deadline, timeWhenInactive, timeEstimate, refundFee, withdrawFee. It holds `timestamp`. Check the ed25519 signature, base58 after the `ed25519:` prefix, over the UTF-8 bytes of that base58 text, with the key above. The CLI's source is the reference: packages/cli, with the repo's src/lib/oneclick/signature.ts.\n\n**Fees.** Zecret Swap charges 0.4% of each swap. NEAR Intents charges its own fee, about 0.2%. Both are listed in `quoteRequest.appFees`. The fees and the price difference together are in the quote's signed dollar figures.\n**Limits.** Each swap is $5 to $50,000. Per IP address, each minute: 40 dry quotes, 8 live quotes, 90 status reads, 12 deposit notices, 30 health reads.\n**Screening.** NEAR Intents screens every swap and can delay or freeze funds.\n\nEvery POST takes JSON only. A browser on another site is refused. Scripts and servers are fine."
  },
  "servers": [
    {
      "url": "https://zecretswap.com"
    }
  ],
  "externalDocs": {
    "description": "The same rules, in plain text for agents.",
    "url": "https://zecretswap.com/llms.txt"
  },
  "tags": [
    {
      "name": "swap",
      "description": "Tokens and quotes."
    },
    {
      "name": "status",
      "description": "Follow a swap."
    },
    {
      "name": "health",
      "description": "Is 1Click answering?"
    }
  ],
  "paths": {
    "/api/swap/tokens": {
      "get": {
        "operationId": "listTokens",
        "tags": [
          "swap"
        ],
        "summary": "List the tokens you can swap",
        "description": "The registry: every token Zecret Swap offers, with its decimals and its price in US dollars at 1Click's list price. A token that is not here is refused by the quote endpoint. The answer is cached for a minute. Tokens on `hypercore` (USDC on Hyperliquid) are payout only. Use them as `destinationAsset`, never as `originAsset`. Their minimum is about $8.",
        "responses": {
          "200": {
            "description": "The tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokensReply"
                }
              }
            }
          },
          "502": {
            "description": "1Click did not answer, is unavailable, or refused this site's key. Try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/swap/quote": {
      "post": {
        "operationId": "createQuote",
        "tags": [
          "swap"
        ],
        "summary": "Ask for a quote",
        "description": "With `dry: true`, a preview that holds no address. With `dry: false`, a live quote for your two addresses, with a deposit address. The reply is 1Click's signed quote, unchanged. **Check a live quote before you send anything** (see the description of this API). The route is confidential on every quote. If 1Click has no confidential route for the pair, the answer is the error `no-confidential-route`, and no public quote is asked for in its place.",
        "x-rate-limit": {
          "window": "1 minute",
          "scope": "ip",
          "dry": 40,
          "live": 8
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "examples": {
                "dry": {
                  "summary": "A preview. It holds no address.",
                  "value": {
                    "dry": true,
                    "originAsset": "nep141:base-0x833589fcd6edb6e08f4c7c32d4f71b54bda02913.omft.near",
                    "destinationAsset": "nep141:zec.omft.near",
                    "amount": "50000000"
                  }
                },
                "live": {
                  "summary": "A live quote. Replace both addresses with your own.",
                  "value": {
                    "dry": false,
                    "originAsset": "nep141:base-0x833589fcd6edb6e08f4c7c32d4f71b54bda02913.omft.near",
                    "destinationAsset": "nep141:zec.omft.near",
                    "amount": "50000000",
                    "recipient": "<a new payout address on the destination chain>",
                    "refundTo": "<a refund address you control on the origin chain>",
                    "slippageBps": 100
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The signed quote.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteReply"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong, or the service refused it. `error.code` says why.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The request came from another site. Scripts and servers send no `Sec-Fetch-Site` header, and are not refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The body is too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "The body is not JSON. Send `content-type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many calls from this IP address, or 1Click is busy. Wait `Retry-After` seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "502": {
            "description": "1Click did not answer, is unavailable, or refused this site's key. Try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/status": {
      "post": {
        "operationId": "getStatus",
        "tags": [
          "status"
        ],
        "summary": "Follow a swap",
        "description": "Asks 1Click for the status of the swap with this deposit address. Poll it about every five seconds until the status is SUCCESS, REFUNDED or FAILED. The reply holds no payout or refund address. POST keeps the deposit address and the proof out of access logs. A deposit address is public on its chain, so the swap's details go only to the person who asked for the quote. Send `proof`, the `signature` of your live quote, to read them. With the right proof the reply holds every field. With none, or a wrong one, it holds `status` and nothing else, and the two replies are the same, so a guess cannot be tested. The tracking page takes the proof after the `#` of its link, which a browser never puts in a request URL, so it stays out of access logs: `/track#<deposit address>.<proof>`.",
        "x-rate-limit": {
          "window": "1 minute",
          "scope": "ip",
          "limit": 90
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StatusRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The status. With the right `proof`, every field. With none or a wrong one, `status` alone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusReply"
                },
                "examples": {
                  "withProof": {
                    "summary": "With the right proof",
                    "value": {
                      "status": "SUCCESS",
                      "updatedAt": "2026-10-12T10:05:00.000Z",
                      "swapDetails": {
                        "intentHashes": [],
                        "nearTxHashes": [],
                        "originChainTxHashes": [],
                        "destinationChainTxHashes": []
                      },
                      "origin": {
                        "symbol": "USDC",
                        "chain": "base"
                      },
                      "destination": {
                        "symbol": "SOL",
                        "chain": "sol"
                      },
                      "kind": "across"
                    }
                  },
                  "withoutProof": {
                    "summary": "With no proof, or a wrong one",
                    "value": {
                      "status": "SUCCESS"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong, or the service refused it. `error.code` says why.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The request came from another site. Scripts and servers send no `Sec-Fetch-Site` header, and are not refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "1Click does not know this deposit address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The body is too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "The body is not JSON. Send `content-type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many calls from this IP address, or 1Click is busy. Wait `Retry-After` seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "502": {
            "description": "1Click did not answer, is unavailable, or refused this site's key. Try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/deposit": {
      "post": {
        "operationId": "notifyDeposit",
        "tags": [
          "status"
        ],
        "summary": "Tell 1Click that you sent the deposit",
        "description": "Optional. 1Click finds the deposit by itself. This notice makes it look sooner. The hash must fit the chain of the deposit address.",
        "x-rate-limit": {
          "window": "1 minute",
          "scope": "ip",
          "limit": 12
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DepositRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "1Click was told.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DepositReply"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong, or the service refused it. `error.code` says why.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The request came from another site. Scripts and servers send no `Sec-Fetch-Site` header, and are not refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The body is too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "The body is not JSON. Send `content-type: application/json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many calls from this IP address, or 1Click is busy. Wait `Retry-After` seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "502": {
            "description": "1Click did not answer, is unavailable, or refused this site's key. Try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "health"
        ],
        "summary": "Is 1Click answering?",
        "description": "Is 1Click answering? Not cached. HTTP 200 while this server read 1Click's token list in the last two minutes, 503 when it did not, so an outside check needs no parsing.",
        "x-rate-limit": {
          "window": "1 minute",
          "scope": "ip",
          "limit": 30
        },
        "responses": {
          "200": {
            "description": "1Click is answering.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthReply"
                }
              }
            }
          },
          "503": {
            "description": "1Click has not answered in the last two minutes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthReply"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "bad-request",
                  "below-min",
                  "drift",
                  "invalid-recipient",
                  "invalid-refund",
                  "no-route",
                  "no-confidential-route",
                  "rate-limited",
                  "unreachable",
                  "upstream"
                ],
                "description": "`bad-request`: The request is wrong, the origin is another site, or the deposit address is unknown. Fix it. `below-min`: The amount is below the minimum for this pair. `minAmount` (base units) or `minUsd` says how much. `drift`: The price moved between two quotes asked for together. Nothing was quoted. Ask again. `invalid-recipient`: The payout address is refused for its chain. `invalid-refund`: The refund address is refused for its chain. A ZEC refund needs a transparent address (t1…). `no-route`: There is no route for this pair right now. Try another amount or another token. With `paused: true`, NEAR Intents has paused the pair: try another network or token. `no-confidential-route`: There is no confidential quote. The server never falls back to a public one. Nothing was quoted. `rate-limited`: Too many calls. `retryAfter` and the Retry-After header say how many seconds to wait. `unreachable`: 1Click did not answer. Try again. `upstream`: 1Click is unavailable. Try again."
              },
              "message": {
                "type": "string",
                "description": "Words for a person. Do not match on them. Match on `code`."
              },
              "minAmount": {
                "type": "string",
                "pattern": "^\\d+$",
                "description": "The smallest amount of the origin token, in base units."
              },
              "minUsd": {
                "type": "number",
                "description": "The smallest swap, in US dollars."
              },
              "retryAfter": {
                "type": "integer",
                "minimum": 1,
                "description": "Seconds to wait."
              },
              "paused": {
                "type": "boolean",
                "const": true,
                "description": "With `no-route`: NEAR Intents has paused this pair for now. Try another network or token. Another amount does not help."
              }
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "Token": {
        "type": "object",
        "required": [
          "assetId",
          "symbol",
          "decimals",
          "chain"
        ],
        "properties": {
          "assetId": {
            "type": "string",
            "maxLength": 200,
            "description": "1Click's id for the asset. A signed quote names it."
          },
          "symbol": {
            "type": "string",
            "description": "The ticker."
          },
          "decimals": {
            "type": "integer",
            "minimum": 0,
            "maximum": 36
          },
          "chain": {
            "type": "string",
            "description": "The chain's id, such as `base`, `sol` or `zec`."
          },
          "contractAddress": {
            "type": "string",
            "description": "The ERC-20 contract or SPL mint. Absent for a native coin."
          },
          "price": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "US dollars, from 1Click's list. For estimates and limits only."
          }
        },
        "additionalProperties": false
      },
      "TokensReply": {
        "type": "object",
        "required": [
          "tokens",
          "fetchedAt"
        ],
        "properties": {
          "tokens": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Token"
            }
          },
          "fetchedAt": {
            "type": "string",
            "format": "date-time"
          },
          "routes": {
            "type": "object",
            "description": "Present only when the website offers routes these docs do not cover, such as `zcash: true`.",
            "properties": {
              "zcash": {
                "type": "boolean",
                "const": true
              }
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "QuoteRequest": {
        "type": "object",
        "properties": {
          "dry": {
            "type": "boolean",
            "description": "true for a preview: no deposit address, and `recipient` and `refundTo` may be left out. false for a live quote, which needs both and returns a deposit address."
          },
          "originAsset": {
            "type": "string",
            "minLength": 3,
            "maxLength": 200,
            "description": "1Click's asset id of the token you send. Take it from `GET /api/swap/tokens`. Tokens on `hypercore` (USDC on Hyperliquid) are payout only. Use them as `destinationAsset`, never as `originAsset`. Their minimum is about $8."
          },
          "destinationAsset": {
            "type": "string",
            "minLength": 3,
            "maxLength": 200,
            "description": "1Click's asset id of the token you receive. It must differ from `originAsset`, unless `send` is true: then it is the same."
          },
          "amount": {
            "type": "string",
            "pattern": "^[1-9]\\d{0,38}$",
            "description": "How much of the origin token to send, in base units, as a string of digits that does not start with 0. Each swap is worth $5 to $50,000."
          },
          "recipient": {
            "type": "string",
            "maxLength": 2000,
            "description": "The payout address, on the destination chain. Required when `dry` is false. Use a new address each time."
          },
          "refundTo": {
            "type": "string",
            "maxLength": 2000,
            "description": "The refund address, on the origin chain. Required when `dry` is false. Use an address you control, not an exchange. When you pay with ZEC, use a t1… address: NEAR Intents refunds ZEC to a transparent address only, and a refund there is public."
          },
          "transparentOptIn": {
            "default": false,
            "type": "boolean",
            "description": "Allow a Zcash payout to a transparent address. It is public. Leave it out for any other chain."
          },
          "slippageBps": {
            "type": "integer",
            "enum": [
              50,
              100,
              200
            ],
            "default": 100,
            "description": "How far the payout may fall below the quote, in basis points."
          },
          "send": {
            "default": false,
            "type": "boolean",
            "description": "true for a send: the payout goes to someone else's address, and `destinationAsset` must be the same as `originAsset`."
          },
          "exactOutput": {
            "default": false,
            "type": "boolean"
          }
        },
        "required": [
          "dry",
          "originAsset",
          "destinationAsset",
          "amount"
        ],
        "additionalProperties": false
      },
      "SignedRequest": {
        "type": "object",
        "description": "1Click's echo of the request it signed. Fields it does not sign (`appFees`, `depositMode`, `confidentiality`) are claims.",
        "required": [
          "dry",
          "swapType",
          "slippageTolerance",
          "originAsset",
          "depositType",
          "destinationAsset",
          "amount",
          "refundTo",
          "refundType",
          "recipient",
          "recipientType",
          "deadline"
        ],
        "properties": {
          "dry": {
            "type": "boolean"
          },
          "swapType": {
            "type": "string",
            "enum": [
              "EXACT_INPUT",
              "EXACT_OUTPUT",
              "FLEX_INPUT"
            ]
          },
          "slippageTolerance": {
            "type": "integer",
            "description": "Basis points."
          },
          "originAsset": {
            "type": "string"
          },
          "depositType": {
            "type": "string",
            "enum": [
              "ORIGIN_CHAIN"
            ]
          },
          "destinationAsset": {
            "type": "string"
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+$"
          },
          "refundTo": {
            "type": "string"
          },
          "refundType": {
            "type": "string",
            "enum": [
              "ORIGIN_CHAIN"
            ]
          },
          "recipient": {
            "type": "string"
          },
          "recipientType": {
            "type": "string",
            "enum": [
              "DESTINATION_CHAIN"
            ]
          },
          "deadline": {
            "type": "string",
            "format": "date-time",
            "description": "When refunds begin if the swap is not done."
          },
          "depositMode": {
            "type": "string",
            "description": "Absent or SIMPLE. Anything else is a reason to send nothing."
          },
          "confidentiality": {
            "type": "string",
            "description": "The route 1Click quoted: `public`, `basic` or `advanced`."
          },
          "referral": {
            "type": "string"
          },
          "quoteWaitingTimeMs": {
            "type": "integer"
          },
          "virtualChainRecipient": {
            "type": "string"
          },
          "virtualChainRefundRecipient": {
            "type": "string"
          },
          "customRecipientMsg": {
            "type": "string"
          },
          "appFees": {
            "type": "array",
            "description": "The fees in the quote, in basis points. Not signed.",
            "items": {
              "type": "object",
              "required": [
                "fee"
              ],
              "properties": {
                "recipient": {
                  "type": "string"
                },
                "fee": {
                  "type": "integer"
                }
              },
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true
      },
      "SignedAmounts": {
        "type": "object",
        "description": "The figures of the quote. 1Click signs every one of them. Amounts are base units, as strings.",
        "required": [
          "amountIn",
          "amountInFormatted",
          "amountInUsd",
          "minAmountIn",
          "amountOut",
          "amountOutFormatted",
          "amountOutUsd",
          "minAmountOut"
        ],
        "properties": {
          "amountIn": {
            "type": "string",
            "pattern": "^\\d+$",
            "description": "Send exactly this much."
          },
          "amountInFormatted": {
            "type": "string",
            "description": "`amountIn` with the token's decimals applied."
          },
          "amountInUsd": {
            "type": "string"
          },
          "minAmountIn": {
            "type": "string",
            "pattern": "^\\d+$"
          },
          "amountOut": {
            "type": "string",
            "pattern": "^\\d+$",
            "description": "The payout 1Click expects to make."
          },
          "amountOutFormatted": {
            "type": "string"
          },
          "amountOutUsd": {
            "type": "string"
          },
          "minAmountOut": {
            "type": "string",
            "pattern": "^\\d+$",
            "description": "The least the payout can be, with the slippage."
          },
          "timeEstimate": {
            "type": "number",
            "description": "Seconds. It counts from the moment the deposit confirms."
          },
          "depositAddress": {
            "type": "string",
            "description": "Only on a live quote. Send the token here, after you have checked the quote."
          },
          "depositMemo": {
            "type": "string",
            "description": "A reason to send nothing. A quote that asks for a memo is refused."
          },
          "deadline": {
            "type": "string",
            "format": "date-time",
            "description": "When the deposit address goes cold."
          },
          "timeWhenInactive": {
            "type": "string",
            "format": "date-time"
          },
          "refundFee": {
            "type": "string",
            "pattern": "^\\d+$"
          },
          "withdrawFee": {
            "type": "string",
            "pattern": "^\\d+$",
            "description": "The payout network fee, in base units of the destination token."
          }
        },
        "additionalProperties": true
      },
      "SignedQuote": {
        "type": "object",
        "description": "1Click's quote, with its signature. The server passes it on unchanged.",
        "required": [
          "timestamp",
          "signature",
          "quoteRequest",
          "quote"
        ],
        "properties": {
          "correlationId": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "When 1Click signed it. The signature covers it."
          },
          "signature": {
            "type": "string",
            "description": "ed25519, base58, with an `ed25519:` prefix. The key is ed25519:reYaWhvwu8Jzo3WUM3zhn6VrhuMEF4eADL17qtRVifc."
          },
          "quoteRequest": {
            "$ref": "#/components/schemas/SignedRequest"
          },
          "quote": {
            "$ref": "#/components/schemas/SignedAmounts"
          }
        },
        "additionalProperties": true
      },
      "QuoteReply": {
        "type": "object",
        "required": [
          "quote",
          "route",
          "preview"
        ],
        "properties": {
          "quote": {
            "$ref": "#/components/schemas/SignedQuote"
          },
          "route": {
            "type": "string",
            "enum": [
              "public",
              "basic",
              "advanced"
            ],
            "description": "The route the server asked 1Click for. It is `basic` or `advanced`, which are the confidential routes. It is not signed: a claim."
          },
          "preview": {
            "type": "object",
            "required": [
              "recipient",
              "refundTo"
            ],
            "description": "True for an address that the server filled in, because a dry quote left it out.",
            "properties": {
              "recipient": {
                "type": "boolean"
              },
              "refundTo": {
                "type": "boolean"
              }
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "StatusRequest": {
        "type": "object",
        "required": [
          "depositAddress"
        ],
        "properties": {
          "depositAddress": {
            "type": "string",
            "maxLength": 120,
            "description": "The deposit address of a live quote, on any chain Zecret Swap offers."
          },
          "proof": {
            "type": "string",
            "maxLength": 200,
            "description": "The `signature` of your live quote (`quote.signature` in the quote reply): `ed25519:` and base58. It shows that you asked for the quote, and it unlocks the swap's details. Leave it out, or send a wrong one, and the reply is the status alone. Treat it like a key."
          }
        },
        "additionalProperties": false
      },
      "TxRef": {
        "type": "object",
        "required": [
          "hash"
        ],
        "properties": {
          "hash": {
            "type": "string"
          },
          "explorerUrl": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "SwapDetails": {
        "type": "object",
        "description": "1Click's own details, passed on.",
        "properties": {
          "intentHashes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "nearTxHashes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "amountIn": {
            "type": "string"
          },
          "amountInFormatted": {
            "type": "string"
          },
          "amountInUsd": {
            "type": "string"
          },
          "amountOut": {
            "type": "string"
          },
          "amountOutFormatted": {
            "type": "string"
          },
          "amountOutUsd": {
            "type": "string"
          },
          "slippage": {
            "type": "number"
          },
          "originChainTxHashes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TxRef"
            }
          },
          "destinationChainTxHashes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TxRef"
            }
          },
          "refundedAmount": {
            "type": "string"
          },
          "refundedAmountFormatted": {
            "type": "string"
          },
          "refundedAmountUsd": {
            "type": "string"
          },
          "refundReason": {
            "type": "string"
          },
          "depositedAmount": {
            "type": "string"
          },
          "depositedAmountFormatted": {
            "type": "string"
          },
          "depositedAmountUsd": {
            "type": "string"
          },
          "withdrawFee": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "StatusReply": {
        "type": "object",
        "description": "With the right proof, every field below. Without it, or with a wrong one, only `status`: no `updatedAt`, no `swapDetails`, no `origin`, no `destination`, no `kind` and no `timeEstimate`.",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "PENDING_DEPOSIT",
              "KNOWN_DEPOSIT_TX",
              "INCOMPLETE_DEPOSIT",
              "PROCESSING",
              "SUCCESS",
              "REFUNDED",
              "FAILED"
            ],
            "description": "SUCCESS, REFUNDED and FAILED are final. Nothing more will change."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "swapDetails": {
            "$ref": "#/components/schemas/SwapDetails"
          },
          "origin": {
            "type": "object",
            "description": "The token sent, when Zecret Swap lists it.",
            "required": [
              "symbol",
              "chain"
            ],
            "properties": {
              "symbol": {
                "type": "string"
              },
              "chain": {
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          "destination": {
            "type": "object",
            "description": "The token received, when Zecret Swap lists it.",
            "required": [
              "symbol",
              "chain"
            ],
            "properties": {
              "symbol": {
                "type": "string"
              },
              "chain": {
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          "kind": {
            "type": "string",
            "enum": [
              "in",
              "out",
              "across"
            ],
            "description": "Which way the swap runs: into ZEC, out of ZEC, or across, with no ZEC on either side."
          },
          "timeEstimate": {
            "type": "integer",
            "minimum": 1,
            "maximum": 86400,
            "description": "1Click's estimate from the quote, in seconds, counted from the moment the deposit confirms."
          }
        },
        "additionalProperties": false
      },
      "DepositRequest": {
        "type": "object",
        "required": [
          "txHash",
          "depositAddress"
        ],
        "properties": {
          "txHash": {
            "type": "string",
            "description": "The hash of your deposit transaction, in the form its chain uses."
          },
          "depositAddress": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "DepositReply": {
        "type": "object",
        "required": [
          "ok"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          }
        },
        "additionalProperties": false
      },
      "HealthReply": {
        "type": "object",
        "required": [
          "oneClick",
          "checkedAt"
        ],
        "properties": {
          "oneClick": {
            "type": "string",
            "enum": [
              "up",
              "down"
            ]
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      }
    }
  }
}