{
  "openapi": "3.1.0",
  "info": {
    "title": "OutLayer API",
    "version": "0.1.0-alpha.1",
    "summary": "HTTP API for OutLayer custody wallets and policy enforcement",
    "description": "OutLayer Agent Custody — multi-chain wallets for AI agents with TEE-enforced\npolicy, multisig approvals, audit log, gasless cross-chain transfers via\nNEAR Intents, and off-chain EVM signing (EIP-712 typed data, EIP-191\npersonal_sign, and client-serialized raw transactions).\n\nPrivate keys live exclusively inside an Intel TDX TEE and are derived from\nNEAR MPC. The wallet owner sets policy (spending limits, whitelists,\nmultisig, freeze) — all enforced inside the TEE before signing.\n\nSee [CUSTODY docs](https://outlayer.fastnear.com/docs/agent-custody) for the\narchitectural overview and [@outlayer/sdk](https://www.npmjs.com/package/@outlayer/sdk)\nfor the TypeScript client.\n",
    "license": {
      "name": "MIT",
      "identifier": "MIT"
    },
    "contact": {
      "name": "OutLayer",
      "url": "https://outlayer.fastnear.com"
    }
  },
  "servers": [
    {
      "url": "https://api.outlayer.fastnear.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Registration",
      "description": "Create a new wallet and obtain an API key."
    },
    {
      "name": "Execution",
      "description": "Run a project. A connector is an ordinary project we curated and priced, reached through the same route; what being one adds — an `operation` it must name, a price it must pay, an owner's secret it may ask for — is decided from the project id, not from the way in. Synchronous by default; send `async` and poll for anything longer than a few seconds."
    },
    {
      "name": "Subscriptions",
      "description": "Payment keys, the allowance a subscription buys, and the secret an owner leaves for their agent. Only money pays a connector's author: a call covered by an allowance costs the caller nothing at the moment of the call, so nothing passes through."
    },
    {
      "name": "Wallet",
      "description": "Multi-chain custody operations — transfer, withdraw, swap, balance, and signing, including NEAR (NEP-413) message signing and off-chain EVM signing (EIP-712 typed data, EIP-191 personal_sign, and client-serialized raw transactions)."
    },
    {
      "name": "Confidential",
      "description": "Confidential intents — move balances between the public intents shard, the Defuse confidential shard, and external chains. The confidential balance ledger is real on-chain state on a separate **private** shard (the `intents.far` contract, distinct from public `intents.near`); it has no public RPC, so you cannot read it externally, but it is an auditable smart contract. Only SHIELD/UNSHIELD touch the public chain. Requires `ENABLE_CONFIDENTIAL_INTENTS` + the confidential partner agreement on the deployment, else every route returns 503. See the agent integration guide referenced under `externalDocs` for the full mental model, privacy recipes, and curl walkthroughs.",
      "externalDocs": {
        "description": "Confidential intents — agent integration guide (mental model, privacy recipes, curl walkthroughs)",
        "url": "https://github.com/out-layer/coordinator/blob/main/docs/CONFIDENTIAL_INTENTS.md"
      }
    },
    {
      "name": "Policy",
      "description": "Encrypted on-chain policy lifecycle (read, encrypt, sign, cache)."
    },
    {
      "name": "Approvals",
      "description": "Multisig approval workflows for actions that exceed policy thresholds."
    },
    {
      "name": "Audit",
      "description": "Event history for the wallet."
    },
    {
      "name": "Requests",
      "description": "Async operation status tracking."
    },
    {
      "name": "Meta",
      "description": "Token catalog and helpers."
    }
  ],
  "paths": {
    "/register": {
      "post": {
        "tags": [
          "Registration"
        ],
        "operationId": "registerWallet",
        "summary": "Register a new wallet",
        "description": "Creates a new multi-chain wallet and returns an API key. For most\ncallers, send `{}` — an anonymous wallet is created and an API key is\nreturned in `api_key` (shown once, not retrievable later).\n\nOptional fields enable advanced registration paths:\n- `account_id` + `pubkey` + `message` + `signature`: bind the wallet to\n  a NEAR account via NEP-413 proof-of-ownership.\n- `vault_id`: bind to a customer-owned vault for sovereign custody (see\n  vault docs).\n\nRate-limited: 30 requests per minute per IP.\n",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              },
              "examples": {
                "anonymous": {
                  "summary": "Anonymous wallet (most common)",
                  "value": {}
                },
                "bound_to_near_account": {
                  "summary": "Bind to NEAR account via NEP-413",
                  "value": {
                    "account_id": "alice.near",
                    "pubkey": "ed25519:7BcBZ9Z...",
                    "message": "register:1716200000",
                    "signature": "ed25519:5K3F..."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Wallet created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegisterResponse"
                },
                "examples": {
                  "anonymous": {
                    "value": {
                      "wallet_id": "9c3c9e10-1c1f-4f5e-9c4a-1d7b9a8f3c20",
                      "api_key": "wk_2a8b1f3c4d5e6789abcdef0123456789",
                      "near_account_id": "9c3c9e101c1f4f5e9c4a1d7b9a8f3c20",
                      "handoff_url": "https://outlayer.fastnear.com/wallet?api_key=wk_2a8b...",
                      "trial": {
                        "calls_remaining": 100,
                        "expires_at": "2026-06-20T00:00:00Z",
                        "limits": {
                          "max_instructions": 100000000,
                          "max_execution_seconds": 30,
                          "max_memory_mb": 64
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/address": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "getAddress",
        "summary": "Derive wallet address for a chain",
        "description": "Returns the deterministically-derived address for the wallet on the\nrequested chain. Same `wallet_id` always produces the same addresses\nacross chains.\n\nFor `chain=near` the response also carries the two Agent Connect\nidentities: `executor_account_id` (always equal to `address` — the\nwallet's implicit account, which signs and pays gas) and\n`asset_account_id` (the bound named account, `null` when the wallet\nhas no binding). They are separate fields on purpose: product balances\nbelong to the asset account, while signing, predecessor checks and gas\nkey off the executor.\n",
        "parameters": [
          {
            "name": "chain",
            "in": "query",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Chain"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Address derived",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddressResponse"
                },
                "example": {
                  "wallet_id": "9c3c9e10-1c1f-4f5e-9c4a-1d7b9a8f3c20",
                  "chain": "near",
                  "address": "9c3c9e101c1f4f5e9c4a1d7b9a8f3c20",
                  "public_key": "ed25519:7BcBZ9Z...",
                  "vault_id": null,
                  "asset_account_id": "agent.tla",
                  "executor_account_id": "9c3c9e101c1f4f5e9c4a1d7b9a8f3c20"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/balance": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "getBalance",
        "summary": "Read balance",
        "description": "Returns the balance for a token on the requested chain. Currently only\n`chain=near` is supported for native balance reads; cross-chain balances\nshould be read from the upstream chain directly.\n\nSet `source=intents` to read the wallet's intents.near balance\n(`mt_balance_of`) instead of the on-chain account balance.\n\nBy default this endpoint answers for the WALLET'S OWN account (the\nexecutor), bound or not — its meaning never changes when a binding is\nadded. Pass `account=asset` to ask about the bound account instead;\nthat path is identical to `GET /wallet/v1/binding/balance`, which\nremains available.\n\nThe default is deliberately NOT the binding. Following it would change,\nwith no warning, which account an existing caller's number describes —\nincluding for `personal_account` bindings, whose owners never asked for\nAgent Connect semantics. `asset` is explicit.\n\nThe rule for both sides of the API is one sentence: everything under\n`/wallet/v1/binding/` is the bound account, everything else is the\nwallet itself unless `account=asset` says otherwise.\n`/wallet/v1/transfer` moves the wallet's own funds;\n`/wallet/v1/binding/transfer` moves the bound account's.\n\n`account=asset` on a wallet with no ACTIVE binding is refused rather\nthan answered with the executor's figure under an asset label.\n\n`source=intents` belongs to the executor and only the executor: an\nintents.near deposit is credited to whoever signed it, so the bound\naccount has no intents balance and never will. Combining it with\n`account=asset` is refused.\n\nThe response names the identity it describes in `account`.\n",
        "parameters": [
          {
            "name": "account",
            "in": "query",
            "required": false,
            "description": "Which of a bound wallet's two identities the balances describe. Omitted means `executor`, unchanged from before this parameter existed. `asset` requires an ACTIVE binding and is refused otherwise.",
            "schema": {
              "type": "string",
              "enum": [
                "asset",
                "executor"
              ],
              "default": "executor"
            }
          },
          {
            "name": "chain",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/Chain",
              "default": "near"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Token ID (`nep141:<contract>` or `native`)."
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "chain",
                "intents"
              ],
              "default": "chain"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Balance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceResponse"
                },
                "example": {
                  "balance": "1000000000000000000000000",
                  "token": "NEAR",
                  "account_id": "9c3c9e101c1f4f5e9c4a1d7b9a8f3c20",
                  "account": "executor"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/tokens": {
      "get": {
        "tags": [
          "Meta"
        ],
        "operationId": "listTokens",
        "summary": "List supported tokens",
        "description": "Returns the token catalog supported for swaps and cross-chain withdraws\nvia NEAR Intents. Token IDs are typed as `nep141:<contract>` and map to\neach chain via `defuse_asset_id`.\n",
        "responses": {
          "200": {
            "description": "Token list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokensResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/binding": {
      "put": {
        "tags": [
          "Wallet"
        ],
        "operationId": "putBinding",
        "summary": "Register the account this wallet operates",
        "description": "Records the one-to-one binding between this custody wallet and an\non-chain account, in one of two mutually exclusive modes (`kind`):\n\n* `hos_lease` (default) — a leased, keyless asset account\n  (e.g. `agent.tla`). `impl_version` is REQUIRED and gated against\n  the supported registry; `owner_account_id` is required.\n* `personal_account` — the caller's own named account\n  (e.g. `user.near`) with the upstream no-sign wallet contract the\n  owner installs personally (see `GET /wallet/v1/binding/setup`).\n  `impl_version` is REJECTED — this mode is versioned by the\n  account's wasm code hash, which the client never declares.\n  `owner_account_id` is optional and must equal `asset_account_id`.\n\nThe response returns `executor_account_id` — the identity to register\nin the account's extension set (and, for `hos_lease`, to provision a\nspend grant for).\n\nGuarantees: binding is one-to-one, and a second call naming a\nDIFFERENT asset account is rejected rather than silently rebinding\n(re-PUTting the same pair in the same mode is idempotent). An\nunsupported `impl_version` is rejected here, at binding time, not at\nfirst execution — that refusal is terminal, do not retry.\n\nThe binding itself authorizes nothing: `binding_status` stays `pending`\nuntil the executor is first observed live in the extension set on\nchain, and nothing can be executed against the account until its owner\n(for `hos_lease`, the partner's provisioning) puts the executor there.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PutBindingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Binding recorded (or the identical existing binding)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BindingResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "The wallet or the asset account is already bound",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "getBinding",
        "summary": "Read the wallet's binding",
        "description": "Returns the binding record, refreshed against the chain\n(`hos_agent_status` + `nft_item_info`), or 404 when the wallet has\nnone. `binding_status` reflects OutLayer's view only — it does not\nattest that the executor is still in the control set, which can change\nwithout OutLayer being told. `gas_balance` is the executor's native\nNEAR in yoctoNEAR, so it can be topped up before a call fails rather\nthan after.\n",
        "responses": {
          "200": {
            "description": "The binding record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BindingResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "The wallet has no binding",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Wallet"
        ],
        "operationId": "deleteBinding",
        "summary": "End the binding",
        "description": "Ends the binding (revocation, transfer, sale, recovery, lease expiry).\nIdempotent — deleting a wallet with no binding is a success. Pending\nmultisig approvals whose operation targets the asset account are\ncancelled rather than left completable; the wallet itself survives,\nonly the binding ends.\n",
        "responses": {
          "200": {
            "description": "The binding is ended (or there was none)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteBindingResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/binding/events": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "postBindingEvent",
        "summary": "Notify OutLayer that a bound account changed",
        "description": "For the account provider (House of Stake) to call on revoke, transfer,\nrecovery, freeze or lease change. Authorized by a shared secret in\n`X-Binding-Webhook-Secret`; answers `503` where no secret is\nconfigured.\n\n**The body is a hint, not an instruction.** A `revoked` event revokes\nnothing: OutLayer drops its cached view of the account and re-reads the\nchain, and the chain decides the resulting `binding_status`. A webhook\nis an assertion about somebody else's account, so a leaked or\nmisconfigured secret must not become the ability to disable an agent by\nname.\n\nThe endpoint is therefore an accelerator with no authority, and it is\noptional: without it, a change is noticed within seconds anyway,\nbecause every signing pre-flight reads the chain. Skipping it costs\nlatency, never correctness.\n\nAnswers `200` with `binding_status: unbound` for an account OutLayer\ndoes not operate, so an unknown account does not accumulate in a retry\nqueue.\n",
        "security": [],
        "parameters": [
          {
            "name": "X-Binding-Webhook-Secret",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "asset_account_id"
                ],
                "properties": {
                  "asset_account_id": {
                    "type": "string",
                    "description": "The account the news is about."
                  },
                  "event": {
                    "type": "string",
                    "description": "What happened, in the sender's words (`revoked`, `transferred`, `recovered`, `frozen`, …). Logged for correlation; never acted on."
                  }
                }
              },
              "example": {
                "asset_account_id": "agent.tla",
                "event": "revoked"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Re-read; the status the chain now implies",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "binding_status"
                  ],
                  "properties": {
                    "binding_status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "active",
                        "suspended",
                        "revoked",
                        "unbound"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "description": "No binding-event secret is configured on this deployment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "getBindingZones",
        "summary": "Which account names this webhook may report on",
        "description": "The account-name suffixes `POST /wallet/v1/binding/events` accepts,\nread with the same `X-Binding-Webhook-Secret`. Ask before sending the\nfirst event for a newly opened zone: an account outside the list\nanswers `binding_status: unbound` and nothing happens.\n\nAn EMPTY list means no zone restriction — every name is accepted,\nsubject to the rule that always applies (only leased bindings are\nreachable at all). Zones are editable by OutLayer without a restart, so\na new one takes effect as soon as it is added.\n",
        "security": [],
        "parameters": [
          {
            "name": "X-Binding-Webhook-Secret",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The zones currently accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "zones"
                  ],
                  "properties": {
                    "zones": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "zones": [
                    "tla.near"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "description": "No binding-event secret is configured on this deployment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/wallet/v1/binding/balance": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "getBindingBalance",
        "summary": "Read the BOUND account's balance",
        "description": "The balance of the account this wallet is bound to — where an Agent\nConnect wallet's product money lives. The wallet's own account (gas,\nand anything it holds itself) is `GET /wallet/v1/balance`.\n\nRequires an ACTIVE binding. A `pending` one answers 400 saying so: it\nauthorizes nothing, and the executor is not in the account's extension\nset yet, so the account holds nothing on this wallet's behalf. No\nbinding at all answers 404. Neither ever falls back to the wallet's own\nfigure under an asset label — a number the owner acts on must not\nquietly be about a different account.\n\n`source=intents` is refused here and pointed at `/wallet/v1/balance`:\nintents deposits are credited to the signer, which is always the\nexecutor.\n",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Token ID (`nep141:<contract>` or `native`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Balance of the bound account (`account` is `asset`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "The wallet has no binding",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/binding/transfer": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "bindingTransfer",
        "summary": "Spend from the BOUND account",
        "description": "Move native NEAR or a NEP-141 token out of the bound asset account.\n\nA BUILDER over the existing lane, not a new one. What gets signed is\nexactly the `w_execute_extension` you could post to `/wallet/v1/call`\nyourself: same canonical op, same pre-flight, same spend-grant rules,\nsame policy evaluation, same multisig trigger, same per-promise receipt\nhandling. It exists so you do not have to assemble a nested base64\nenvelope by hand.\n\nSeparate from `/wallet/v1/transfer` because it is a different\noperation, not a mode of the same one: `/wallet/v1/transfer` signs a\nplain transfer from the wallet's own account, this signs a contract\ncall under an on-chain grant, with its own error classes\n(`agent_connect_denied`) and its own approval path.\n\nResponses are those of `/wallet/v1/call`, including `403\nagent_connect_denied` when the pre-flight refuses — read `terminal`\nbefore retrying.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to",
                  "amount"
                ],
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Where the value lands. For a token this is the LOGICAL recipient, not the token contract."
                  },
                  "amount": {
                    "type": "string",
                    "description": "Native yoctoNEAR, or the token's own smallest unit when `token` is set."
                  },
                  "token": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "NEP-141 contract. Absent means native NEAR."
                  },
                  "memo": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "NEP-141 `memo`, passed through untouched."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted; same shape as `/wallet/v1/call`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The Agent Connect pre-flight refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentConnectDeniedResponse"
                }
              }
            }
          },
          "404": {
            "description": "The wallet has no binding",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Another operation holds this wallet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/binding/setup": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "getBindingSetup",
        "summary": "Installation kit for a personal_account binding",
        "description": "`kind=personal_account` only (for `hos_lease` the partner provisions\naccounts, and this endpoint answers 400). Requires an existing\n`personal_account` binding for the wallet.\n\nReturns ONE transaction of three actions — reference the wallet code\nby its global-contract hash, `w_init` (1 yoctoNEAR), and\n`w_execute_extension` adding the executor as an extension — with\nreceiver = signer = the owner's own account. OutLayer only assembles\npayloads; the owner signs with any wallet, and their signature is\nwhat authorizes the lane, once and entirely.\n\nHARD refusal, not a warning: if the account already has ANY contract\ndeployed, the kit answers 409 — deploying over it would not clear the\nold contract's state (the typical victim being a 2FA/multisig wallet\ncontract).\n\nThe wallet code is referenced by GLOBAL-CONTRACT HASH, and a global\ncontract is published per network: the same wasm has the same hash\neverywhere, but somebody has to publish it once on each chain. This\nendpoint checks that before handing you a transaction, and answers 400\nwhere it has not been published — otherwise you would sign a\ntransaction that cannot succeed.\n",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "personal_account"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The installation transaction, ready to sign",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BindingSetupResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "The wallet has no binding",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "The account already has a contract deployed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "description": "The chain could not be read just now, so neither the account's\ncurrent code nor the availability of the wallet contract could be\nconfirmed. Transient — retry. (A network where the wallet contract\nis not published answers 400 instead, because that does not change\non a retry.)\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/wallet/v1/call": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "call",
        "summary": "Sign and broadcast a NEAR function call",
        "description": "Native NEAR contract call. Policy is enforced before signing; if the\ncall exceeds limits, the response carries `status=pending_approval`\nand an `approval_id` for the multisig flow.\n\n\nAgent Connect: a `w_execute_extension` call aimed at the wallet's own\nbound account is additionally checked against the LIVE chain — binding\nliveness, spend grant, call form, reserve floor — before anything is\nsigned. That refusal is a `403` shaped as `AgentConnectDeniedResponse`\n(not the generic error body), carrying the class, the promise index and\na `terminal` flag. The policy engine independently decodes the same\nrequest and rules it on the owner's own limits.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallRequest"
              },
              "example": {
                "receiver_id": "usdt.tether-token.near",
                "method_name": "ft_transfer",
                "args": {
                  "receiver_id": "bob.near",
                  "amount": "1000000"
                },
                "gas": "30000000000000",
                "deposit": "1"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Call submitted (or queued for approval)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Refused by policy, or — for `w_execute_extension` on a bound account — by the Agent Connect pre-flight, which answers `AgentConnectDeniedResponse`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/AgentConnectDeniedResponse"
                    }
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Another money-moving operation is using this wallet. Applies to every spending endpoint, not only this one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletBusyResponse"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/OnChainTxFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "description": "The chain could not be reached to verify a binding. Transient — retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/wallet/v1/transfer": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "transfer",
        "summary": "Native chain transfer",
        "description": "Chain-agnostic native transfer (NEAR yoctoNEAR units). Currently only\n`chain=near` is supported; other chains return `unsupported_chain`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransferRequest"
              },
              "examples": {
                "canonical": {
                  "summary": "Canonical `to` field",
                  "value": {
                    "chain": "near",
                    "to": "bob.near",
                    "amount": "1000000000000000000000000"
                  }
                },
                "legacyReceiverId": {
                  "summary": "Legacy `receiver_id` alias (still accepted)",
                  "value": {
                    "chain": "near",
                    "receiver_id": "bob.near",
                    "amount": "1000000000000000000000000"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transfer submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/OnChainTxFailed"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/delete": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "deleteWallet",
        "summary": "Delete the wallet account, sweeping its full balance to a beneficiary",
        "description": "Irreversible. Signs and broadcasts NEAR's native `DeleteAccount`\naction, which sends the wallet account's **entire** remaining NEAR\nbalance to `beneficiary`, revokes all API keys, and marks the wallet\ndeleted. Guards: the beneficiary cannot be the wallet's own account,\nthe wallet must have a non-zero on-chain balance (a zero-balance\nimplicit account does not exist on-chain), and the wallet must not be\nalready deleted. Policy is enforced before signing; on a multisig\nwallet the response carries `status=pending_approval` and an\n`approval_id` for the multisig flow.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteRequest"
              },
              "example": {
                "beneficiary": "treasury.near",
                "chain": "near"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Account deleted (or queued for approval)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/OnChainTxFailed"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/storage-deposit": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "storageDeposit",
        "summary": "Register storage on a NEP-141 token contract",
        "description": "Calls `storage_deposit` (with `registration_only: true`) on a NEP-141\ntoken contract so `account_id` (defaults to the wallet's own NEAR\naddress) can hold that token. Idempotent: if the account is already\nregistered the call returns `already_registered: true` without signing\na transaction. The wallet must have NEAR to pay gas when a transaction\nis required. Policy gates this as a `call`; multisig is not supported\nfor this endpoint.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StorageDepositRequest"
              },
              "example": {
                "token": "wrap.near"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Storage registered (or already registered)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StorageDepositResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/OnChainTxFailed"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/intents/deposit": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "intentsDeposit",
        "summary": "Deposit FT into intents.near",
        "description": "Wraps `ft_transfer_call` to `intents.near`, auto-handling storage\ndeposit if required. After this call, the wallet's intents.near balance\nincreases by `amount` of `token` and is available for swaps and\ngasless cross-chain withdrawals.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntentsDepositRequest"
              },
              "example": {
                "token": "wrap.near",
                "amount": "5000000000000000000000000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deposit submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntentsDepositResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/OnChainTxFailed"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/intents/withdraw": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "intentsWithdraw",
        "summary": "Withdraw via NEAR Intents (native NEAR / wNEAR / cross-chain)",
        "description": "Gasless withdrawal through NEAR Intents solver relay. The wallet's\nintents.near balance is debited; the receiver gets funds without paying\ngas on either side.\n\n**`chain=near`** — the `token` field selects what the recipient receives:\n  - omitted / `near` / `native` (default): **native NEAR**. intents.near\n    unwraps the wallet's wNEAR (`native_withdraw` intent) and sends native\n    NEAR. The recipient needs **no** `wrap.near` storage. The recipient\n    account must already exist (or be a 64-char implicit account) —\n    withdrawing to a non-existent named account is rejected (the unwrapped\n    wNEAR would otherwise be burned).\n  - `nep141:wrap.near`: **wNEAR** (NEP-141). Recipient must be\n    storage-registered on `wrap.near`.\n  - other `nep141:<token>`: that NEP-141. Recipient must be storage-registered.\n\n**Other chains** (`ethereum`, `solana`, `bitcoin`, ...) — `token` is the\nsource Intents asset; 1Click bridges and delivers the destination chain's\nnative asset.\n\nIf the policy requires approval, returns `status=pending_approval` with\nan `approval_id`.\n\n**Cross-chain withdrawals are best called with `async=true`**: the bridge\ncan take longer than the synchronous response window, so the call returns\n`status=processing` + a `poll_url` immediately and you poll\n`GET /wallet/v1/requests/{request_id}` for the terminal status.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WithdrawRequest"
              },
              "examples": {
                "nativeNear": {
                  "summary": "Native NEAR (default for chain=near)",
                  "value": {
                    "chain": "near",
                    "to": "receiver.near",
                    "amount": "1000000000000000000000000",
                    "token": "near"
                  }
                },
                "wnear": {
                  "summary": "wNEAR (NEP-141, recipient needs wrap.near storage)",
                  "value": {
                    "chain": "near",
                    "to": "receiver.near",
                    "amount": "1000000000000000000000000",
                    "token": "nep141:wrap.near"
                  }
                },
                "crossChain": {
                  "summary": "Cross-chain (1Click delivers native on the target chain)",
                  "value": {
                    "chain": "ethereum",
                    "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f8b4f5",
                    "amount": "1000000",
                    "token": "nep141:usdt.tether-token.near"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Withdraw submitted (or queued for approval)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WithdrawResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/intents/withdraw/dry-run": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "intentsWithdrawDryRun",
        "summary": "Simulate a withdraw (policy + balance check, no execution)",
        "description": "Runs the full policy and balance check for a withdraw without signing\nor broadcasting. Useful for showing the user whether an action would\nsucceed before they confirm.\n\nEvery pre-condition the real withdraw enforces is evaluated here, in the\nsame order: request shape, policy (same canonical op — cross-chain is\ngated by the separate `cross_chain_withdraw` capability), intents\nbalance, NEAR recipient existence / NEP-141 storage registration, and —\nfor a cross-chain withdraw — a `dry` 1Click quote. The quote is what\nsurfaces bridge minimums, unsupported routes and missing liquidity\nbefore the user confirms; its result is summarised in `message`.\n\nApproval-gated wallets get the same pre-condition checks: a\n`would_succeed: true` with `message` \"Operation would require multisig\napproval\" means the op is executable once approvers sign.\n\nCross-chain results omit `estimated_fee`/`fee_token` — the bridge takes\nits cut out of the transferred amount rather than charging NEAR gas, so\nthe quote line in `message` is the cost estimate.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WithdrawRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Dry-run result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DryRunResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/intents/transfer": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "intentsTransfer",
        "summary": "Transfer inside NEAR Intents (to another account's intents balance)",
        "description": "Move a token balance from the wallet's `intents.near` balance to **another\naccount's** `intents.near` balance, staying **inside** the intents pool\n(the defuse `transfer` intent). Gasless via the solver relay — neither side\npays NEAR gas.\n\nThis is **not** a withdrawal: funds never leave `intents.near`. Use\n`POST /wallet/v1/intents/withdraw` to deliver to a plain on-chain account\ninstead. NEAR-only: there is no `chain` field, and `token` is **required**\n(to send NEAR, transfer `nep141:wrap.near`).\n\nThe recipient is credited inside `intents.near`, so the account need **not**\nexist on-chain (a 64-hex implicit OutLayer wallet is a valid recipient); only\nthe account-id format is validated.\n\nGated by the wallet policy exactly like withdraw (recipient whitelist +\nper-token amount limit). If the policy requires approval, returns\n`status=pending_approval` with an `approval_id`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntentsTransferRequest"
              },
              "examples": {
                "usdt": {
                  "summary": "Transfer USDT to another account's intents balance",
                  "value": {
                    "to": "partner.near",
                    "amount": "1000000",
                    "token": "nep141:usdt.tether-token.near"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transfer submitted (or queued for approval)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WithdrawResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/intents/swap": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "intentsSwap",
        "summary": "Swap via NEAR Intents (1Click)",
        "description": "Cross-chain swap via the 1Click solver. Quote → deposit to intents.near\n→ mt_transfer → poll. Returns when the swap settles or fails.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SwapRequest"
              },
              "example": {
                "token_in": "nep141:wrap.near",
                "token_out": "nep141:usdt.tether-token.near",
                "amount_in": "5000000000000000000000000",
                "min_amount_out": "24000000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Swap submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwapResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/intents/swap/quote": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "intentsSwapQuote",
        "summary": "Get a swap quote (no execution)",
        "description": "Returns the indicative output and deadline for a swap without executing\nit. Use this before calling `/intents/swap` to show the user expected\noutput and allow them to set `min_amount_out`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SwapRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwapQuoteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/evm/sign-typed-data": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "evmSignTypedData",
        "summary": "Sign EIP-712 typed data (EVM)",
        "description": "Sign an EIP-712 v4 typed-data object with the wallet's EVM (secp256k1)\nkey for `chain`. The digest is computed server-side from the full\ntyped-data object (no client-supplied hash is trusted); `ecrecover` over\nit returns the address from `GET /wallet/v1/address?chain=<evm>`. This is\nthe core trading primitive (signs CLOB orders). Off-chain — returns the\nsignature only, no broadcast. Gated by the `evm_sign` capability.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EvmSignTypedDataRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvmSignResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/evm/sign-message": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "evmSignMessage",
        "summary": "Sign an EIP-191 personal_sign message (EVM)",
        "description": "Sign an EIP-191 `personal_sign` message with the wallet's EVM key.\n`message` is `0x`-hex (signed as raw bytes) or a UTF-8 string. Used for\nvenue L1 auth (e.g. deriving a Polymarket CLOB API key). Gated by the\n`evm_sign` capability.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EvmSignMessageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvmSignResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/evm/sign-transaction": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "evmSignTransaction",
        "summary": "Sign a raw EVM transaction",
        "description": "Sign a raw EVM transaction with the wallet's EVM key. The caller supplies\nthe **serialized unsigned transaction** (`unsigned_tx`, e.g. viem\n`serializeTransaction(tx)` — `0x02‖rlp(...)` for EIP-1559); the service\nkeccak256-hashes it and returns the recoverable signature. It does NOT\nassemble the transaction, manage nonce/gas, or broadcast — the caller\nassembles the final signed tx and broadcasts it. For an EIP-1559 tx the\n`yParity` needed to assemble is `v - 27`. Gated by the **`evm_sign.raw_tx`**\nsub-capability (default-OFF, separate from base `evm_sign`).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EvmSignTransactionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvmSignResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/solana/sign-message": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "solanaSignMessage",
        "summary": "Sign an off-chain message (Solana)",
        "description": "Sign raw message bytes with the wallet's Solana (ed25519) key. The\ndecoded bytes are signed AS-IS (Solana convention — verifiable with\n`nacl.sign.detached.verify`; Sign-in-with-Solana flows work unchanged).\nBytes that parse as a valid Solana **transaction message** are rejected\n(HTTP 400): Solana has no EIP-191-style prefix separating messages from\ntransactions, so this guard (the same one Phantom/Solflare apply) keeps\nthe message endpoint from bypassing the `solana_sign.raw_tx`\nsub-capability. Off-chain — returns the signature only, no broadcast.\nGated by the `solana_sign` capability. The `sol` alias is accepted for\n`chain` and canonicalized — the response always echoes `solana`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SolanaSignMessageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SolanaSignResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/solana/sign-transaction": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "solanaSignTransaction",
        "summary": "Sign a Solana transaction message",
        "description": "Sign a Solana transaction with the wallet's Solana (ed25519) key. The\ncaller supplies the **serialized unsigned transaction message**\n(`unsigned_tx`, base64 — what the signature covers: web3.js\n`tx.serializeMessage()` / `versionedTx.message.serialize()`); the\nservice signs the bytes as-is and returns the base58 signature. It does\nNOT parse or assemble the transaction, pick a blockhash, or broadcast —\nthe caller assembles the signed tx\n(`compact-u16 signature count ‖ signatures ‖ message`) and broadcasts\nit. Gated by the **`solana_sign.raw_tx`** sub-capability (default-OFF,\nseparate from base `solana_sign`). The `sol` alias is accepted for\n`chain` and canonicalized — the response always echoes `solana`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SolanaSignTransactionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SolanaSignResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/confidential/shield": {
      "post": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialShield",
        "summary": "SHIELD — move public intents balance into the confidential shard",
        "description": "Moves the wallet's **public** `intents.near` balance into its\nconfidential balance (deposit-type `INTENTS` → recipient-type\n`CONFIDENTIAL_INTENTS`).\n\n**Privacy note:** SHIELD links the wallet to the confidential pool\n**on-chain** (an `execute_intents` call signed by the wallet, with the\npublic asset id visible). It is a convenience hop, not an unlinkable\noperation — for unlinkability fund the confidential balance via\n`confidentialDepositCrossChain` (cross-chain) instead. See CUSTODY docs.\n\nAsync: returns `status=pending_deposit` with a `request_id`; poll\n`GET /wallet/v1/requests/{id}` until terminal.\n\n**Legacy alias:** `POST /wallet/v1/confidential/deposit` still works and\nresolves to the same handler.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfidentialShieldRequest"
              },
              "example": {
                "token": "nep141:wrap.near",
                "amount": "10000000000000000000000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Shield submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfidentialOpResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/confidential/deposit": {
      "post": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialDeposit",
        "deprecated": true,
        "summary": "[Deprecated] SHIELD into the confidential shard (use /wallet/v1/confidential/shield)",
        "description": "**Deprecated legacy alias.** Use\n[`confidentialShield`](#tag/Confidential/operation/confidentialShield)\n(`POST /wallet/v1/confidential/shield`) instead. Retained for backward\ncompatibility; resolves to the same handler with identical\nrequest/response shapes.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfidentialShieldRequest"
              },
              "example": {
                "token": "nep141:wrap.near",
                "amount": "10000000000000000000000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Shield submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfidentialOpResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/confidential/unshield": {
      "post": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialUnshield",
        "summary": "UNSHIELD — move confidential balance back to public intents",
        "description": "Reverse of SHIELD (deposit-type `CONFIDENTIAL_INTENTS` → recipient-type\n`INTENTS`). Like SHIELD, this links the wallet on-chain (exit-to-public\nreveal). Async — poll `GET /wallet/v1/requests/{id}`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfidentialUnshieldRequest"
              },
              "example": {
                "token": "nep141:wrap.near",
                "amount": "10000000000000000000000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Unshield submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfidentialOpResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/confidential/withdraw": {
      "post": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialWithdraw",
        "summary": "Withdraw a confidential balance to an external chain",
        "description": "Moves a confidential balance out to a destination chain\n(`CONFIDENTIAL_INTENTS` → `DESTINATION_CHAIN`). The wallet's NEAR address\nnever appears on chain — only the destination-chain receiver does, on the\ndestination chain.\n\n`chain` must be either the token's **home chain** (e.g. `chain=zcash`\nfor `nep141:zec.omft.near`, delivering the native asset to a\ndestination-chain address) or `\"near\"`; any other combination is\nrejected with 400 — the destination is derived from the token, so a\nmismatched `chain` would describe a withdrawal that cannot happen.\n\n`chain=\"near\"` delivers to the named NEAR account. For NEAR-native\ntokens (`nep141:wrap.near` → **native NEAR** via a `native_withdraw`\nintent on `intents.near`, verified live: settlement\n`FVzan8XRMwHYPe2hgX4GffwG3bdndxETWb2FKVFzFdur`). For omft bridge\nassets (e.g. `nep141:zec.omft.near`) the NEP-141 representation is\ndelivered **on NEAR** (`1cs_v1:near:<asset>` destination) — the funds\nstay bridged instead of being withdrawn to the token's home chain. To\nsend funds back to your **own** public intents balance use\n`confidentialUnshield` (different semantics — self vs. external\nrecipient).\n\n`token` is **required** — the source confidential asset to deliver on the\ndestination chain. The same per-chain whitelist / spending-limit policy\nas `intentsWithdraw` is enforced. Async — poll\n`GET /wallet/v1/requests/{id}`.\n\n**Delivery to a NEAR account is a direct NEP-141 `ft_transfer`** to the\nrecipient (not an intents-balance credit). The recipient does NOT need\nprior storage registration on the token contract: 1Click checks it and,\nfor an unregistered recipient, registers the storage itself, netting\nits cost out of `amount_out` (observable in the dry-run quote — an\nunregistered recipient is quoted slightly less than a registered one).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfidentialWithdrawRequest"
              },
              "example": {
                "chain": "solana",
                "to": "Esf7JS1sM46vEHtk75ik7N7UD5FJ13TXVKGWEd2kksiw",
                "amount": "500000",
                "token": "nep141:sol-5ce3bf3a31af18be40ba30f721101b4341690186.omft.near"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Withdraw submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfidentialOpResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/confidential/withdraw/dry-run": {
      "post": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialWithdrawDryRun",
        "summary": "Quote a confidential withdraw (no execution)",
        "description": "Returns the indicative output for a confidential withdraw without\nsigning or submitting. Read-only.\n\nApplies the same gates as the real confidential withdraw, in the same\norder: `to` is required (400 without it), then the policy decision over\nthe same canonical op (403 when frozen or denied), then the upstream\nquote. A wallet whose policy requires approval still gets its quote —\nthe op is executable once approvers sign.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfidentialWithdrawRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwapQuoteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/confidential/transfer": {
      "post": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialTransfer",
        "summary": "Private transfer inside the confidential shard",
        "description": "Transfers a confidential balance to another NEAR account, fully inside\nthe confidential shard (`CONFIDENTIAL_INTENTS` → `CONFIDENTIAL_INTENTS`).\nSettles on the private shard, so it leaves no public-chain trace.\nNEAR-only context — no `chain` field; `token`\nrequired. Async — poll `GET /wallet/v1/requests/{id}`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfidentialTransferRequest"
              },
              "example": {
                "to": "950c134ec86a21a8525d16d1dbae79258b923cabdaa8d32da284d931f74bdcb2",
                "amount": "1000000",
                "token": "nep141:wrap.near"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transfer submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfidentialOpResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/confidential/swap": {
      "post": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialSwap",
        "summary": "Swap between two assets inside the confidential shard",
        "description": "Confidential swap with distinct `token_in`/`token_out`, both settled\ninside the confidential shard. Settles on the private shard, so it leaves\nno public-chain trace. Async — poll\n`GET /wallet/v1/requests/{id}`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfidentialSwapRequest"
              },
              "example": {
                "token_in": "nep141:wrap.near",
                "token_out": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
                "amount_in": "5000000000000000000000000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Swap submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfidentialOpResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/confidential/swap/quote": {
      "post": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialSwapQuote",
        "summary": "Quote a confidential swap (no execution)",
        "description": "Read-only price preview for a confidential swap.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfidentialSwapRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwapQuoteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/confidential/deposit/cross-chain": {
      "post": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialDepositCrossChain",
        "summary": "Cross-chain deposit into the confidential shard (quote only)",
        "description": "Requests a one-time deposit address to fund the confidential balance\nfrom an external chain (`ORIGIN_CHAIN` → `CONFIDENTIAL_INTENTS`) via\n1Click / NEAR Intents. The caller sends `amount` of the source asset to\nthe returned `deposit_address` out-of-band; the solver delivers it and\ncredits the confidential balance. **The wallet's NEAR address never\ntouches the public side** — this is the most private way to fund a\nconfidential balance. Poll `GET /wallet/v1/requests/{id}` for status.\n\n**Legacy alias:** `POST /wallet/v1/confidential/deposit-intent` still\nworks and resolves to the same handler.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfidentialDepositIntentRequest"
              },
              "example": {
                "source_asset": "nep141:sol-5ce3bf3a31af18be40ba30f721101b4341690186.omft.near",
                "amount": "500000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deposit address issued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfidentialDepositIntentResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/confidential/deposit-intent": {
      "post": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialDepositIntent",
        "deprecated": true,
        "summary": "[Deprecated] Cross-chain deposit into the confidential shard (use /wallet/v1/confidential/deposit/cross-chain)",
        "description": "**Deprecated legacy alias.** Use\n[`confidentialDepositCrossChain`](#tag/Confidential/operation/confidentialDepositCrossChain)\n(`POST /wallet/v1/confidential/deposit/cross-chain`) instead. Retained\nfor backward compatibility; resolves to the same handler with identical\nrequest/response shapes.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfidentialDepositIntentRequest"
              },
              "example": {
                "source_asset": "nep141:sol-5ce3bf3a31af18be40ba30f721101b4341690186.omft.near",
                "amount": "500000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deposit address issued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfidentialDepositIntentResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/confidential/balance": {
      "get": {
        "tags": [
          "Confidential"
        ],
        "operationId": "confidentialBalance",
        "summary": "Read confidential balances",
        "description": "Returns the wallet's confidential balances from the private shard\n(`intents.far`), which has no public RPC of its own. With `?token=`\nreturns a single asset; without it returns the\nfull list. Accepts both `wrap.near` and `nep141:wrap.near` forms.\n",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Defuse asset id to filter to (e.g. `nep141:wrap.near`). Omit for all."
          }
        ],
        "responses": {
          "200": {
            "description": "Confidential balance(s)",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ConfidentialBalanceResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ConfidentialBalancesResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/intents/deposit/cross-chain": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "intentsDepositCrossChain",
        "summary": "Create a cross-chain deposit (via 1Click / NEAR Intents)",
        "description": "Requests a one-time deposit address on a source chain via NEAR Intents\n1Click. The caller sends `amount` of the source token to the returned\n`deposit_address`; the solver delivers it and credits the wallet's\nintents.near balance. Poll `getCrossChainDepositStatus` until `success`.\n\nThis is a **cross-chain deposit via 1Click / NEAR Intents** — the\nwallet's NEAR address receives the intents.near credit once the solver\nsettles the source-chain deposit.\n\nTwo request shapes are accepted (see\n[`DepositIntentRequest`](#/components/schemas/DepositIntentRequest)):\n\n- **By asset id (preferred)** — `{ source_asset, destination_asset?,\n  amount }`. The source chain is derived from the\n  [`defuse_asset_id`](#/components/schemas/TokenInfo) prefix. This is\n  the shape returned by `listTokens` and used by the dashboard.\n- **By chain + symbol (legacy)** — `{ chain, token?, amount }`. The\n  coordinator resolves `(chain, symbol)` to an asset id via the\n  1Click token catalog.\n\nIf both shapes are present and disagree, `source_asset` wins and the\ncoordinator logs a warning. `near` is a valid source chain — a\nNEAR-origin deposit returns a 64-char hex implicit account.\n\nAmounts are in the token's smallest unit. There's a small solver fee,\nso `amount_out` < `amount`.\n\nThe returned `deposit_address` is chain-specific — see the\n[`DepositIntentResponse.deposit_address`](#/components/schemas/DepositIntentResponse)\ndescription for per-chain address formats.\n\n**Legacy alias:** `POST /wallet/v1/deposit-intent` still works and\nresolves to the same handler.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DepositIntentRequest"
              },
              "examples": {
                "bySourceAsset": {
                  "summary": "USDC on Base → NEAR USDC (preferred shape)",
                  "value": {
                    "source_asset": "nep141:base-0x833589fcd6edb6e08f4c7c32d4f71b54bda02913.omft.near",
                    "destination_asset": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
                    "amount": "5000000"
                  }
                },
                "byChainAndToken": {
                  "summary": "USDC on Ethereum (legacy shape)",
                  "value": {
                    "chain": "ethereum",
                    "token": "USDC",
                    "amount": "5000000"
                  }
                },
                "nearNative": {
                  "summary": "NEAR-origin deposit (returns a NEAR implicit account)",
                  "value": {
                    "source_asset": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
                    "amount": "5000000"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deposit intent created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DepositIntentResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/deposit-intent": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "createDepositIntent",
        "deprecated": true,
        "summary": "[Deprecated] Create a cross-chain deposit (use /wallet/v1/intents/deposit/cross-chain)",
        "description": "**Deprecated legacy alias.** Use\n[`intentsDepositCrossChain`](#tag/Wallet/operation/intentsDepositCrossChain)\n(`POST /wallet/v1/intents/deposit/cross-chain`) instead. This path is\nretained for backward compatibility and resolves to the same handler\nwith identical request/response shapes — existing clients continue to\nwork unchanged.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DepositIntentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deposit intent created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DepositIntentResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/intents/deposit/cross-chain/status": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "getCrossChainDepositStatus",
        "summary": "Poll a cross-chain deposit's status",
        "description": "Returns the live status of a cross-chain deposit (from\n`intentsDepositCrossChain`), lazily refreshing it against 1Click on each\ncall.\n\n**Legacy alias:** `GET /wallet/v1/deposit-status` still works and\nresolves to the same handler.\n",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The `intent_id` from `intentsDepositCrossChain`."
          }
        ],
        "responses": {
          "200": {
            "description": "Deposit status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DepositStatusResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/deposit-status": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "getDepositStatus",
        "deprecated": true,
        "summary": "[Deprecated] Poll a cross-chain deposit's status (use /wallet/v1/intents/deposit/cross-chain/status)",
        "description": "**Deprecated legacy alias.** Use\n[`getCrossChainDepositStatus`](#tag/Wallet/operation/getCrossChainDepositStatus)\n(`GET /wallet/v1/intents/deposit/cross-chain/status`) instead. Retained\nfor backward compatibility; resolves to the same handler.\n",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The `intent_id` from the cross-chain deposit call."
          }
        ],
        "responses": {
          "200": {
            "description": "Deposit status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DepositStatusResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/intents/deposit/cross-chain/list": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "listCrossChainDeposits",
        "summary": "List this wallet's cross-chain deposits",
        "description": "Returns the wallet's cross-chain deposits (from\n`intentsDepositCrossChain`), most recent first. Each entry has the same\nshape as `getCrossChainDepositStatus`, but the list endpoint does NOT\nlazily refresh in-flight deposits against 1Click — statuses are read\nstraight from storage as last persisted. Poll\n`getCrossChainDepositStatus` for a single deposit when you need its live\nstatus.\n\n**Legacy alias:** `GET /wallet/v1/deposits` still works and resolves to\nthe same handler.\n",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            },
            "description": "Max rows to return (server caps at 100). Defaults to 20."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Number of rows to skip for pagination."
          }
        ],
        "responses": {
          "200": {
            "description": "Cross-chain deposits (most recent first)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DepositStatusResponse"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/deposits": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "listDeposits",
        "deprecated": true,
        "summary": "[Deprecated] List cross-chain deposits (use /wallet/v1/intents/deposit/cross-chain/list)",
        "description": "**Deprecated legacy alias.** Use\n[`listCrossChainDeposits`](#tag/Wallet/operation/listCrossChainDeposits)\n(`GET /wallet/v1/intents/deposit/cross-chain/list`) instead. Retained\nfor backward compatibility; resolves to the same handler.\n",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            },
            "description": "Max rows to return (server caps at 100). Defaults to 20."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Number of rows to skip for pagination."
          }
        ],
        "responses": {
          "200": {
            "description": "Cross-chain deposits (most recent first)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DepositStatusResponse"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/payment-check/create": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "createPaymentCheck",
        "summary": "Create a payment check (bearer-claimable transfer)",
        "description": "Creates a \"payment check\": the wallet's intents balance is moved to a\nfresh ephemeral intents account (gaslessly, via the solver relay), and\nthe ephemeral account's private key is returned as `check_key`. Whoever\nholds `check_key` can `claimPaymentCheck` the funds; the creator can\n`reclaimPaymentCheck` them back. Fund the wallet's intents balance first\nvia `intentsDeposit`.\n\n**Security model:** this is NOT gated by multisig. The keystore signs the\ntransfer-to-ephemeral only when the wallet policy grants the default-DENY\n`payment_check` capability and the per-transaction amount is within the\ncapability's per-token amount cap. `claimPaymentCheck` / `reclaimPaymentCheck`\nare signed by the ephemeral `check_key` itself (NEP-413), never by the\nkeystore.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentCheckCreateRequest"
              },
              "example": {
                "token": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
                "amount": "1000000",
                "memo": "coffee",
                "expires_in": 86400
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Payment check created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentCheckCreateResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/payment-check/batch-create": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "batchCreatePaymentChecks",
        "summary": "Create up to 10 payment checks in one call",
        "description": "Creates between 1 and 10 payment checks in a single request, each backed\nby its own ephemeral intents account. The wallet's intents balance must\ncover the sum per token across the batch.\n\n**Security model:** identical to `createPaymentCheck` — gated by the\ndefault-DENY `payment_check` capability and its per-transaction amount cap\n(NOT multisig); each check's claim/reclaim is signed by that check's\nephemeral key, not the keystore.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentCheckBatchCreateRequest"
              },
              "example": {
                "checks": [
                  {
                    "token": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
                    "amount": "1000000",
                    "memo": "invoice-1"
                  },
                  {
                    "token": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
                    "amount": "2000000"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Payment checks created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentCheckBatchCreateResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/payment-check/claim": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "claimPaymentCheck",
        "summary": "Claim a payment check",
        "description": "Claims the funds held by a payment check into the authenticated wallet's\nintents balance. Signed by the ephemeral `check_key` (NEP-413), so the\ncaller need only hold the key. Omit `amount` for a full claim, or pass a\npartial amount (minimal units) to claim part and leave the remainder\nclaimable. Expired or already-settled checks are rejected.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentCheckClaimRequest"
              },
              "example": {
                "check_key": "9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Claim settled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentCheckClaimResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/payment-check/reclaim": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "reclaimPaymentCheck",
        "summary": "Reclaim an unclaimed payment check",
        "description": "Returns the (remaining) funds of a payment check the wallet created back\nto the wallet's own intents balance — used to cancel/expire a check that\nwas never claimed. Identified by `check_id` and scoped to the creating\nwallet. Signed by the ephemeral check key (re-derived inside the\nkeystore), not the keystore's wallet key. Omit `amount` for a full\nreclaim or pass a partial amount.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentCheckReclaimRequest"
              },
              "example": {
                "check_id": "3a2b1c0d-4e5f-6a7b-8c9d-0e1f2a3b4c5d"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reclaim settled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentCheckReclaimResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/payment-check/status": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "getPaymentCheckStatus",
        "summary": "Get a payment check's status",
        "description": "Returns the full lifecycle state of a payment check the wallet created,\nidentified by `check_id`. `status` is normalized to `expired` when an\n`unclaimed` check is past its `expires_at`.\n",
        "parameters": [
          {
            "name": "check_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The `check_id` returned by `createPaymentCheck`."
          }
        ],
        "responses": {
          "200": {
            "description": "Payment check status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentCheckStatusResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/payment-check/list": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "operationId": "listPaymentChecks",
        "summary": "List the wallet's payment checks",
        "description": "Lists payment checks created by the authenticated wallet, most recent\nfirst. Optionally filter by `status` (including the virtual `expired`\nstatus: unclaimed checks past their `expires_at`).\n",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional status filter. Accepts stored statuses (`unclaimed`, `claimed`, `partially_claimed`, `reclaimed`, `partially_reclaimed`) plus the virtual `expired` (unclaimed + past expiry)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 100
            },
            "description": "Max rows to return (server caps at 100). Defaults to 50."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Number of rows to skip for pagination."
          }
        ],
        "responses": {
          "200": {
            "description": "Payment checks (most recent first)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentCheckListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/payment-check/peek": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "peekPaymentCheck",
        "summary": "Inspect a payment check by its key (without claiming)",
        "description": "Reads a payment check's on-chain balance and metadata using its\n`check_key`, without moving any funds. Use this before\n`claimPaymentCheck` to see what a key is worth. Returns the live\non-chain `balance` of the ephemeral account plus the stored `token` /\n`memo` / `status` / `expires_at`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentCheckPeekRequest"
              },
              "example": {
                "check_key": "9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Payment check snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentCheckPeekResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/sign-message": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "signMessage",
        "summary": "Sign a generic NEP-413 message",
        "description": "Signs a NEP-413 message with the wallet's NEAR key (e.g. dApp login).\nGated by the `sign_message` capability: `allowed_recipients` is a\ndefault-DENY allowlist of verifier recipients, and `intents.near` /\n`intents.far` are always excluded. Policy and freeze still apply — a\nfrozen wallet cannot sign.\n\n**`format` is now NEP-413 only.** `format=raw` is **rejected** with\n`400 bad_request` — OutLayer NEAR-key auth (the old raw-ed25519\n`auth:`/`register:`/`api-key:` token) moved to\n[`POST /wallet/v1/auth-sign`](#tag/Wallet/operation/authSign), which\nbuilds the challenge with a fresh server timestamp. This is NOT OutLayer\nauth signing.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignMessageRequest"
              },
              "example": {
                "message": "login:myapp:1716200000",
                "recipient": "myapp.example",
                "format": "nep413"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignMessageResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/auth-sign": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "operationId": "authSign",
        "summary": "Sign an OutLayer NEAR-key auth challenge (Bearer / register / api-key)",
        "description": "Produces the OutLayer authentication signature used to construct a\n`Bearer near:` token, or to authenticate `POST /register` /\n`PUT /wallet/v1/api-key` from a deterministic wallet's own NEAR key.\n\nThe keystore **builds** the exact challenge string\n`<prefix>:<seed>:<ts>` (where `prefix` is `auth` for `bearer`, else the\npurpose) with a **fresh server timestamp** and signs it raw ed25519. The\nclient does NOT supply or control the timestamp — the fresh `ts` is what\nthe coordinator's verifier requires. Send the returned `auth_message`\nverbatim.\n\nThis is the replacement for the old\n`POST /wallet/v1/sign-message {format:\"raw\"}`. It maps to an always-allowed,\nnon-fund `Op::Auth` (domain-separated from a tx hash) — no capability and\nno multisig gate it, but a frozen wallet still cannot sign.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthSignRequest"
              },
              "examples": {
                "bearer": {
                  "summary": "Bearer-near auth token",
                  "value": {
                    "purpose": "bearer",
                    "seed": "my-app-session"
                  }
                },
                "register": {
                  "summary": "Authenticate POST /register",
                  "value": {
                    "purpose": "register",
                    "seed": "my-app-session"
                  }
                },
                "apiKey": {
                  "summary": "Authenticate PUT /api-key",
                  "value": {
                    "purpose": "api-key",
                    "seed": "my-app-session"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Auth signature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthSignResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/wallet/v1/requests": {
      "get": {
        "tags": [
          "Requests"
        ],
        "operationId": "listRequests",
        "summary": "List async wallet requests",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/RequestType"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/RequestStatus"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Request list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RequestListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/requests/{id}": {
      "get": {
        "tags": [
          "Requests"
        ],
        "operationId": "getRequest",
        "summary": "Get a single request's status",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Request status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RequestStatusResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/policy": {
      "get": {
        "tags": [
          "Policy"
        ],
        "operationId": "getPolicy",
        "summary": "Get current decrypted policy",
        "description": "Returns the wallet's current policy (decrypted by the keystore TEE),\nplus the current usage counters used by velocity-limit checks.\n",
        "responses": {
          "200": {
            "description": "Current policy + usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/encrypt-policy": {
      "post": {
        "tags": [
          "Policy"
        ],
        "operationId": "encryptPolicy",
        "summary": "Encrypt a policy for on-chain storage",
        "description": "Sends a policy JSON to the keystore TEE for encryption. The returned\n`encrypted_base64` is what gets stored in NEAR via\n`store_wallet_policy(...)` after the keystore signs it.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EncryptPolicyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Encrypted policy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EncryptPolicyResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/sign-policy": {
      "post": {
        "tags": [
          "Policy"
        ],
        "operationId": "signPolicy",
        "summary": "Sign an encrypted policy (for on-chain submission)",
        "description": "The keystore signs the message the contract rebuilds —\n`store_wallet_policy:v1:{wallet_pubkey}:{len}:{encrypted_data}:{caller}` —\nwith the wallet's key. The signature + encrypted data + public key form\nthe payload for the on-chain `store_wallet_policy(...)` call.\n\n`caller` is the account that will SEND that call, and it is signed: the\nanswer works for that account and no other. Sending the prepared payload\nfrom anywhere else is refused on chain.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignPolicyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signature",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignPolicyResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/invalidate-cache": {
      "post": {
        "tags": [
          "Policy"
        ],
        "operationId": "invalidatePolicyCache",
        "summary": "Invalidate negative policy cache",
        "description": "Forces the coordinator to drop its cached `no_policy` flag for a wallet.\nCall this after submitting `store_wallet_policy(...)` on-chain so that\nsubsequent operations pick up the new policy without waiting for the\n5-minute TTL to expire.\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvalidateCacheRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cache cleared",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/pending_approvals": {
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "listPendingApprovals",
        "summary": "List pending multisig approvals",
        "responses": {
          "200": {
            "description": "Pending approvals",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PendingApprovalsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/approval/{id}": {
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "getApprovalDetail",
        "summary": "Get a single pending approval's detail (public, read-only)",
        "description": "Returns non-sensitive detail for one pending approval — used by the\ndashboard approval-detail page. No auth: rate-limited by IP, returns only\npublic metadata. The `wallet_pubkey` and `request_hash` are the values an\napprover binds into the NEP-413 vote message\n(`approve:{id}:{wallet_pubkey}:{request_hash}` /\n`reject:{id}:{wallet_pubkey}:{request_hash}`); the dashboard renders `op`.\n",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Approval detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApprovalDetail"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/approve/{id}": {
      "post": {
        "tags": [
          "Approvals"
        ],
        "operationId": "approveRequest",
        "summary": "Approve a pending action (NEP-413 signed)",
        "description": "Submits an approver's NEP-413 signature for a pending action. When the\napproval threshold is reached, the action auto-executes. No API key\nrequired — auth is established by the NEP-413 signature in the body.\n",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Nep413Auth"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Approval recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApproveResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/reject/{id}": {
      "post": {
        "tags": [
          "Approvals"
        ],
        "operationId": "rejectRequest",
        "summary": "Reject a pending action (NEP-413 signed)",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RejectRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reject vote recorded (request cancelled if from a real approver)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RejectResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/wallet/v1/audit": {
      "get": {
        "tags": [
          "Audit"
        ],
        "operationId": "listAuditEvents",
        "summary": "Event history for the wallet",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Event list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuditResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/call/{owner}/{project}": {
      "post": {
        "tags": [
          "Execution"
        ],
        "operationId": "callProject",
        "summary": "Run a project, or a connector",
        "description": "One route for every project. A **connector** is an ordinary project we\ncurated and priced; what being one adds is decided from the project id,\nnot from the way in.\n\nA connector call MUST name its operation as a top-level `operation`\nstring inside `input`. The same bytes are read by four parties — the\ncontract prices the call, the coordinator bills it, the worker refuses\nto run without it, and the guest dispatches on it — so there is one\nvalue and nothing to keep in step. Absent, blank, non-string, nested, or\nspelled `op` are all refused before anything runs, and an operation with\nno on-chain price is refused too: unpriced is not free.\n\n**Waiting.** Synchronous by default: the connection is held until the\nrun finishes or the server's window elapses. Send `\"async\": true` for\nanything longer — it returns at once with `call_id` and `poll_url`, and\nthat path has no window at all. A synchronous call that outruns the\nwindow is settled, not suspended: `408`, charged for the compute\nauthorised and never for the operation. Do not retry it — the original\nmay still be running and will be charged.\n",
        "security": [
          {
            "PaymentKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "owner",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Project owner account, e.g. `connectors.outlayer.near`."
          },
          {
            "name": "project",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Use-Owner-Secret",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1",
                "true"
              ]
            },
            "description": "Fetch the secret stored for this agent and inject it into the run.\nNothing is looked up unless the call asks. Only meaningful for a key\nowned by a custody wallet; an ordinary payment key addresses its own\nsecrets through the body.\n"
          },
          {
            "name": "X-Attached-Deposit",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Stablecoin minimal units paid to the project's owner. Refused for connectors."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectCallRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The run finished, or was accepted for polling when `async`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectCallResponse"
                }
              }
            }
          },
          "401": {
            "description": "No payment credential, or one that cannot pay. `reason` tells the two apart: `missing_payment_key` for a caller that sent nothing, and `wk_is_not_a_payer` for one that sent a `wk_` — that credential names a wallet on /wallet/v1/*, it does not buy anything here.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallRefusal"
                }
              }
            }
          },
          "402": {
            "description": "The key cannot pay for this call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallRefusal"
                }
              }
            }
          },
          "403": {
            "description": "The key's scope does not include this project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallRefusal"
                }
              }
            }
          },
          "408": {
            "description": "The call did not finish inside the synchronous window and has been\nsettled as failed. Terminal — send it as `async` and poll instead.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallTimedOut"
                }
              }
            }
          },
          "409": {
            "description": "`no_bound_identity` — the call asked to run as a bound account with\n`use_bound_identity`, and there is none to run as: either the paying\nkey names no wallet (`terminal: true`), or the wallet has no ACTIVE\nbinding (`terminal: false` — a pending or revoked binding authorizes\nnothing, and one that activates later makes the same call work).\n\nRefused before the call is priced or charged, and never answered by\nrunning the job under the caller's own name: which account the guest\nIS decides what a connector does with it.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallRefusal"
                }
              }
            }
          },
          "429": {
            "description": "Three different refusals share this status, and a caller tells them\napart by `reason` rather than by the code:\n\n* `call_already_in_flight` — an ALLOWANCE runs one call at a time.\n  **`terminal: false`**: it clears by itself when the call in flight\n  finishes, so the move is to wait, or to fund the key (money is not\n  limited this way, and a funded key answers both at once);\n* `connector_quota_exceeded` — the wallet's daily connector quota,\n  which widens with the wallet's age and is independent of paying;\n* a plain rate limit, with no `reason`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "call_already_in_flight",
                        "connector_quota_exceeded"
                      ]
                    },
                    "terminal": {
                      "type": "boolean",
                      "description": "False means waiting clears it; true means it will not."
                    },
                    "used": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "`upstream_unavailable` — something this call depends on has not\ncaught up yet, and the same call works later. `keystore_error` —\nthe TEE keystore was unreachable or refused to sign. Both are\ntransient and both are sent with a `Retry-After` interval.\n\nDeliberately not `service_unavailable`: on `/wallet/v1/*` that code\nmeans a feature this deployment does not offer and carries no\ninterval, and one product must not use one code for \"come back\" and\n\"never come back\".\n",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallRefusal"
                }
              }
            }
          }
        }
      }
    },
    "/calls/{call_id}": {
      "get": {
        "tags": [
          "Execution"
        ],
        "operationId": "getCallResult",
        "summary": "Poll an asynchronous call",
        "security": [
          {
            "PaymentKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "call_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call's current state. `status` is `pending` until it settles.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectCallResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/wallet/v1/create-payment-key": {
      "post": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "createPaymentKey",
        "summary": "Create a payment key for this wallet",
        "description": "Two transactions on chain: the key's secret is stored, then funded. An\n**agent** key is keyless — it is named after the wallet itself, so the\nresponse carries no `payment_key` to hand out, and a wallet may have\nexactly one.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePaymentKeyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePaymentKeyResponse"
                }
              }
            }
          },
          "400": {
            "description": "The amount is not one an account can hold."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The wallet holds less than the deposit asks for. The message names\nwhat it holds, what is needed, and where to send it.\n"
          },
          "503": {
            "description": "The wallet's balance could not be read, so nothing was attempted and\nnothing was charged. Transient — retry.\n"
          }
        }
      }
    },
    "/wallet/v1/subscription/purchase-info": {
      "get": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "subscriptionPurchaseInfo",
        "summary": "What is on sale, and what this wallet needs to buy it",
        "responses": {
          "200": {
            "description": "The plans on chain joined with their terms. An empty list means one\nhalf is missing — a plan is sellable only when both exist.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/subscription/status": {
      "get": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "subscriptionStatus",
        "summary": "A key's allowance, balance, and the connectors in its scope",
        "security": [
          {
            "PaymentKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "`allowance_*` describe a subscription; `balance` and `withdrawable`\ndescribe money. A call covered by an allowance costs the caller\nnothing at the moment of the call — and pays the connector's author\nnothing either.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionStatus"
                }
              }
            }
          },
          "401": {
            "description": "No credential, or — with a `wk_` — a wallet that has no agent key\nyet, which answers `Missing X-Payment-Key header`. That is the reply\nto \"which key of yours should I report on\", not a rejected `wk_`:\ncreate the agent key with `create-payment-key {\"agent\": true}`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/subscription/purchase": {
      "post": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "purchaseAllowance",
        "summary": "Turn balance already on a key into allowance",
        "description": "Buys a plan out of the key's own balance. Needs a key STRING, so it is\nfor ordinary payment keys: an agent's key has none — it is named after\nthe wallet — and an agent's subscription is bought on chain instead,\nwith an `ft_transfer_call` carrying\n`{\"action\":\"buy_subscription\",\"nonce\":N,\"owner\":\"<agent>\",\"plan\":0}`.\n\nOnly the plan's PRICE is spent; an overpayment stays on the key as\nbalance. The allowance ADDS to whatever is there and validity extends\nfrom `max(now, expires_at)`, so buying again never shortens what is\nalready paid for.\n\nA `wk_` is deliberately NOT accepted here: it is a read credential, and\nspending on the owner's behalf is not something a compromised agent\nshould be able to do.\n",
        "security": [
          {
            "PaymentKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount_usd"
                ],
                "properties": {
                  "amount_usd": {
                    "type": "string",
                    "description": "Stablecoin minimal units, as an integer string. `10000000`\nis $10.00.\n"
                  },
                  "plan": {
                    "type": "integer",
                    "description": "Which plan, by index — the same identifier an on-chain\npurchase names. Absent means the cheapest on sale. A payment\nshort of the named plan buys the best plan it does cover.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The plan sold, which is not always the one asked for.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plan": {
                      "type": "string"
                    },
                    "allowance_purchased_usd": {
                      "type": "string"
                    },
                    "allowance_total_usd": {
                      "type": "string"
                    },
                    "spent_usd": {
                      "type": "string",
                      "description": "The PLAN'S price, not the whole `amount_usd`."
                    },
                    "expires_at": {
                      "type": "string"
                    },
                    "days_added": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The key's balance does not cover the cheapest plan on sale."
          }
        }
      }
    },
    "/subscription/notifications": {
      "put": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "setSubscriptionNotifications",
        "summary": "Where to warn the owner about the allowance and the expiry",
        "description": "Either channel, or both; `null` clears one. Never defaulted to an\naddress the owner did not choose — a warning nobody reads is worse than\nno warning.\n\nPERSONAL DATA: the address is stored against the key.\n",
        "security": [
          {
            "PaymentKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "webhook_url": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What is stored now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "webhook_url": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/trial-key": {
      "post": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "claimTrialKey",
        "summary": "Claim this wallet's one trial key",
        "description": "A real payment key, granted rather than sold, and spent exactly like any\nother — same header, same balance endpoint, same refusals.\n\nWhat makes it a trial: its value is an ALLOWANCE (never withdrawable, it\nends at its expiry), it is a GRANT (it cannot pay a developer through\n`X-Attached-Deposit`), and it is SCOPED to the curated connector\nnamespace, so it cannot run arbitrary code at our expense. It carries no\nwallet, so a trial call gets no custody host functions.\n\nOne per wallet, claimable within a window after the wallet is created;\nthe window, the days and the allowance are operator settings, and the\nanswer states the ones it granted. There is also a per-IP cap.\n\n**The key string is shown once and cannot be re-issued.**\n",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The claimed key and its terms.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payment_key": {
                      "type": "string",
                      "description": "The whole `X-Payment-Key` header value. Shown once."
                    },
                    "owner": {
                      "type": "string"
                    },
                    "nonce": {
                      "type": "integer"
                    },
                    "allowance_usd": {
                      "type": "string"
                    },
                    "days": {
                      "type": "integer"
                    },
                    "project_ids": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Anything else is refused as `project_not_allowed`."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "Already claimed, or claimed too late for this wallet's age."
          }
        }
      }
    },
    "/public/payment-keys/{owner}/{nonce}/balance": {
      "get": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "publicPaymentKeyBalance",
        "summary": "What a key has spent and what is left, without presenting it",
        "description": "Addressed by owner and nonce rather than by the key itself, so a\ndashboard can show a key's balance without holding its string. It\nreports MONEY only — allowance and expiry need the key, through\n`/subscription/status`.\n",
        "security": [],
        "parameters": [
          {
            "name": "owner",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "nonce",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Money on the key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "owner": {
                      "type": "string"
                    },
                    "nonce": {
                      "type": "integer"
                    },
                    "initial_balance": {
                      "type": "string"
                    },
                    "spent": {
                      "type": "string"
                    },
                    "reserved": {
                      "type": "string"
                    },
                    "available": {
                      "type": "string"
                    },
                    "last_used_at": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such key."
          }
        }
      }
    },
    "/wallet/v1/agent-secret/pubkey": {
      "get": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "agentSecretPubkey",
        "summary": "The key to seal a secret for this agent",
        "description": "Encrypt on your own machine and send back only ciphertext — the\ncredential never exists in this process. The answer names the seed and\nthe agent so a mismatch is visible rather than silent.\n\nGive exactly ONE of `project_id` and `wasm_hash`. A project secret is\nreadable by every version of that project; a `wasm_hash` secret is\nreadable only by that exact build, and a rebuild that changes a byte\ncannot open it. They seal to different seeds, so the choice made here\nmust be the one made when storing.\n",
        "parameters": [
          {
            "name": "project_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wasm_hash",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "X25519 public key (hex), encrypt-only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentSecretPubkey"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/wallet/v1/agent-secret": {
      "post": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "storeAgentSecret",
        "summary": "Store a secret for this agent, the agent's wallet paying",
        "description": "The agent's own wallet signs and stakes the storage, so it needs about\n0.11 NEAR. With none, the answer says so and names the endpoint that\npays instead.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "encrypted_secrets_base64"
                ],
                "properties": {
                  "project_id": {
                    "type": "string",
                    "description": "Exactly one of this and `wasm_hash`."
                  },
                  "wasm_hash": {
                    "type": "string",
                    "description": "One exact build instead of a project."
                  },
                  "encrypted_secrets_base64": {
                    "type": "string",
                    "description": "Sealed to the key from `/agent-secret/pubkey`. Never a plaintext secret."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tx_hash": {
                      "type": "string"
                    },
                    "agent_account": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The agent's wallet cannot pay the storage deposit."
          }
        }
      }
    },
    "/wallet/v1/agent-secret/prepare": {
      "post": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "prepareAgentSecret",
        "summary": "The same store, as a call for the payer to send",
        "description": "The keystore only signs; the call comes back for `payer` to send from\ntheir own account, so the agent's wallet needs no NEAR at all. The\nsignature binds every argument that decides what is stored, who may read\nit and who pays — including the payer, so handing the prepared call to\nsomebody else is a refusal rather than a shortcut.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "encrypted_secrets_base64",
                  "payer"
                ],
                "properties": {
                  "project_id": {
                    "type": "string",
                    "description": "Exactly one of this and `wasm_hash`."
                  },
                  "wasm_hash": {
                    "type": "string",
                    "description": "One exact build instead of a project."
                  },
                  "encrypted_secrets_base64": {
                    "type": "string"
                  },
                  "payer": {
                    "type": "string",
                    "description": "The account that will send the transaction and stake the storage."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A `store_agent_secret` call, complete with the wallet's signature.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreparedAgentSecret"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "description": "The ciphertext did not decrypt under this agent's seed, so nothing\nwas signed. Encrypt with the key from `/agent-secret/pubkey` for\nthis exact project.\n"
          }
        }
      }
    },
    "/wallet/v1/agent-secret/delete/prepare": {
      "post": {
        "tags": [
          "Subscriptions"
        ],
        "operationId": "prepareAgentSecretDelete",
        "summary": "Remove the agent's secret, as a call for the payer to send",
        "description": "The mirror of `/agent-secret/prepare`: the keystore signs, `payer` sends\nthe call from their own account, and the storage deposit goes back to\nwhoever sends it. The agent needs no NEAR to be forgotten.\n\nAuthority is the `wk_`, not the NEAR account paying. The agent's key\nnever moves — it IS the agent's account — so a rotated credential still\nspeaks for the agent, and a wallet whose seed is lost cannot delete its\nsecrets at all.\n\nSigned under its OWN domain, so a signature obtained to store a secret\ncannot be presented to destroy one.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "payer"
                ],
                "properties": {
                  "project_id": {
                    "type": "string",
                    "description": "Exactly one of this and `wasm_hash`."
                  },
                  "wasm_hash": {
                    "type": "string",
                    "description": "One exact build instead of a project."
                  },
                  "payer": {
                    "type": "string",
                    "description": "The account that will send the transaction, and the one the\nstorage deposit returns to.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A `delete_agent_secret` call, complete with the wallet's signature.\nNo deposit field: the method is not payable, and attaching anything\nis refused before the contract sees it.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreparedAgentSecretDelete"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "wk_<32-hex-chars>",
        "description": "API key returned by `POST /register`. Format `wk_<hex>`. Pass as\n`Authorization: Bearer wk_...`.\n"
      },
      "PaymentKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Payment-Key",
        "description": "A payment key, `{owner}:{nonce}:{secret}`. Created by\n`POST /wallet/v1/create-payment-key` and funded on chain. An AGENT key\nhas no secret to present — it is named after its wallet, and that\nwallet's `Authorization: Bearer wk_...` stands for it.\n"
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Optional idempotency token. Resubmitting a write request with the same\nkey returns the original result without re-execution. Recommended for\nclients that retry on network failure.\n"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "bad_request": {
                "value": {
                  "error": "bad_request",
                  "message": "amount must be positive"
                }
              },
              "invalid_address": {
                "value": {
                  "error": "invalid_address"
                }
              },
              "unsupported_chain": {
                "value": {
                  "error": "unsupported_chain"
                }
              },
              "unsupported_token": {
                "value": {
                  "error": "unsupported_token"
                }
              },
              "insufficient_balance": {
                "value": {
                  "error": "insufficient_balance"
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid credentials",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "missing_auth": {
                "value": {
                  "error": "missing_auth"
                }
              },
              "invalid_api_key": {
                "value": {
                  "error": "invalid_api_key"
                }
              },
              "timestamp_expired": {
                "value": {
                  "error": "timestamp_expired"
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Action blocked by policy or wallet state",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "policy_denied": {
                "value": {
                  "error": "policy_denied",
                  "message": "daily limit exceeded"
                }
              },
              "wallet_frozen": {
                "value": {
                  "error": "wallet_frozen"
                }
              },
              "not_approver": {
                "value": {
                  "error": "not_approver"
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource does not exist",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "request_not_found": {
                "value": {
                  "error": "request_not_found"
                }
              },
              "approval_not_found": {
                "value": {
                  "error": "approval_not_found"
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "rate_limited": {
                "value": {
                  "error": "rate_limited"
                }
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "Server error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "internal_error": {
                "value": {
                  "error": "internal_error"
                }
              }
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Unavailable — either permanently on this deployment or transiently, and the code says which. `service_unavailable`: feature not enabled/configured (e.g. the `/wallet/v1/confidential/*` routes when `ENABLE_CONFIDENTIAL_INTENTS` is off, the binding-event endpoints without a webhook secret, or NEAR Intents on a network with no solvers) — do not retry; no interval is sent because none would be true. `chain_unavailable`: the chain, or a node in front of it, could not answer this second — a balance that could not be read, a code hash that could not be fetched, a binding that could not be verified, a transaction that expired against a block the node has forgotten. Nothing happened and nothing was charged — transient, retry after the `Retry-After` header. `keystore_error`: the TEE keystore (signing / key derivation) was unreachable or rejected the request — transient, retry after `Retry-After`. `confidential_jwt_expired`: the confidential per-account JWT was rejected by the 1Click upstream and re-authentication also failed — transient, retry after `Retry-After`. (Transient upstream failures use 503 rather than 502 because Cloudflare replaces origin 502/504 responses with its own HTML error page, hiding the JSON body from clients.)",
        "headers": {
          "Retry-After": {
            "description": "Present on transient failures (`chain_unavailable`, `keystore_error`, `confidential_jwt_expired`) — seconds to wait before retrying. Absent on `service_unavailable`, which is not transient: there is no interval after which an unset environment variable becomes set.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "service_unavailable": {
                "value": {
                  "error": "service_unavailable",
                  "message": "Confidential intents are not configured on this deployment"
                }
              },
              "keystore_error": {
                "value": {
                  "error": "keystore_error"
                }
              },
              "confidential_jwt_expired": {
                "value": {
                  "error": "confidential_jwt_expired"
                }
              }
            }
          }
        }
      },
      "OnChainTxFailed": {
        "description": "The transaction was signed and broadcast — the network accepted it — but its on-chain execution REVERTED. `tx_hash` is real (the tx is on chain); no state-changing effect happened. Do NOT retry: re-broadcasting duplicates an already-recorded transaction and burns gas again. `message` is the decoded FunctionCallError / panic string, `failure` the raw NEAR execution-failure JSON. (422 rather than 502 because Cloudflare replaces origin 502/504 responses with its own HTML error page, which would hide this body from clients.) Scope: synchronous execution only. When the operation goes through the multisig approval flow instead, execution happens in the background after the threshold is met — a revert there surfaces as `status: \"failed\"` via `GET /wallet/v1/requests/{request_id}` and the `request_completed` webhook, never as a synchronous 422.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/OnChainTxFailedError"
            },
            "examples": {
              "onchain_tx_failed": {
                "value": {
                  "error": "onchain_tx_failed",
                  "message": "Exceeded the prepaid gas.",
                  "tx_hash": "8YyQnDxNSgrtZtAhDwHRfcpyTAcqGJ4kdmSf7eVLh2dG",
                  "failure": {
                    "ActionError": {
                      "index": 0,
                      "kind": {
                        "FunctionCallError": {
                          "ExecutionError": "Exceeded the prepaid gas."
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "ProjectCallRequest": {
        "type": "object",
        "required": [
          "input"
        ],
        "properties": {
          "input": {
            "type": "object",
            "description": "The project's own input. A CONNECTOR additionally requires a\ntop-level `operation` string here — the one field every connector\nuses, read by the contract, the coordinator, the worker and the\nguest alike.\n",
            "properties": {
              "operation": {
                "type": "string",
                "description": "Required for connectors. `op` is not accepted."
              }
            }
          },
          "async": {
            "type": "boolean",
            "default": false,
            "description": "Return at once with `call_id` and `poll_url` instead of holding the\nconnection. No window applies on this path — long work belongs here.\n"
          },
          "version_key": {
            "type": "string",
            "description": "Pin a specific published version instead of the active one."
          },
          "use_bound_identity": {
            "type": "boolean",
            "default": false,
            "description": "Agent Connect: run under the BOUND asset account's name, so the\nguest's `NEAR_SENDER_ID` becomes e.g. `agent.tla` instead of the\nwallet's own account. near-email turns that into the mailbox it\nsends from, and other connectors derive keys and state from it.\n\nOpt-in, and off by default on purpose. A binding is a capability\nthe agent gains, not a change to who it already is — renaming the\nguest the moment one activated would move every derived identity\nwith nothing in your code to point at.\n\nRequires an ACTIVE binding, and says so rather than falling back:\nwith no binding — or with one still pending, or revoked — the call\nis refused `409 no_bound_identity` before it is priced or charged.\nThe worker then re-verifies the claim against the chain inside the\nTEE and refuses the job if the chain disagrees. Billing never\nfollows the name: `NEAR_USER_ACCOUNT_ID` stays the paying key's\nowner.\n"
          },
          "secrets_ref": {
            "type": "object",
            "properties": {
              "profile": {
                "type": "string"
              },
              "account_id": {
                "type": "string"
              }
            }
          }
        }
      },
      "ProjectCallResponse": {
        "type": "object",
        "properties": {
          "call_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "completed",
              "failed"
            ]
          },
          "output": {
            "type": [
              "object",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "compute_cost": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stablecoin minimal units, compute only — the operation's fee is separate."
          },
          "instructions": {
            "type": [
              "integer",
              "null"
            ]
          },
          "time_ms": {
            "type": [
              "integer",
              "null"
            ]
          },
          "poll_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Present when the call was accepted for polling."
          },
          "attestation_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The TEE quote for this execution."
          }
        }
      },
      "CallRefusal": {
        "type": "object",
        "description": "Every refusal from `POST /call/{owner}/{project}`. `error` is a sentence written for a person and is reworded freely; `reason` is the contract — branch on it. The enum is generated from `CallError::reason()` in the coordinator and is exhaustive there, so a value outside it means the client is older than the server.\n",
        "properties": {
          "error": {
            "type": "string",
            "description": "The human sentence."
          },
          "terminal": {
            "type": "boolean",
            "description": "Present on the refusals where retrying has an answer. `true` means the same request can never succeed; `false` means it succeeds once something changes that the caller or the wallet's owner can change. `no_bound_identity` sends both: `true` when the paying key names no wallet at all, `false` when the wallet simply has no ACTIVE binding yet.\n`upstream_unavailable` (503 + `Retry-After`) is this door's transient answer — something we depend on has not caught up and the same call works later. It is deliberately NOT called `service_unavailable`: on `/wallet/v1/*` that code means a feature this deployment does not offer and must not be retried, and a client's retry rule is written once for every answer.\n"
          },
          "reason": {
            "type": "string",
            "description": "Machine-readable name of the refusal.",
            "enum": [
              "allowance_no_deposit",
              "bad_key_format",
              "compute_limit_too_low",
              "connector_quota_exceeded",
              "expires_too_soon",
              "insufficient_allowance",
              "insufficient_balance",
              "internal_error",
              "invalid_key",
              "keystore_error",
              "max_per_call_exceeded",
              "missing_payment_key",
              "no_bound_identity",
              "no_deposit",
              "operation_limit_reached",
              "out_of_funds",
              "project_not_allowed",
              "project_not_found",
              "rate_limit_exceeded",
              "upstream_unavailable",
              "tee_session_required",
              "timeout",
              "too_many_concurrent_calls",
              "unknown_operation",
              "vault_not_verified",
              "vault_unlocked",
              "wallet_not_yours",
              "wk_is_not_a_payer"
            ]
          }
        },
        "required": [
          "error",
          "reason"
        ]
      },
      "CallTimedOut": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "enum": [
              "timeout"
            ]
          },
          "call_id": {
            "type": "string",
            "format": "uuid"
          },
          "poll_url": {
            "type": "string"
          }
        }
      },
      "CreatePaymentKeyRequest": {
        "type": "object",
        "properties": {
          "initial_deposit_usdc": {
            "type": "string",
            "description": "Dollars, e.g. `\"1.00\"`. Mutually exclusive with `initial_deposit_near`."
          },
          "initial_deposit_near": {
            "type": "string",
            "description": "NEAR, swapped to the stablecoin through Intents — mainnet only.\n"
          },
          "project_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The scope, fixed at creation. `owner/*` allows a whole namespace.\n"
          },
          "max_per_call": {
            "type": "string",
            "description": "Dollars. `\"0\"` or absent means no limit."
          }
        }
      },
      "CreatePaymentKeyResponse": {
        "type": "object",
        "properties": {
          "nonce": {
            "type": "integer"
          },
          "owner": {
            "type": "string"
          },
          "payment_key": {
            "type": "string",
            "description": "Shown once. Store it: a call presents it as `X-Payment-Key`, and it\ncannot be re-issued.\n"
          },
          "store_secrets_tx": {
            "type": "string"
          },
          "top_up_tx": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "SubscriptionStatus": {
        "type": "object",
        "properties": {
          "owner": {
            "type": "string"
          },
          "nonce": {
            "type": "integer"
          },
          "has_subscription": {
            "type": "boolean"
          },
          "wallet_account": {
            "type": "string",
            "description": "The custody wallet this key belongs to, when it belongs to one.\nAbsent for a key owned by a named account. Read from the key's\n`owner`.\n"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "expired": {
            "type": "boolean"
          },
          "allowance_total_usd": {
            "type": [
              "string",
              "null"
            ]
          },
          "allowance_spent_usd": {
            "type": [
              "string",
              "null"
            ]
          },
          "allowance_available_usd": {
            "type": [
              "string",
              "null"
            ]
          },
          "balance": {
            "type": "string",
            "description": "Money on the key. Nets out anything currently reserved by a call in flight."
          },
          "withdrawable": {
            "type": "string"
          },
          "connector_namespace": {
            "type": "string"
          },
          "connectors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "connector_id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "endpoint": {
                  "type": "string"
                },
                "project_id": {
                  "type": "string"
                },
                "operations": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "operation": {
                        "type": "string"
                      },
                      "price_usd": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "connectors_in_scope": {
            "type": "boolean"
          }
        }
      },
      "AgentSecretPubkey": {
        "type": "object",
        "properties": {
          "pubkey": {
            "type": "string",
            "description": "X25519 public key, hex. Encrypt-only."
          },
          "seed": {
            "type": "string",
            "description": "`project:{project_id}:{agent_account}` — returned so a mismatch is\nvisible rather than silent.\n"
          },
          "agent_account": {
            "type": "string",
            "description": "The secret's name AND its owner."
          }
        }
      },
      "PreparedAgentSecret": {
        "type": "object",
        "properties": {
          "contract_id": {
            "type": "string"
          },
          "method_name": {
            "type": "string",
            "enum": [
              "store_agent_secret"
            ]
          },
          "args": {
            "type": "object",
            "description": "Complete arguments, including the agent wallet's signature."
          },
          "deposit": {
            "type": "string",
            "description": "yoctoNEAR; the contract refunds the excess."
          },
          "gas": {
            "type": "string"
          },
          "agent_account": {
            "type": "string"
          }
        }
      },
      "PreparedAgentSecretDelete": {
        "type": "object",
        "properties": {
          "contract_id": {
            "type": "string"
          },
          "method_name": {
            "type": "string",
            "enum": [
              "delete_agent_secret"
            ]
          },
          "args": {
            "type": "object",
            "description": "Complete arguments, including the agent wallet's signature."
          },
          "gas": {
            "type": "string"
          },
          "agent_account": {
            "type": "string"
          }
        }
      },
      "Chain": {
        "type": "string",
        "enum": [
          "near",
          "ethereum",
          "polygon",
          "base",
          "arbitrum",
          "optimism",
          "bsc",
          "avalanche",
          "solana",
          "bitcoin"
        ],
        "description": "Supported chain identifier. The EVM chains (ethereum, polygon, base, arbitrum, optimism, bsc, avalanche) all share ONE derived address (a single secp256k1 key) and are signable via `/wallet/v1/evm/*`. `solana` has its own derived ed25519 address and is signable via `/wallet/v1/solana/*`. `bitcoin` can be derived in the keystore but is not yet serviced by wallet v1."
      },
      "RequestType": {
        "type": "string",
        "description": "Tracked async request / policy transaction type. `withdraw` is a\nsame-chain intents withdrawal; `cross_chain_withdraw` is a separate type\nfor 1Click bridge-out (a policy must list it explicitly — it is NOT\nfolded into `withdraw`). `confidential` covers every confidential-shard\nop (shield / unshield / withdraw / transfer / swap). The deposit family\n(`intents/deposit`, `storage-deposit`, cross-chain deposit) all gate as\n`call`.\n",
        "enum": [
          "call",
          "transfer",
          "withdraw",
          "cross_chain_withdraw",
          "deposit",
          "swap",
          "confidential"
        ]
      },
      "RequestStatus": {
        "type": "string",
        "description": "`partially_failed` occurs only for `w_execute_extension` (Agent Connect's extension door) and is a NORMAL outcome, not an error: the wallet detaches its promises, they execute concurrently and independently, and one failing neither reverts the others nor undoes the value they moved. Read `result.promises[]` for which did what.",
        "enum": [
          "pending_deposit",
          "processing",
          "success",
          "partially_failed",
          "failed",
          "refunded",
          "pending_approval",
          "approved",
          "rejected",
          "cancelled",
          "needs_review"
        ]
      },
      "ErrorCode": {
        "type": "string",
        "enum": [
          "missing_auth",
          "invalid_api_key",
          "missing_wallet_id",
          "invalid_wallet_id",
          "missing_signature",
          "invalid_signature",
          "missing_timestamp",
          "timestamp_expired",
          "wallet_frozen",
          "policy_denied",
          "not_approver",
          "insufficient_balance",
          "wallet_underfunded",
          "vault_underfunded",
          "invalid_address",
          "rate_limited",
          "unsupported_chain",
          "unsupported_token",
          "request_not_found",
          "approval_not_found",
          "already_approved",
          "bad_request",
          "conflict",
          "duplicate_idempotency_key",
          "onchain_tx_failed",
          "internal_error",
          "keystore_error",
          "service_unavailable",
          "chain_unavailable",
          "chain_refused",
          "tx_rejected_by_node",
          "confidential_jwt_expired",
          "agent_connect_denied",
          "wallet_busy",
          "binding_not_found"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorCode"
          },
          "message": {
            "type": "string"
          },
          "details": {
            "type": "object",
            "additionalProperties": {}
          }
        }
      },
      "OnChainTxFailedError": {
        "type": "object",
        "required": [
          "error",
          "tx_hash"
        ],
        "description": "Body of the 422 `onchain_tx_failed` response. Unlike `ErrorResponse`,\nit carries the real `tx_hash` (the transaction IS on chain) and the raw\nNEAR execution-failure JSON.\n",
        "properties": {
          "error": {
            "type": "string",
            "const": "onchain_tx_failed"
          },
          "message": {
            "type": "string",
            "description": "Decoded FunctionCallError / contract panic string."
          },
          "tx_hash": {
            "type": "string",
            "description": "Hash of the broadcast transaction. Real — the tx is on chain."
          },
          "failure": {
            "description": "Raw NEAR execution-failure JSON (e.g. `ActionError`)."
          }
        }
      },
      "RegisterRequest": {
        "type": "object",
        "description": "For the common case (anonymous wallet on OutLayer's shared master),\nsend empty `{}`. Optional fields enable advanced flows:\n\n- `vault_id`: bind this wallet to an existing customer-owned vault,\n  so the wallet's master is derived through the vault's per-customer\n  MPC-derived master instead of OutLayer's shared default. The vault\n  must already be deployed and verified — **vault deployment is not\n  done through this endpoint**. Use the dashboard\n  (https://outlayer.fastnear.com/vault) or the CLI\n  (`outlayer vault init`) to deploy. Reference:\n  https://outlayer.fastnear.com/docs/vaults\n- `account_id` + `pubkey` + `message` + `signature`: bind the wallet\n  to a verified NEAR account via NEP-413 proof-of-ownership. Used\n  by clients that want operational keys (`wk_...`) tied to a real\n  NEAR account.\n",
        "properties": {
          "account_id": {
            "type": "string",
            "description": "NEAR account ID to bind the wallet to (requires NEP-413 signature)."
          },
          "seed": {
            "type": "string",
            "description": "Reserved for deterministic-wallet derivation. Most callers leave unset."
          },
          "pubkey": {
            "type": "string",
            "description": "NEP-413 signer pubkey (paired with `account_id` + `signature`)."
          },
          "message": {
            "type": "string",
            "description": "NEP-413 message that was signed."
          },
          "signature": {
            "type": "string",
            "description": "NEP-413 signature over `message`."
          },
          "vault_id": {
            "type": "string",
            "description": "ID of a deployed and verified customer vault (e.g. `vault.alice.near`).\nBinding is permanent — the resulting API key is tied to this vault\nfor its lifetime. All subsequent operations on the API key\nautomatically derive keys through the per-vault master; you do\nNOT pass `vault_id` on every call.\n"
          }
        }
      },
      "RegisterResponse": {
        "type": "object",
        "required": [
          "wallet_id",
          "near_account_id"
        ],
        "properties": {
          "wallet_id": {
            "type": "string",
            "format": "uuid"
          },
          "api_key": {
            "type": "string",
            "description": "Shown once. Not retrievable later — save it now."
          },
          "near_account_id": {
            "type": "string"
          },
          "handoff_url": {
            "type": "string",
            "format": "uri",
            "description": "Dashboard URL where the controller sets policy."
          },
          "trial": {
            "$ref": "#/components/schemas/TrialInfo"
          }
        }
      },
      "TrialInfo": {
        "type": "object",
        "properties": {
          "calls_remaining": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "limits": {
            "type": "object",
            "properties": {
              "max_instructions": {
                "type": "integer"
              },
              "max_execution_seconds": {
                "type": "integer"
              },
              "max_memory_mb": {
                "type": "integer"
              }
            }
          }
        }
      },
      "AddressResponse": {
        "type": "object",
        "required": [
          "wallet_id",
          "chain",
          "address",
          "public_key"
        ],
        "properties": {
          "wallet_id": {
            "type": "string"
          },
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "address": {
            "type": "string"
          },
          "public_key": {
            "type": "string"
          },
          "vault_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "asset_account_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent Connect: the bound named asset account. Always present for chain=near and `null` when the wallet has no binding — a key that is simply absent is a different answer to a client that checks for one."
          },
          "executor_account_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent Connect: the executor identity. For chain=near always equal to `address`."
          },
          "gas_balance": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent Connect: the executor's native balance in yoctoNEAR, cached about 30 seconds. Present only for chain=near on a BOUND wallet — it is here so whoever funds the executor learns the account and whether it needs funding in one call. Absent also when the chain could not be read, which is not a balance of zero."
          }
        }
      },
      "BalanceResponse": {
        "type": "object",
        "required": [
          "balance",
          "token",
          "account_id",
          "account"
        ],
        "properties": {
          "balance": {
            "type": "string",
            "description": "Amount in token's smallest unit."
          },
          "token": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "account": {
            "type": "string",
            "enum": [
              "asset",
              "executor"
            ],
            "description": "Which identity `account_id` is, so the figure cannot be misattributed. `executor` for every wallet without a binding."
          }
        }
      },
      "WalletBusyResponse": {
        "type": "object",
        "description": "A `409` meaning another money-moving operation is using this wallet.\n\nA wallet runs one spend at a time. The check \"does this fit the limit?\" and the write that satisfies it are seconds apart, so two overlapping operations would both read a total that neither had written yet and both pass — spending a daily cap twice. Serializing per wallet is what makes the counters true, and it cannot be done in SQL instead: the limits live in the customer's encrypted policy, which only the enclave can read.\n\nCallers wait a short grace first, so two quick calls in a row simply queue and never see this. It appears when the operation in front is a long one — a swap or a cross-chain withdraw. Poll `in_flight_request_id` when it is set, rather than retrying blindly; when it is `null`, `in_flight_operation` still says what to wait for.",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "wallet_busy"
            ]
          },
          "message": {
            "type": "string"
          },
          "in_flight_request_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The request holding the wallet, and only ever one that `GET /wallet/v1/requests/{request_id}` can already answer. `null` never means the wallet is free: it means there is nothing to poll, and `message` says which of the two reasons applies — the holder has not written its request row yet (retrying shortly yields an id), or the operation writes no request row at all, as a cross-chain deposit intent does. An id is deliberately withheld until its row exists, because one handed out earlier answers `404` and reads as a request that was lost."
          },
          "in_flight_operation": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the holder is doing. Present even when `in_flight_request_id` is `null`, and worth branching on: a transfer clears in seconds while a cross-chain withdraw can run for many minutes, and that is the difference between retrying at once and backing off. `repair` is the odd one — a read of a request left unresolved settles it under the same lock, and that finishes quickly.",
            "enum": [
              null,
              "transfer",
              "call",
              "swap",
              "delete",
              "withdraw",
              "cross_chain_withdraw",
              "cross_chain_deposit",
              "storage_deposit",
              "intents_transfer",
              "intents_deposit",
              "confidential",
              "confidential_deposit",
              "repair"
            ]
          }
        }
      },
      "AgentConnectDeniedResponse": {
        "type": "object",
        "description": "A `403` from the Agent Connect pre-flight: the request was refused BEFORE it was signed, so no gas was spent, and the refusal names the rule the wallet contract would have panicked on.\n\nStructured rather than prose because a client has to route on it. Read `terminal` first: `true` means retrying is pointless and the OWNER must act — issue a new grant, re-provision the executor, fund the account, or rewrite the request. `false` means the same request may succeed later with nothing changed (a freeze lifted, a parked account reactivated, recognized wallet code restored, the account's registry caught up with it).",
        "required": [
          "error",
          "message",
          "class",
          "terminal"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "agent_connect_denied"
            ]
          },
          "message": {
            "type": "string",
            "description": "One sentence the owner can act on."
          },
          "class": {
            "type": "string",
            "description": "Stable machine class. Binding liveness: `executor_not_in_control_set`, `lease_expired`, `account_expired`, `account_frozen`, `account_not_active`, `unsupported_wallet_implementation`, `unrecognized_wallet_code`, `registry_disagrees` (a leased account whose collection's `nft_token` does not confirm the account's own `nft_item_info` — reversible, the registry may trail by a block), `binding_evidence_mismatch`, `chain_status_unreadable`. Spend grant: `grant_missing`, `grant_expired`, `grant_unreadable`, `grant_exhausted`, `receiver_not_granted`, `token_not_granted`, `token_budget_exceeded`, `collection_not_granted`, `item_not_granted`, `own_collection_refused`, `insufficient_vs_reserve`, and `grant_shape_violation:<subcode>` where subcode is one of `refund_target_not_allowed`, `grant_call_must_stand_alone`, `grant_call_deposit`, `grant_approval_not_allowed`, `grant_args_unreadable`, `grant_method_not_allowed`, `grant_action_not_allowed`."
          },
          "terminal": {
            "type": "boolean",
            "description": "`true` — do not retry. Every grant-layer class is terminal: the chain's answer does not change while the request and the grant stay as they are."
          },
          "promise_index": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Which promise inside the decoded request the rule is about; `null` for a refusal about the request or the binding as a whole."
          },
          "additional_violations": {
            "type": "integer",
            "description": "How many further violations the same request carries beyond the one reported — whether fixing this one ends the matter."
          }
        }
      },
      "PutBindingRequest": {
        "type": "object",
        "required": [
          "asset_account_id"
        ],
        "properties": {
          "asset_account_id": {
            "type": "string",
            "description": "Named NEAR account this wallet operates: the leased asset account (`agent.tla`) for `hos_lease`, the owner's own account (`user.near`) for `personal_account`."
          },
          "kind": {
            "type": "string",
            "enum": [
              "hos_lease",
              "personal_account"
            ],
            "default": "hos_lease",
            "description": "Binding mode. Omitted means `hos_lease`, byte-for-byte the pre-kind behavior."
          },
          "owner_account_id": {
            "type": "string",
            "description": "`hos_lease`: required; the account that authorized the lane, as reported at provisioning (informational — authorization is proven by the on-chain control set). `personal_account`: optional, and must equal `asset_account_id` — the owner IS the account."
          },
          "impl_version": {
            "type": "integer",
            "description": "`hos_lease` only (REQUIRED there, REJECTED for `personal_account`): wallet implementation version, read from the asset account's `hos_lease()`. Pins which request decoder applies; an unsupported value is rejected at binding time (terminal, do not retry)."
          }
        },
        "example": {
          "asset_account_id": "agent.tla",
          "owner_account_id": "owner.near",
          "impl_version": 6
        }
      },
      "BindingResponse": {
        "type": "object",
        "required": [
          "binding_id",
          "wallet_id",
          "kind",
          "asset_account_id",
          "executor_account_id",
          "binding_status",
          "created_at"
        ],
        "properties": {
          "binding_id": {
            "type": "string"
          },
          "wallet_id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "hos_lease",
              "personal_account"
            ]
          },
          "asset_account_id": {
            "type": "string"
          },
          "executor_account_id": {
            "type": "string",
            "description": "The OutLayer execution identity — register it in the account's extension set (and, for hos_lease, provision its spend grant). NOTE: the response deliberately carries no owner. `owner_account_id` is accepted at PUT as the provisioning receipt House of Stake hands the integrator, and is stored — but it is checked for shape only and compared against nothing, so returning it would present a claim as an established fact. Who holds a leased account is `nft_item_info.owner_id` on chain, and what ends a lane when it changes hands is the rotation pin — the item's `owner_id`, `rotation_epoch` and `rotation_seq` as read at activation: a different owner, or a different seq inside the same epoch, ends the binding; a new epoch under the same owner re-establishes it."
          },
          "binding_status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "suspended",
              "revoked"
            ],
            "description": "OutLayer's view only, not an attestation of chain state. `pending` until the executor is first observed live in the extension set; `suspended` on reversible faults (freeze, non-Active state, unsupported impl_version, unrecognized code hash); `revoked` is terminal (extension removed, lease/state expired, ownership rotated, or DELETE)."
          },
          "impl_version": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Absent for kind=personal_account."
          },
          "decoder_version": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Derived by OutLayer, never client-supplied. Absent for kind=personal_account, whose versioning is the account's code hash checked at verify time."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "gas_balance": {
            "type": [
              "string",
              "null"
            ],
            "description": "Executor's native NEAR balance in yoctoNEAR (GET only). Null when the RPC could not answer."
          },
          "gas_balance_low": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "The executor is under `gas_balance_threshold` and will soon be unable to pay for transactions — top it up. `null` means the balance could not be read, which is UNKNOWN rather than low. Poll this endpoint; there is no separate low-gas notification."
          },
          "gas_balance_threshold": {
            "type": [
              "string",
              "null"
            ],
            "description": "yoctoNEAR floor `gas_balance_low` is measured against."
          }
        },
        "example": {
          "binding_id": "bnd_7f3a2b1c9d8e4f60a1b2c3d4e5f60718",
          "wallet_id": "9c3c9e10-1c1f-4f5e-9c4a-1d7b9a8f3c20",
          "kind": "hos_lease",
          "asset_account_id": "agent.tla",
          "executor_account_id": "9c3c9e101c1f4f5e9c4a1d7b9a8f3c20",
          "binding_status": "pending",
          "impl_version": 6,
          "decoder_version": 1,
          "created_at": "2026-08-18T14:00:00Z"
        }
      },
      "BindingSetupResponse": {
        "type": "object",
        "required": [
          "kind",
          "asset_account_id",
          "executor_account_id",
          "code_hash",
          "transactions"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "personal_account"
            ]
          },
          "asset_account_id": {
            "type": "string"
          },
          "executor_account_id": {
            "type": "string"
          },
          "code_hash": {
            "type": "string",
            "description": "Base58 code hash of the published no-sign wallet global contract — the same artifact the verifier's allowlist pins."
          },
          "transactions": {
            "type": "array",
            "description": "One transaction, three actions; receiver = signer = the owner's account.",
            "items": {
              "type": "object",
              "required": [
                "signer_id",
                "receiver_id",
                "actions"
              ],
              "properties": {
                "signer_id": {
                  "type": "string"
                },
                "receiver_id": {
                  "type": "string"
                },
                "actions": {
                  "type": "array",
                  "items": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "example": {
          "kind": "personal_account",
          "asset_account_id": "user.near",
          "executor_account_id": "9c3c9e101c1f4f5e9c4a1d7b9a8f3c20",
          "code_hash": "BwjDnyemmBhrCyuviDGpoQAm9mdjTfrX7ZjqgZB4MHvM",
          "transactions": [
            {
              "signer_id": "user.near",
              "receiver_id": "user.near",
              "actions": [
                {
                  "type": "UseGlobalContract",
                  "code_hash": "BwjDnyemmBhrCyuviDGpoQAm9mdjTfrX7ZjqgZB4MHvM"
                },
                {
                  "type": "FunctionCall",
                  "method_name": "w_init",
                  "args": {},
                  "gas": "30000000000000",
                  "deposit": "1"
                },
                {
                  "type": "FunctionCall",
                  "method_name": "w_execute_extension",
                  "args": {
                    "request": {
                      "internal": [
                        {
                          "op": "add_extension",
                          "payload": {
                            "account_id": "9c3c9e101c1f4f5e9c4a1d7b9a8f3c20"
                          }
                        }
                      ]
                    }
                  },
                  "gas": "30000000000000",
                  "deposit": "1"
                }
              ]
            }
          ]
        }
      },
      "DeleteBindingResponse": {
        "type": "object",
        "required": [
          "binding_status",
          "cancelled_approvals"
        ],
        "properties": {
          "binding_status": {
            "type": "string",
            "enum": [
              "revoked"
            ]
          },
          "binding_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The record that was ended; absent when there was nothing to end."
          },
          "cancelled_approvals": {
            "type": "integer",
            "description": "Pending multisig approvals targeting the asset account that were cancelled rather than left completable. The requests those approvals were gating are moved to `cancelled` in the same step — an approval cancelled on its own stops the execution but leaves the request at `pending_approval` for good, unable to complete and never saying so. Requests already past the decision (`processing`) are left alone: they may have bytes on the wire."
          }
        }
      },
      "TokenInfo": {
        "type": "object",
        "required": [
          "id",
          "symbol",
          "chains",
          "decimals"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "symbol": {
            "type": "string"
          },
          "chains": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Chain"
            }
          },
          "decimals": {
            "type": "integer"
          },
          "defuse_asset_id": {
            "type": "string"
          }
        }
      },
      "TokensResponse": {
        "type": "object",
        "required": [
          "tokens"
        ],
        "properties": {
          "tokens": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TokenInfo"
            }
          }
        }
      },
      "CallRequest": {
        "type": "object",
        "required": [
          "receiver_id",
          "method_name"
        ],
        "properties": {
          "receiver_id": {
            "type": "string"
          },
          "method_name": {
            "type": "string"
          },
          "args": {
            "type": "object",
            "additionalProperties": {},
            "description": "Method arguments as a JSON object. Mutually exclusive with `args_base64`."
          },
          "args_base64": {
            "type": "string",
            "description": "Pre-encoded base64 args. Use for non-JSON encodings."
          },
          "gas": {
            "type": "string",
            "description": "Gas budget in TGas as a string. Defaults to `30000000000000` (30 TGas) server-side."
          },
          "deposit": {
            "type": "string",
            "description": "Attached NEAR deposit in yoctoNEAR. Defaults to `0` server-side."
          }
        }
      },
      "CallResponse": {
        "type": "object",
        "required": [
          "request_id",
          "status"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/RequestStatus"
          },
          "tx_hash": {
            "type": [
              "string",
              "null"
            ]
          },
          "result": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {},
            "description": "For `w_execute_extension` this also carries `promises`: one entry per promise of the decoded request, in the order the request listed them, each with `index`, `receiver`, `status` (`success` | `failed` | `unknown`), `receipt_id` and the chain's own `failure` object where it failed. `unknown` means the promise's receipt was not found in the outcome — it is NOT read as success, and its amounts are not charged to the velocity counters. A call the wallet contract REFUSED carries `promises: []`: the refusal happened before anything was detached, so no promise exists to report on, and none of the request is charged to the counters — read `failure` for the contract's reason."
          },
          "approval_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "required": {
            "type": [
              "integer",
              "null"
            ]
          },
          "approved": {
            "type": [
              "integer",
              "null"
            ]
          },
          "request_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Present on `pending_approval` — sha256(canonical_json(op)); sign to approve."
          }
        }
      },
      "TransferRequest": {
        "type": "object",
        "description": "Native transfer on a single chain. Canonical recipient field is\n`to` — matches `WithdrawRequest.to` and the dashboard. The legacy\nfield name `receiver_id` is still accepted as an alias for\nbackward compatibility with clients that predate the rename;\nsending BOTH in the same body is rejected as a deserialization\nerror.",
        "required": [
          "to",
          "amount"
        ],
        "properties": {
          "chain": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Chain"
              }
            ],
            "description": "Chain to transfer on. Defaults to `near` server-side."
          },
          "to": {
            "type": "string",
            "description": "Recipient account id on `chain`. For NEAR, accepts both named\naccounts (`alice.near`) and 64-char implicit hex accounts.",
            "example": "alice.near"
          },
          "receiver_id": {
            "type": "string",
            "deprecated": true,
            "description": "**Deprecated alias for `to`.** Existing clients sending\n`receiver_id` continue to work; new clients should use `to`.\nSending both `to` and `receiver_id` in the same body is\nrejected with a 400."
          },
          "amount": {
            "type": "string",
            "description": "Amount in the chain's smallest unit (yoctoNEAR, wei, …)."
          }
        }
      },
      "DeleteRequest": {
        "type": "object",
        "description": "Irreversibly delete the wallet account and sweep its full NEAR balance\nto `beneficiary` (NEAR's native `DeleteAccount`).",
        "required": [
          "beneficiary"
        ],
        "properties": {
          "beneficiary": {
            "type": "string",
            "description": "Account that receives the deleted wallet's entire remaining\nbalance. Must not be the wallet's own account.",
            "example": "treasury.near"
          },
          "chain": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Chain"
              }
            ],
            "description": "Chain the account lives on. Defaults to `near` server-side."
          }
        }
      },
      "DeleteResponse": {
        "type": "object",
        "required": [
          "request_id",
          "status",
          "beneficiary"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/RequestStatus"
          },
          "tx_hash": {
            "type": [
              "string",
              "null"
            ]
          },
          "beneficiary": {
            "type": "string"
          },
          "approval_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "required": {
            "type": [
              "integer",
              "null"
            ]
          },
          "approved": {
            "type": [
              "integer",
              "null"
            ]
          },
          "request_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Present on `pending_approval` — sha256(canonical_json(op)); sign to approve."
          }
        }
      },
      "StorageDepositRequest": {
        "type": "object",
        "required": [
          "token"
        ],
        "properties": {
          "token": {
            "type": "string",
            "description": "NEP-141 token contract (e.g. `wrap.near`). A `nep141:` prefix is stripped if present."
          },
          "account_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Account to register. Defaults to the wallet's own NEAR address."
          }
        }
      },
      "StorageDepositResponse": {
        "type": "object",
        "description": "Response for `storageDeposit`. `storage_deposit` is multisig-capable: on\na multisig wallet that requires approval the response carries\n`status=pending_approval` with the `approval_id` / `required` / `approved`\n/ `request_hash` fields populated (same shape as `CallResponse`); the\ndeposit executes only after the approval threshold is met. On the direct\npath those fields are omitted and the single-sig response is byte-identical\nto before.\n",
        "required": [
          "request_id",
          "status",
          "token",
          "account_id",
          "already_registered"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/RequestStatus"
          },
          "tx_hash": {
            "type": [
              "string",
              "null"
            ]
          },
          "token": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "already_registered": {
            "type": "boolean",
            "description": "True when the account was already storage-registered; no transaction was sent."
          },
          "approval_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Present only on `pending_approval` (multisig) — the approval to vote on."
          },
          "required": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Present only on `pending_approval` — approvals needed."
          },
          "approved": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Present only on `pending_approval` — approvals collected so far."
          },
          "request_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Present on `pending_approval` — sha256(canonical_json(op)); sign to approve."
          }
        }
      },
      "IntentsDepositRequest": {
        "type": "object",
        "required": [
          "token",
          "amount"
        ],
        "properties": {
          "token": {
            "type": "string"
          },
          "amount": {
            "type": "string"
          }
        }
      },
      "IntentsDepositResponse": {
        "type": "object",
        "required": [
          "request_id",
          "status"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/RequestStatus"
          },
          "tx_hash": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "WithdrawRequest": {
        "type": "object",
        "required": [
          "chain",
          "amount"
        ],
        "properties": {
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "to": {
            "type": "string",
            "description": "Destination address on the target chain."
          },
          "amount": {
            "type": "string"
          },
          "token": {
            "type": "string",
            "description": "Token ID, e.g. `nep141:usdt.tether-token.near`. For `chain=near`, omit (or use `near`/`native`) to deliver **native NEAR** (unwraps the wallet's wNEAR); use `nep141:wrap.near` to deliver wNEAR instead. For other chains, this is the source Intents asset bridged via 1Click."
          },
          "async": {
            "type": "boolean",
            "default": false,
            "description": "When `true`, the call returns immediately with `status=processing` and a `poll_url`; the withdrawal settles in the background and the caller polls `GET /wallet/v1/requests/{request_id}` for the terminal status. **Recommended for cross-chain withdrawals**, whose bridge can take longer than the synchronous response window. When `false` (default) the call blocks until the withdrawal settles. Auth, policy and validation errors are returned synchronously in both modes; in async mode only an execution failure surfaces as the request's `failed` status."
          }
        }
      },
      "IntentsTransferRequest": {
        "type": "object",
        "required": [
          "to",
          "amount",
          "token"
        ],
        "properties": {
          "to": {
            "type": "string",
            "description": "Recipient NEAR account id (named or 64-hex implicit). Credited inside `intents.near`; the account need not exist on-chain."
          },
          "amount": {
            "type": "string",
            "description": "Amount in minimal units (integer string)."
          },
          "token": {
            "type": "string",
            "description": "Defuse asset id, e.g. `nep141:usdt.tether-token.near` (a bare `usdt.tether-token.near` is normalized to `nep141:`). To send NEAR, use `nep141:wrap.near`."
          }
        }
      },
      "WithdrawResponse": {
        "type": "object",
        "description": "Response for `intentsWithdraw` (same-chain AND cross-chain). On a\nmultisig wallet that requires approval, `status=pending_approval` and the\n`approval_id` / `required` / `approved` / `request_hash` fields are\npopulated; approvers sign `request_hash` (see `Nep413Auth`). On the\ndirect path these fields are omitted/null.\n",
        "required": [
          "request_id",
          "status"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/RequestStatus"
          },
          "approval_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "required": {
            "type": [
              "integer",
              "null"
            ]
          },
          "approved": {
            "type": [
              "integer",
              "null"
            ]
          },
          "request_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Canonical request hash to sign when `status=pending_approval` (otherwise absent)."
          },
          "poll_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Present when `async=true` (`status=processing`) — poll this path (`GET /wallet/v1/requests/{request_id}`) for the terminal status."
          }
        }
      },
      "DryRunResponse": {
        "type": "object",
        "properties": {
          "would_succeed": {
            "type": "boolean"
          },
          "reason": {
            "type": "string",
            "description": "Present when `would_succeed` is false. One of: `wallet_frozen`, `policy_denied`, `storage_not_registered` (wNEAR/NEP-141 recipient has no token storage), `recipient_not_found` (native NEAR to a non-existent named account — would burn the wNEAR), `insufficient_balance` (intents balance below the requested amount), `invalid_request` (body the real withdraw would reject with a 400), `bridge_rejected` (1Click refused the cross-chain quote — below the bridge minimum, unsupported route or no liquidity; `message` carries 1Click's own text, e.g. \"Amount is too low for bridge, try at least 303064\")."
          },
          "message": {
            "type": "string"
          },
          "estimated_fee": {
            "type": "string"
          },
          "fee_token": {
            "type": "string"
          },
          "policy_check": {
            "$ref": "#/components/schemas/PolicyCheckResult"
          }
        }
      },
      "PolicyCheckResult": {
        "type": "object",
        "description": "Policy evaluation summary attached to a dry-run. Present whenever the\npolicy engine ran (i.e. not on a `wallet_frozen` short-circuit). On a\n`policy_denied` result `within_limits` is false and the optional fields\nare omitted; on an allowed / requires-approval result `within_limits` is\ntrue and `address_allowed` is populated.\n",
        "required": [
          "within_limits"
        ],
        "properties": {
          "within_limits": {
            "type": "boolean",
            "description": "True when the op is within the wallet's spending limits."
          },
          "daily_remaining": {
            "type": [
              "string",
              "null"
            ],
            "description": "Remaining daily allowance in minimal units, when the policy tracks one."
          },
          "address_allowed": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "True when the destination address passed the policy allowlist."
          }
        }
      },
      "SwapRequest": {
        "type": "object",
        "required": [
          "token_in",
          "token_out",
          "amount_in"
        ],
        "properties": {
          "token_in": {
            "type": "string"
          },
          "token_out": {
            "type": "string"
          },
          "amount_in": {
            "type": "string"
          },
          "min_amount_out": {
            "type": "string"
          }
        }
      },
      "SwapResponse": {
        "type": "object",
        "description": "Response for `intentsSwap`. Swap is a **Trusted** op, so on a multisig\nwallet it can return `status=pending_approval` with the\n`approval_id` / `required` / `approved` / `request_hash` fields populated\n(same shape as `WithdrawResponse`); the swap executes only after the\napproval threshold is met. On the direct path those fields are omitted and\n`amount_out` / `intent_hash` carry the settled result.\n",
        "required": [
          "request_id",
          "status"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/RequestStatus"
          },
          "amount_out": {
            "type": "string"
          },
          "intent_hash": {
            "type": "string"
          },
          "approval_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "required": {
            "type": [
              "integer",
              "null"
            ]
          },
          "approved": {
            "type": [
              "integer",
              "null"
            ]
          },
          "request_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Canonical request hash to sign when `status=pending_approval` (otherwise absent)."
          }
        }
      },
      "SwapQuoteResponse": {
        "type": "object",
        "properties": {
          "amount_out": {
            "type": "string"
          },
          "min_amount_out": {
            "type": "string"
          },
          "deadline": {
            "type": "string",
            "format": "date-time"
          },
          "time_estimate_seconds": {
            "type": "integer"
          }
        }
      },
      "DefuseAssetId": {
        "type": "string",
        "description": "NEAR Intents `defuse_asset_id`. Currently always a NEP-141 token\nidentifier of the form `nep141:<contract>`. Format matches the\n[`TokenInfo.defuse_asset_id`](#/components/schemas/TokenInfo) field\nreturned by `listTokens`.",
        "example": "nep141:wrap.near"
      },
      "DestinationAsset": {
        "allOf": [
          {
            "$ref": "#/components/schemas/DefuseAssetId"
          }
        ],
        "description": "Defuse asset id the recipient will hold after the cross-chain deposit\nsettles. Defaults to NEAR USDC.",
        "default": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1"
      },
      "DepositIntentRequest": {
        "description": "Request body for `createDepositIntent`. Two shapes accepted; see the\n`anyOf` branches below. `amount` is always required. The\n`destination_asset` field defaults to NEAR USDC\n(`nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1`)\nwhen omitted — see\n[`DestinationAsset`](#/components/schemas/DestinationAsset).\n\nUses `anyOf` (not `oneOf`) deliberately — a request that supplies\nboth `source_asset` and `chain` matches both branches; the\ncoordinator resolves the conflict by preferring `source_asset` and\nlogging a warning, so `oneOf`'s strict \"exactly one match\" rule\nwould needlessly reject valid requests.",
        "anyOf": [
          {
            "title": "BySourceAsset",
            "description": "Preferred shape. The source chain is derived from the\n`source_asset` prefix (e.g. `nep141:eth-…` → ethereum,\n`nep141:base-…` → base, `nep141:btc.omft.near` → bitcoin,\nanything else `nep141:<contract>.near` → near).",
            "type": "object",
            "required": [
              "source_asset",
              "amount"
            ],
            "properties": {
              "source_asset": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/DefuseAssetId"
                  }
                ],
                "description": "NEAR Intents `defuse_asset_id` of the source token. The\nsource chain is derived from this id.",
                "example": "nep141:base-0x833589fcd6edb6e08f4c7c32d4f71b54bda02913.omft.near"
              },
              "destination_asset": {
                "$ref": "#/components/schemas/DestinationAsset"
              },
              "amount": {
                "type": "string",
                "description": "Amount in the source token's smallest unit (e.g. `5000000` = 5 USDC)."
              },
              "refund_address": {
                "type": "string",
                "description": "Address on the source chain to refund to if the cross-chain\ndeposit fails. Defaults to the wallet's derived address on that\nchain."
              }
            }
          },
          {
            "title": "ByChainAndToken",
            "description": "Legacy shape — kept for backwards compatibility. The coordinator\nresolves `(chain, token)` to a `source_asset` via the 1Click\ntoken catalog.",
            "type": "object",
            "required": [
              "chain",
              "amount"
            ],
            "properties": {
              "chain": {
                "type": "string",
                "description": "Source chain. Supported: `near`, `ethereum`, `base`,\n`arbitrum`, `solana`, `bitcoin`, `bsc`, `polygon`,\n`optimism`, `avalanche`.",
                "example": "ethereum"
              },
              "token": {
                "type": "string",
                "description": "Source token symbol on the origin chain. Defaults to `USDC`.",
                "default": "USDC"
              },
              "destination_asset": {
                "$ref": "#/components/schemas/DestinationAsset"
              },
              "amount": {
                "type": "string"
              },
              "refund_address": {
                "type": "string"
              }
            }
          }
        ]
      },
      "DepositIntentResponse": {
        "type": "object",
        "required": [
          "intent_id",
          "deposit_address",
          "amount",
          "amount_out",
          "min_amount_out"
        ],
        "properties": {
          "intent_id": {
            "type": "string"
          },
          "deposit_address": {
            "type": "string",
            "description": "One-time address on the source chain — send funds here. The\naddress format depends on the resolved source chain:\n\n- `near` — 64-char hex (NEAR implicit account), e.g.\n  `f51768dc0c4d4bbb78890262da9882dee2ee5b6c2fcf2c527e56c6eadcb54353`\n- `ethereum` / `base` / `arbitrum` / `bsc` / `polygon` /\n  `optimism` / `avalanche` — `0x` + 40 hex (EVM), e.g.\n  `0x582290c0b2Cb60989B35FFF66049f3e3247355bc`\n- `solana` — base58, 32-44 chars (Solana), e.g.\n  `5AmGa2Bcfajbytg55UUb4vCAAzKBMYKZNQwx5S2BH2qf`\n- `bitcoin` — `bc1…` (segwit) or `1…` / `3…` (legacy)\n\nSending tokens to an address whose format doesn't match the\nsource chain will lose the funds — always verify the format\nclient-side before initiating the transfer."
          },
          "amount": {
            "type": "string"
          },
          "amount_out": {
            "type": "string",
            "description": "Amount credited after the cross-chain deposit fee."
          },
          "min_amount_out": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Deadline after which the deposit address is no longer\nguaranteed to be honored by 1Click. Absent if 1Click did not\nreturn a deadline."
          },
          "estimated_time_secs": {
            "type": "integer",
            "description": "Solver's estimate of deposit settlement time, in seconds. Absent\nif 1Click did not return an estimate."
          },
          "hint": {
            "type": "string",
            "description": "Non-binding advisory when a faster / cheaper endpoint exists\nfor the same logical operation. Present only when the resolved\nsource chain is `near` — in that case the caller's funds are\nalready on NEAR and `POST /wallet/v1/intents/deposit` would do\nthe deposit in one direct `ft_transfer_call` without the\n1Click solver hop. Clients that don't read this field are\nunaffected; clients that prefer the most direct path can\nswitch endpoints on receiving it."
          }
        }
      },
      "DepositStatusResponse": {
        "type": "object",
        "required": [
          "intent_id",
          "status",
          "amount",
          "deposit_address",
          "created_at"
        ],
        "properties": {
          "intent_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "bridging",
              "success",
              "failed",
              "expired"
            ]
          },
          "amount": {
            "type": "string",
            "description": "Deposit amount in the source token's minimal units."
          },
          "deposit_address": {
            "type": "string",
            "description": "The one-time source-chain address funds were to be sent to."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "1Click deadline after which the deposit address is no longer honored."
          },
          "result": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {}
          }
        }
      },
      "PaymentCheckCreateRequest": {
        "type": "object",
        "required": [
          "token",
          "amount"
        ],
        "properties": {
          "token": {
            "type": "string",
            "description": "Token to fund the check with. Accepts a `defuse_asset_id` (`nep141:<contract>`) or a bare NEP-141 contract id; normalized server-side."
          },
          "amount": {
            "type": "string",
            "description": "Amount in the token's minimal units. Must be > 0."
          },
          "memo": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional note (max 256 chars) surfaced to whoever peeks/claims the check."
          },
          "expires_in": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "minimum": 0,
            "description": "Optional time-to-live in seconds from creation. After it elapses an unclaimed check reports `expired` and can no longer be claimed (only reclaimed by the creator)."
          }
        }
      },
      "PaymentCheckCreateResponse": {
        "type": "object",
        "required": [
          "check_id",
          "check_key",
          "token",
          "amount",
          "created_at"
        ],
        "properties": {
          "check_id": {
            "type": "string",
            "format": "uuid",
            "description": "Server-side identifier for the check (used by status / reclaim)."
          },
          "check_key": {
            "type": "string",
            "description": "The ephemeral account's ed25519 private key (64 hex chars). Bearer secret — whoever holds it can claim the funds. Returned ONCE."
          },
          "token": {
            "type": "string"
          },
          "amount": {
            "type": "string"
          },
          "memo": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "PaymentCheckBatchCreateRequest": {
        "type": "object",
        "required": [
          "checks"
        ],
        "properties": {
          "checks": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/PaymentCheckCreateRequest"
            }
          }
        }
      },
      "PaymentCheckBatchCreateResponse": {
        "type": "object",
        "required": [
          "checks"
        ],
        "properties": {
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentCheckCreateResponse"
            }
          }
        }
      },
      "PaymentCheckClaimRequest": {
        "type": "object",
        "required": [
          "check_key"
        ],
        "properties": {
          "check_key": {
            "type": "string",
            "description": "The check's ephemeral private key (64 hex chars)."
          },
          "amount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional partial claim amount in minimal units. Omit to claim the full remaining balance."
          }
        }
      },
      "PaymentCheckClaimResponse": {
        "type": "object",
        "required": [
          "token",
          "amount_claimed",
          "remaining",
          "claimed_at"
        ],
        "properties": {
          "token": {
            "type": "string"
          },
          "amount_claimed": {
            "type": "string"
          },
          "remaining": {
            "type": "string",
            "description": "Amount still claimable on the check after this claim (minimal units)."
          },
          "memo": {
            "type": [
              "string",
              "null"
            ]
          },
          "claimed_at": {
            "type": "string",
            "format": "date-time"
          },
          "intent_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "base58 solver-relay intent hash for the claim transfer, if returned."
          }
        }
      },
      "PaymentCheckReclaimRequest": {
        "type": "object",
        "required": [
          "check_id"
        ],
        "properties": {
          "check_id": {
            "type": "string",
            "format": "uuid",
            "description": "The `check_id` of a check the authenticated wallet created."
          },
          "amount": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional partial reclaim amount in minimal units. Omit to reclaim the full remaining balance."
          }
        }
      },
      "PaymentCheckReclaimResponse": {
        "type": "object",
        "required": [
          "token",
          "amount_reclaimed",
          "remaining",
          "reclaimed_at"
        ],
        "properties": {
          "token": {
            "type": "string"
          },
          "amount_reclaimed": {
            "type": "string"
          },
          "remaining": {
            "type": "string",
            "description": "Amount still outstanding on the check after this reclaim (minimal units)."
          },
          "reclaimed_at": {
            "type": "string",
            "format": "date-time"
          },
          "intent_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "base58 solver-relay intent hash for the reclaim transfer, if returned."
          }
        }
      },
      "PaymentCheckStatusResponse": {
        "type": "object",
        "required": [
          "check_id",
          "token",
          "amount",
          "claimed_amount",
          "reclaimed_amount",
          "status",
          "created_at"
        ],
        "properties": {
          "check_id": {
            "type": "string",
            "format": "uuid"
          },
          "token": {
            "type": "string"
          },
          "amount": {
            "type": "string",
            "description": "Original check amount in minimal units."
          },
          "claimed_amount": {
            "type": "string",
            "description": "Total claimed so far (minimal units)."
          },
          "reclaimed_amount": {
            "type": "string",
            "description": "Total reclaimed so far (minimal units)."
          },
          "status": {
            "type": "string",
            "description": "Lifecycle state: `unclaimed`, `claiming`, `partially_claimed`, `claimed`, `reclaiming`, `partially_reclaimed`, `reclaimed`, or the virtual `expired` (unclaimed and past `expires_at`)."
          },
          "memo": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "claimed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "claimed_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "Wallet id that claimed the check, if any."
          }
        }
      },
      "PaymentCheckListResponse": {
        "type": "object",
        "required": [
          "checks"
        ],
        "properties": {
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentCheckStatusResponse"
            }
          }
        }
      },
      "PaymentCheckPeekRequest": {
        "type": "object",
        "required": [
          "check_key"
        ],
        "properties": {
          "check_key": {
            "type": "string",
            "description": "The check's ephemeral private key (64 hex chars)."
          }
        }
      },
      "PaymentCheckPeekResponse": {
        "type": "object",
        "required": [
          "token",
          "balance",
          "status"
        ],
        "properties": {
          "token": {
            "type": "string"
          },
          "balance": {
            "type": "string",
            "description": "Live on-chain balance of the check's ephemeral account (minimal units)."
          },
          "memo": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "description": "Same status vocabulary as `PaymentCheckStatusResponse`."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "SignMessageRequest": {
        "type": "object",
        "required": [
          "message",
          "recipient"
        ],
        "properties": {
          "message": {
            "type": "string"
          },
          "recipient": {
            "type": "string"
          },
          "nonce": {
            "type": "string",
            "description": "Base64-encoded 32-byte nonce. Auto-generated if omitted."
          },
          "format": {
            "type": "string",
            "description": "NEP-413 only. `raw` is **rejected** (`400 bad_request`) — use [`authSign`](#tag/Wallet/operation/authSign) for OutLayer NEAR-key auth. The field is retained only to return that redirect error.",
            "enum": [
              "nep413"
            ],
            "default": "nep413"
          }
        }
      },
      "SignMessageResponse": {
        "type": "object",
        "properties": {
          "account_id": {
            "type": "string"
          },
          "signature": {
            "type": "string"
          },
          "signature_base64": {
            "type": "string"
          },
          "public_key": {
            "type": "string"
          },
          "nonce": {
            "type": "string"
          }
        }
      },
      "AuthSignRequest": {
        "type": "object",
        "required": [
          "purpose",
          "seed"
        ],
        "properties": {
          "purpose": {
            "type": "string",
            "description": "`bearer` → challenge `auth:<seed>:<ts>` (+`:<vault>` when `vault_id` set); `register` → `register:<seed>:<ts>`; `api-key` → `api-key:<seed>:<ts>`.",
            "enum": [
              "bearer",
              "register",
              "api-key"
            ]
          },
          "seed": {
            "type": "string",
            "description": "Wallet seed — selects which sub-wallet's key signs."
          },
          "vault_id": {
            "type": "string",
            "description": "Vault scope — only valid for `purpose=bearer`."
          }
        }
      },
      "AuthSignResponse": {
        "type": "object",
        "required": [
          "auth_message",
          "auth_timestamp",
          "signature",
          "public_key"
        ],
        "properties": {
          "auth_message": {
            "type": "string",
            "description": "The exact challenge string the keystore built and signed — send this verbatim."
          },
          "auth_timestamp": {
            "type": "integer",
            "format": "int64",
            "description": "Fresh server timestamp (unix seconds) baked into `auth_message`."
          },
          "signature": {
            "type": "string",
            "description": "Raw ed25519 signature over `auth_message`, base58 (no `ed25519:` prefix)."
          },
          "public_key": {
            "type": "string",
            "description": "Signer public key in `ed25519:<base58>` form."
          }
        }
      },
      "PolicyRules": {
        "type": "object",
        "properties": {
          "transaction_types": {
            "type": "array",
            "description": "Allowed op types (allowlist). Omit to allow all. `cross_chain_withdraw` must be listed explicitly to permit bridging out (it is NOT covered by `withdraw`). The deposit family gates as `call`.",
            "items": {
              "$ref": "#/components/schemas/RequestType"
            }
          },
          "allowed_tokens": {
            "type": "array",
            "description": "Token allowlist (defuse asset ids, or `\"*\"` for any). Omit or use `[\"*\"]` to allow all tokens.",
            "items": {
              "type": "string"
            }
          },
          "addresses": {
            "$ref": "#/components/schemas/AddressList"
          },
          "limits": {
            "$ref": "#/components/schemas/PolicyLimits"
          },
          "time_restrictions": {
            "$ref": "#/components/schemas/TimeRestrictions"
          },
          "rate_limit": {
            "$ref": "#/components/schemas/RateLimit"
          }
        }
      },
      "PolicyLimits": {
        "type": "object",
        "description": "Per-period spending limits. The amount fields are maps of\n`token_id → amount` (use `\"*\"` as the key to match all tokens).\n`per_transaction` is STATELESS (enforced on every signature inside the\nTEE); `hourly` / `daily` / `monthly` and `hourly_tx_count` are STATEFUL\nvelocity limits (checked against coordinator-supplied usage; best-effort\nunder concurrency).\n",
        "properties": {
          "per_transaction": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "hourly": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "daily": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "monthly": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "hourly_tx_count": {
            "type": "integer",
            "description": "Max number of transactions per rolling hour (transaction-count rate limit; equivalent to `rate_limit.max_per_hour`)."
          }
        }
      },
      "AddressList": {
        "type": "object",
        "required": [
          "mode",
          "list"
        ],
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "whitelist",
              "blacklist"
            ]
          },
          "list": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "TimeRestrictions": {
        "type": "object",
        "properties": {
          "timezone": {
            "type": "string",
            "description": "IANA timezone name, e.g. `UTC` or `Europe/Berlin`."
          },
          "allowed_hours": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 0,
              "maximum": 23
            }
          },
          "allowed_days": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 0,
              "maximum": 6
            },
            "description": "0 = Sunday, 6 = Saturday."
          }
        }
      },
      "RateLimit": {
        "type": "object",
        "properties": {
          "max_per_hour": {
            "type": "integer"
          }
        }
      },
      "ApprovalConfig": {
        "type": "object",
        "description": "Multisig approval configuration. When set, an op that trips the approval\ntrigger returns `pending_approval` and executes only after `threshold`\napprovers sign. Applies to fund ops including the Trusted kinds (swap,\nconfidential, cross_chain_withdraw); `payment_check` is excluded from the\ngeneric trigger (cap-gated instead).\n",
        "properties": {
          "threshold": {
            "description": "Number of approvals required — either a bare integer (e.g. `2`) or an object `{ \"required\": N }`.",
            "oneOf": [
              {
                "type": "integer",
                "minimum": 1
              },
              {
                "type": "object",
                "required": [
                  "required"
                ],
                "properties": {
                  "required": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            ]
          },
          "approvers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Approver"
            }
          },
          "excluded_types": {
            "type": "array",
            "description": "Op types exempt from the generic approval trigger.",
            "items": {
              "$ref": "#/components/schemas/RequestType"
            }
          }
        }
      },
      "Approver": {
        "type": "object",
        "required": [
          "id",
          "pubkey"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Approver's NEAR account id, e.g. `alice.near`."
          },
          "pubkey": {
            "type": "string",
            "description": "Approver's NEAR public key (`ed25519:<base58>`), pinned on-chain. The approver's NEP-413 vote signature is verified against this key."
          },
          "role": {
            "type": "string",
            "description": "Optional. `admin` may also modify policy / freeze; `signer` may only approve transactions. Defaults to `signer` when omitted.",
            "enum": [
              "admin",
              "signer"
            ]
          }
        }
      },
      "AdminQuorum": {
        "type": "object",
        "required": [
          "required",
          "admins"
        ],
        "properties": {
          "required": {
            "type": "integer"
          },
          "admins": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Capability": {
        "type": "object",
        "description": "A single non-Built primitive toggle. All capabilities default to DENY except `sign_message`. `requires_approval` opts that primitive into multisig specifically.",
        "properties": {
          "allowed": {
            "type": "boolean"
          },
          "requires_approval": {
            "type": "boolean"
          }
        }
      },
      "RawSignCapability": {
        "type": "object",
        "description": "`raw_sign` — sign arbitrary raw bytes. `chains` is an optional allowlist; when absent, ALL chains (including `near`, which can sign a NEAR tx/intent outside the structured policy — enable with care).",
        "properties": {
          "allowed": {
            "type": "boolean"
          },
          "requires_approval": {
            "type": "boolean"
          },
          "chains": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "SignMessageCapability": {
        "type": "object",
        "description": "`sign_message` — generic non-fund NEP-413 (e.g. dApp login). Defaults ALLOWED. `allowed_recipients` is a default-DENY allowlist of verifier recipients (NOT a blocklist); `intents.near` / `intents.far` are always excluded. This is NOT OutLayer auth (that is `authSign`).",
        "properties": {
          "allowed": {
            "type": "boolean"
          },
          "requires_approval": {
            "type": "boolean"
          },
          "allowed_recipients": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "EvmSignCapability": {
        "type": "object",
        "description": "`evm_sign` — EVM signing (EIP-712 typed-data / EIP-191 personal_sign / raw tx). **Default-DENY under a policy** (like the other fund-moving capabilities) — set `allowed: true` to permit. A wallet with no policy is unrestricted. CAVEAT (why it must be opt-in): an EIP-712 signature is itself fund-moving (EIP-3009 `TransferWithAuthorization` ≈ transfer, EIP-2612 `Permit` ≈ approve), so this grants full authority over the EVM address's float (bounded to what you bridge there — the NEAR-intents balance is never exposed to any EVM signature). `raw_tx` (default false) is a SEPARATE kill-switch for arbitrary raw transactions; it does NOT contain typed-data drains.",
        "properties": {
          "allowed": {
            "type": "boolean",
            "description": "Master on/off for EVM signing. Default false (opt in with true)."
          },
          "raw_tx": {
            "type": "boolean",
            "description": "Permit signing arbitrary raw EVM transactions. Default false."
          }
        }
      },
      "SolanaSignCapability": {
        "type": "object",
        "description": "`solana_sign` — Solana signing (off-chain messages / transaction messages). **Default-DENY under a policy** (same model as `evm_sign`) — set `allowed: true` to permit. A wallet with no policy is unrestricted. CAVEAT: a signed Solana transaction message is itself fund-moving, so `raw_tx` grants full authority over the Solana address's float (bounded to what you send there — the NEAR-intents balance is never exposed to any Solana signature). The base flag covers message signing only; the service rejects \"message\" bytes that parse as a valid transaction message, so the message endpoint cannot bypass `raw_tx`.",
        "properties": {
          "allowed": {
            "type": "boolean",
            "description": "Master on/off for Solana signing. Default false (opt in with true)."
          },
          "raw_tx": {
            "type": "boolean",
            "description": "Permit signing Solana transaction messages. Default false."
          }
        }
      },
      "Capabilities": {
        "type": "object",
        "description": "Default-DENY opt-ins for the non-Built primitives, stored alongside\n`rules` / `approval` in the encrypted policy. Every capability defaults to\nDENY under a policy **except** `sign_message` (default-allow). A wallet\nwith no policy at all is unrestricted.\n",
        "properties": {
          "raw_sign": {
            "$ref": "#/components/schemas/RawSignCapability"
          },
          "confidential": {
            "$ref": "#/components/schemas/Capability"
          },
          "swap": {
            "$ref": "#/components/schemas/Capability"
          },
          "cross_chain_withdraw": {
            "$ref": "#/components/schemas/Capability"
          },
          "payment_check": {
            "$ref": "#/components/schemas/Capability"
          },
          "sign_message": {
            "$ref": "#/components/schemas/SignMessageCapability"
          },
          "evm_sign": {
            "$ref": "#/components/schemas/EvmSignCapability"
          },
          "solana_sign": {
            "$ref": "#/components/schemas/SolanaSignCapability"
          }
        }
      },
      "EvmSignTypedDataRequest": {
        "type": "object",
        "required": [
          "chain",
          "typed_data"
        ],
        "properties": {
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "typed_data": {
            "type": "object",
            "description": "Standard EIP-712 v4 object (as `eth_signTypedData_v4`): `{ domain, types, primaryType, message }`. Arbitrary struct types are supported (incl. EIP-3009 `TransferWithAuthorization` and EIP-2612 `Permit`). The digest is computed server-side — no client-supplied hash is trusted."
          }
        }
      },
      "EvmSignMessageRequest": {
        "type": "object",
        "required": [
          "chain",
          "message"
        ],
        "properties": {
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "message": {
            "type": "string",
            "description": "The message to sign under EIP-191 `personal_sign`, interpreted per `encoding`."
          },
          "encoding": {
            "type": "string",
            "enum": [
              "utf8",
              "hex"
            ],
            "default": "utf8",
            "description": "How to interpret `message`. `utf8` (default) signs its UTF-8 bytes (matches viem `hashMessage(string)` / MetaMask `personal_sign`); `hex` treats `message` as hex (`0x`-prefixed or bare) and signs the decoded bytes (viem `hashMessage({ raw })`). No content sniffing — malformed hex is rejected."
          }
        }
      },
      "EvmSignTransactionRequest": {
        "type": "object",
        "required": [
          "chain",
          "unsigned_tx"
        ],
        "properties": {
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "unsigned_tx": {
            "type": "string",
            "description": "Serialized unsigned transaction, `0x`-hex (e.g. viem `serializeTransaction(tx)`). The service keccak256-hashes and signs it — it does not parse, assemble, or broadcast the transaction."
          }
        }
      },
      "EvmSignResponse": {
        "type": "object",
        "required": [
          "signature",
          "chain",
          "wallet_id"
        ],
        "properties": {
          "signature": {
            "type": "string",
            "description": "65-byte recoverable EVM signature, `0x`-hex (`r‖s‖v`, `v ∈ {27,28}`, low-s). `ecrecover` over the signed digest returns the wallet's EVM address."
          },
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "wallet_id": {
            "type": "string"
          }
        }
      },
      "SolanaSignMessageRequest": {
        "type": "object",
        "required": [
          "chain",
          "message"
        ],
        "properties": {
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "message": {
            "type": "string",
            "description": "The message to sign, interpreted per `encoding`. The decoded bytes are signed as-is; bytes that parse as a valid Solana transaction message are rejected (use `/wallet/v1/solana/sign-transaction`)."
          },
          "encoding": {
            "type": "string",
            "enum": [
              "utf8",
              "hex",
              "base64"
            ],
            "default": "utf8",
            "description": "How to interpret `message`. `utf8` (default) signs its UTF-8 bytes; `hex` (`0x`-prefixed or bare) / `base64` decode `message` first and sign the decoded bytes. No content sniffing — malformed input is rejected."
          }
        }
      },
      "SolanaSignTransactionRequest": {
        "type": "object",
        "required": [
          "chain",
          "unsigned_tx"
        ],
        "properties": {
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "unsigned_tx": {
            "type": "string",
            "description": "Serialized unsigned transaction **message**, base64 (what the signature covers: web3.js `tx.serializeMessage()` / `versionedTx.message.serialize()`; max 1232 bytes — the Solana packet limit). The service signs the bytes as-is — it does not parse, assemble, or broadcast the transaction."
          }
        }
      },
      "SolanaSignResponse": {
        "type": "object",
        "required": [
          "signature",
          "chain",
          "wallet_id"
        ],
        "properties": {
          "signature": {
            "type": "string",
            "description": "64-byte ed25519 signature, base58 (Solana convention). Verifies against the wallet's Solana address (its base58 ed25519 public key) over the exact submitted bytes."
          },
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "wallet_id": {
            "type": "string"
          }
        }
      },
      "PolicyResponse": {
        "type": "object",
        "required": [
          "wallet_id",
          "controller",
          "frozen"
        ],
        "properties": {
          "wallet_id": {
            "type": "string"
          },
          "controller": {
            "type": "string",
            "description": "NEAR account that owns the policy."
          },
          "frozen": {
            "type": "boolean"
          },
          "rules": {
            "$ref": "#/components/schemas/PolicyRules"
          },
          "approval": {
            "$ref": "#/components/schemas/ApprovalConfig"
          },
          "capabilities": {
            "$ref": "#/components/schemas/Capabilities"
          },
          "authorized_key_hashes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "usage": {
            "type": "object",
            "additionalProperties": {},
            "description": "Current accumulated usage per token, per period. Same shape as\n`PolicyLimits` — used for client-side velocity-limit visualization.\n"
          }
        }
      },
      "EncryptPolicyRequest": {
        "type": "object",
        "required": [
          "wallet_id",
          "rules"
        ],
        "properties": {
          "wallet_id": {
            "type": "string"
          },
          "rules": {
            "$ref": "#/components/schemas/PolicyRules"
          },
          "approval": {
            "$ref": "#/components/schemas/ApprovalConfig"
          },
          "capabilities": {
            "$ref": "#/components/schemas/Capabilities"
          },
          "admin_quorum": {
            "$ref": "#/components/schemas/AdminQuorum"
          },
          "webhook_url": {
            "type": "string",
            "format": "uri"
          },
          "authorized_key_hashes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "EncryptPolicyResponse": {
        "type": "object",
        "required": [
          "encrypted_base64",
          "wallet_pubkey"
        ],
        "properties": {
          "encrypted_base64": {
            "type": "string"
          },
          "wallet_pubkey": {
            "type": "string"
          }
        }
      },
      "SignPolicyRequest": {
        "type": "object",
        "required": [
          "encrypted_data",
          "caller"
        ],
        "properties": {
          "encrypted_data": {
            "type": "string"
          },
          "caller": {
            "type": "string",
            "description": "The NEAR account that will send `store_wallet_policy`. Signed, so\nthe signature is usable by that account only.\n"
          }
        }
      },
      "SignPolicyResponse": {
        "type": "object",
        "required": [
          "signature_hex",
          "public_key_hex"
        ],
        "properties": {
          "signature_hex": {
            "type": "string"
          },
          "public_key_hex": {
            "type": "string"
          }
        }
      },
      "InvalidateCacheRequest": {
        "type": "object",
        "required": [
          "wallet_id"
        ],
        "properties": {
          "wallet_id": {
            "type": "string"
          }
        }
      },
      "PendingApproval": {
        "type": "object",
        "required": [
          "approval_id",
          "type",
          "request_data",
          "request_hash",
          "decision",
          "required",
          "approved",
          "expires_at"
        ],
        "properties": {
          "approval_id": {
            "type": "string",
            "format": "uuid"
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "type": {
            "$ref": "#/components/schemas/RequestType"
          },
          "request_data": {
            "type": "object",
            "additionalProperties": {}
          },
          "op": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {},
            "description": "Canonical op to RENDER (parsed from the stored canonical JSON). The dashboard renders this directly. `null` for legacy rows predating canonical-op storage."
          },
          "decoded_effects": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {},
            "description": "Present ONLY for a `w_execute_extension` op (Agent Connect's extension door), whose own fields describe nothing: the destination is the agent's own account and the deposit is a mandated 1-yocto marker, while every real recipient and amount is nested in `args_base64`. This is those contents, decoded from the same bytes `request_hash` covers — an approver renders it INSTEAD of guessing from `op`. Absent for every other operation, which states its effects in its own fields.\n\nShape: `native_total` (yocto, decimal string), `action_count`, `promises[]` (`index`, `receiver`, `refund_to`, `native`), `token_moves[]` (`promise`, `method`, `token`, `recipient`, `amount`, `unit` = `token_units` | `token_id`), `storage_registrations[]`, plus `internal_ops[]` and `unstatable_calls[]` — the last two are refused before an approval can exist and are rendered only so their presence could never be silent."
          },
          "request_hash": {
            "type": "string",
            "description": "Canonical request hash to SIGN — approvers sign `approve:{approval_id}:{wallet_pubkey}:{request_hash}` (see `Nep413Auth`)."
          },
          "decision": {
            "type": "string",
            "description": "Always `requires_approval` for a pending row.",
            "enum": [
              "requires_approval"
            ]
          },
          "required": {
            "type": "integer"
          },
          "approved": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PendingApprovalsResponse": {
        "type": "object",
        "required": [
          "approvals"
        ],
        "properties": {
          "approvals": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PendingApproval"
            }
          }
        }
      },
      "ApprovalDetail": {
        "type": "object",
        "description": "Public, read-only detail for one pending approval (`getApprovalDetail`). Returns only non-sensitive metadata. `wallet_pubkey` + `request_hash` are what an approver binds into the NEP-413 vote (see `Nep413Auth`); the dashboard renders `op`.",
        "required": [
          "id",
          "wallet_id",
          "request_type",
          "request_data",
          "status",
          "required_approvals",
          "request_hash",
          "wallet_pubkey",
          "expires_at",
          "created_at",
          "approvers"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "wallet_id": {
            "type": "string"
          },
          "request_type": {
            "$ref": "#/components/schemas/RequestType"
          },
          "request_data": {
            "type": "object",
            "additionalProperties": {}
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "rejected",
              "expired"
            ]
          },
          "required_approvals": {
            "type": "integer"
          },
          "request_hash": {
            "type": "string"
          },
          "op": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {},
            "description": "Canonical op for the dashboard to render. `null` for legacy rows."
          },
          "decoded_effects": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {},
            "description": "What a `w_execute_extension` op actually moves, decoded from the same bytes `request_hash` covers — see `PendingApproval` for the shape and for why rendering `op` alone would show an approver nothing about the money. `null` for every other operation."
          },
          "wallet_pubkey": {
            "type": "string",
            "description": "On-chain wallet pubkey bound into the approve/reject vote message."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "approvers": {
            "type": "array",
            "description": "Signatures collected so far.",
            "items": {
              "type": "object",
              "required": [
                "approver_id",
                "approver_role",
                "signature",
                "created_at"
              ],
              "properties": {
                "approver_id": {
                  "type": "string"
                },
                "approver_role": {
                  "type": "string"
                },
                "signature": {
                  "type": "string"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "Nep413Auth": {
        "type": "object",
        "description": "NEP-413 signed authentication payload. The signed message binds the\nwallet's on-chain pubkey so a vote can't be replayed onto another wallet:\n`approve:{approval_id}:{wallet_pubkey}:{request_hash}` for approve, or\n`reject:{approval_id}:{wallet_pubkey}:{request_hash}` for reject. The\n`wallet_pubkey` and `request_hash` come from `getApprovalDetail` (or\n`listPendingApprovals`). `recipient` for the NEP-413 signature is the\nwallet contract id.\n",
        "required": [
          "signature",
          "public_key",
          "account_id",
          "nonce"
        ],
        "properties": {
          "signature": {
            "type": "string"
          },
          "public_key": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "nonce": {
            "type": "string"
          }
        }
      },
      "RejectRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Nep413Auth"
          },
          {
            "type": "object",
            "properties": {
              "reason": {
                "type": "string"
              }
            }
          }
        ]
      },
      "ApproveResponse": {
        "type": "object",
        "required": [
          "approval_id",
          "status",
          "approved",
          "required"
        ],
        "properties": {
          "approval_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "rejected",
              "expired"
            ]
          },
          "approved": {
            "type": "integer"
          },
          "required": {
            "type": "integer"
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "RejectResponse": {
        "type": "object",
        "required": [
          "approval_id",
          "status"
        ],
        "properties": {
          "approval_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "description": "`rejected` — the reject came from a REAL policy approver, so the request is cancelled (a real reject vetoes it irrevocably). `reject_vote_recorded` — the signer is not a configured approver: the vote is stored and the request stays `pending_approval` (the keystore still vetoes execution at sign time).",
            "enum": [
              "rejected",
              "reject_vote_recorded"
            ]
          }
        }
      },
      "AuditEvent": {
        "type": "object",
        "required": [
          "type",
          "details",
          "at"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "details": {
            "type": "object",
            "additionalProperties": {}
          },
          "at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AuditResponse": {
        "type": "object",
        "required": [
          "events"
        ],
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuditEvent"
            }
          }
        }
      },
      "RequestStatusResponse": {
        "type": "object",
        "required": [
          "request_id",
          "type",
          "status",
          "created_at"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "$ref": "#/components/schemas/RequestType"
          },
          "status": {
            "$ref": "#/components/schemas/RequestStatus"
          },
          "result": {
            "description": "Operation-specific result payload, present once `status` is\n`success` (otherwise `null` or an `{ \"error\": \"<reason>\" }` object\non `failed`). The shape depends on `type`:\n\n- `type = \"withdraw\"` — conforms to\n  [`WithdrawResult`](#/components/schemas/WithdrawResult).\n- Cross-chain withdraws (`intents_cross_chain_withdraw`) and\n  gasless swaps additionally carry a nullable\n  `destination_tx_hash` — the real delivery transaction on the\n  destination chain (safe to render as an explorer link on the\n  requested chain). `null` until the 1Click bridge settles; the\n  lazy on-read refresh fills it in, and the `request_completed`\n  webhook carries it. All other identifier fields\n  (`transfer_intent_hash`, `intent_hash`) are NEAR-Intents\n  hashes, NOT destination-chain transactions.\n- Other request types are not yet schema-documented; treat the\n  object as opaque until added in a later spec release.\n\nUses `anyOf` (not `oneOf`) deliberately — `WithdrawResult` is also\na valid `object`, and `oneOf` would require exactly one match,\nso a successful withdraw would fail strict validators.",
            "anyOf": [
              {
                "$ref": "#/components/schemas/WithdrawResult"
              },
              {
                "type": "null"
              },
              {
                "type": "object",
                "additionalProperties": {}
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "WithdrawResult": {
        "type": "object",
        "description": "Result payload for a successful `withdraw` request — what the\nrecipient actually received on-chain. Emitted in the `result` field of\n`GET /wallet/v1/requests/{id}` and in the `result` field of the\n`request_completed` webhook for withdraw requests.",
        "required": [
          "intent_hash",
          "delivered"
        ],
        "properties": {
          "intent_hash": {
            "type": "string",
            "description": "Base58-encoded NEAR Intents hash returned by `solver-relay`.\nEmpty string if the relay did not return one — the field is\nalways present so clients can index it without `Option` handling.",
            "example": "4GArqMi1WJwsw7p4tWHAbYG2Se5Wp6YSb8Ab3C8cUTJg"
          },
          "delivered": {
            "description": "Canonical identifier of the asset the recipient received on-chain.\nFormat mirrors the request `token` field of\n`POST /wallet/v1/intents/withdraw`, so clients can compare request\nvs. response directly:\n\n- `\"native_near\"` — the wallet emitted a `native_withdraw` intent\n  that unwrapped wNEAR and sent native NEAR. The recipient\n  received native NEAR regardless of what the request `token`\n  field named (`\"\"`, `\"near\"`, or `\"native\"`).\n- `\"nep141:<contract>\"` — the wallet emitted a NEP-141 `transfer`\n  intent. The recipient received that token, including wNEAR\n  (`\"nep141:wrap.near\"`), USDC, USDT, etc.\n\nNever returned: the legacy short symbol `\"wnear\"` (was emitted\nin error by coordinator versions prior to the\n`WithdrawResult`-typed response — see api-spec CHANGELOG).",
            "oneOf": [
              {
                "type": "string",
                "const": "native_near"
              },
              {
                "type": "string",
                "pattern": "^nep141:.+",
                "example": "nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1"
              }
            ]
          }
        }
      },
      "RequestListResponse": {
        "type": "object",
        "required": [
          "requests",
          "total"
        ],
        "properties": {
          "requests": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RequestStatusResponse"
            }
          },
          "total": {
            "type": "integer"
          }
        }
      },
      "ConfidentialShieldRequest": {
        "description": "SHIELD body — same shape as `IntentsDepositRequest`.",
        "allOf": [
          {
            "$ref": "#/components/schemas/IntentsDepositRequest"
          }
        ]
      },
      "ConfidentialUnshieldRequest": {
        "description": "UNSHIELD body — same shape as `IntentsDepositRequest`.",
        "allOf": [
          {
            "$ref": "#/components/schemas/IntentsDepositRequest"
          }
        ]
      },
      "ConfidentialWithdrawRequest": {
        "description": "Confidential withdraw body — same shape as `WithdrawRequest`; `token` is required (the source confidential asset to deliver). `chain` must be the token's home chain or `\"near\"` (mismatches are rejected). `chain=\"near\"` delivers native NEAR for `nep141:wrap.near` (via `intents.near native_withdraw`) and the NEP-141 token on NEAR for omft bridge assets (use `confidentialUnshield` if you want to send to your own public balance instead).",
        "allOf": [
          {
            "$ref": "#/components/schemas/WithdrawRequest"
          }
        ]
      },
      "ConfidentialSwapRequest": {
        "description": "Confidential swap body — same shape as `SwapRequest` (`token_in` / `amount_in` / `token_out` / optional `min_amount_out`). On a multisig wallet the confidential swap binds `token_out` and `min_amount_out` into the approved op (like a public swap), so approvers commit to the output terms — a compromised coordinator cannot change them after approval.",
        "allOf": [
          {
            "$ref": "#/components/schemas/SwapRequest"
          }
        ]
      },
      "ConfidentialDepositIntentRequest": {
        "description": "Cross-chain deposit body — same shape as `DepositIntentRequest` (`source_asset` or `chain`+`token`). NOTE: for the confidential endpoint `destination_asset` and `refund_address` are **ignored** — the destination is forced to the origin asset (same-asset cross-chain deposit into the confidential shard) and refund is forced to the wallet's 64-hex intentsUserId (the `refundType=CONFIDENTIAL_INTENTS` invariant).",
        "allOf": [
          {
            "$ref": "#/components/schemas/DepositIntentRequest"
          }
        ]
      },
      "ConfidentialDepositIntentResponse": {
        "description": "Cross-chain deposit address — same shape as `DepositIntentResponse`.",
        "allOf": [
          {
            "$ref": "#/components/schemas/DepositIntentResponse"
          }
        ]
      },
      "ConfidentialTransferRequest": {
        "type": "object",
        "description": "Private confidential→confidential transfer. No `chain` (NEAR-only context); `token` required (no native-asset concept inside the confidential shard).",
        "required": [
          "to",
          "amount",
          "token"
        ],
        "properties": {
          "to": {
            "type": "string",
            "description": "Recipient NEAR account id (intentsUserId / 64-hex implicit account)."
          },
          "amount": {
            "type": "string",
            "description": "Amount in minimal units."
          },
          "token": {
            "type": "string",
            "description": "Defuse asset id, e.g. `nep141:wrap.near`."
          }
        }
      },
      "ConfidentialOpResponse": {
        "type": "object",
        "description": "Result of a confidential shield / unshield / withdraw / transfer / swap.\nThe op is asynchronous — the user's signed intent settles on the private\nshard (`intents.far`, no public RPC), so there is no public `tx_hash`;\ntrack via `intent_hash` / `deposit_address` and poll\n`GET /wallet/v1/requests/{id}`.\n\nOnce the op is terminal, the request row's `result.swap_details`\ncarries `intentHashes`, `nearTxHashes`, `originChainTxHashes` and\n`destinationChainTxHashes` — **all arrays of plain hash strings**,\nwith 1Click-style **camelCase** inner keys (the `swap_details`\ncontainer itself is snake_case). Upstream 1Click emits\n`{hash, explorerUrl}` objects; the coordinator normalizes them to\nplain strings, so this wire shape is stable regardless of upstream\nchanges. Also present: settled amounts and refund fields\n(`amountIn`, `amountOut`, `refundedAmount`, `refundReason`, …). For\nan external-chain `confidentialWithdraw`, `destinationChainTxHashes`\nholds the actual delivery transaction on the destination chain; the\narrays are empty until settlement and may stay empty for\nshard-internal ops (shield / unshield / transfer / swap).\n\n**Multisig:** the Trusted confidential ops (withdraw / transfer / swap)\nare subject to the wallet's approval policy. When approval is required the\nresponse inlines `approval_id` / `required` / `approved` / `request_hash`\n(same shape as `WithdrawResponse` / `SwapResponse`) — sign `request_hash`\nand submit your approval vote; the op executes after the threshold is met.\n",
        "required": [
          "request_id",
          "status"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "description": "Normalized lifecycle status (1Click's UPPERCASE state machine mapped to lowercase): `pending_deposit` → `processing` → `success` / `failed` / `refunded`. `pending_approval` is returned instead when a multisig wallet must approve the op first. The raw upstream status is in the request row's `result.oneclick_status`.",
            "enum": [
              "pending_deposit",
              "processing",
              "success",
              "failed",
              "refunded",
              "pending_approval"
            ]
          },
          "intent_hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Confidential-shard intent hash (never appears on the public chain)."
          },
          "deposit_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque 1Click hop address — the status primary key."
          },
          "approval_id": {
            "type": "string",
            "format": "uuid",
            "description": "Present only on `pending_approval` (multisig) — the approval to vote on."
          },
          "required": {
            "type": "integer",
            "description": "Present only on `pending_approval` — approvals needed."
          },
          "approved": {
            "type": "integer",
            "description": "Present only on `pending_approval` — approvals collected so far (0 at creation)."
          },
          "request_hash": {
            "type": "string",
            "description": "Present only on `pending_approval` — sha256(canonical_json(op)); sign this to approve."
          }
        }
      },
      "ConfidentialBalanceResponse": {
        "type": "object",
        "description": "A single confidential balance (response to `?token=`).",
        "required": [
          "balance",
          "token",
          "account_id"
        ],
        "properties": {
          "balance": {
            "type": "string",
            "description": "Minimal units; `\"0\"` if the wallet holds none of this asset."
          },
          "token": {
            "type": "string"
          },
          "account_id": {
            "type": "string",
            "description": "Wallet's 64-hex NEAR implicit account (intentsUserId)."
          }
        }
      },
      "ConfidentialBalanceEntry": {
        "type": "object",
        "required": [
          "token",
          "balance"
        ],
        "properties": {
          "token": {
            "type": "string"
          },
          "balance": {
            "type": "string"
          }
        }
      },
      "ConfidentialBalancesResponse": {
        "type": "object",
        "description": "All confidential balances (response when `token` is omitted).",
        "required": [
          "balances",
          "account_id"
        ],
        "properties": {
          "balances": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfidentialBalanceEntry"
            }
          },
          "account_id": {
            "type": "string"
          }
        }
      }
    }
  }
}