openapi: 3.0.3
info:
  title: Para REST API
  version: "1.0"
  description: Server-to-server wallet creation and signing over HTTP

servers:
  - url: https://api.beta.getpara.com
    description: Beta
  - url: https://api.getpara.com
    description: Production

security:
  - ApiKeyAuth: []

paths:
  /v1/address-screening/status:
    get:
      operationId: getAddressScreeningStatus
      summary: Get Address Screening Status
      description: Check whether an address is sanctioned.
      tags:
        - Screening
      parameters:
        - $ref: "#/components/parameters/RequestId"
        - name: address
          in: query
          required: true
          description: Address to screen.
          schema:
            type: string
            example: "0x000000000000000000000000000000000000dEaD"
      responses:
        "200":
          description: Address screening status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AddressScreeningStatus"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: Address screening is unavailable.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                screeningUnavailable:
                  summary: Address screening unavailable
                  value:
                    code: "ADDRESS_SCREENING_UNAVAILABLE"
                    message: "Address screening is unavailable"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets:
    get:
      operationId: listWallets
      summary: List Wallets
      description: List wallets with optional filters and cursor-based pagination.
      tags:
        - Wallets
      parameters:
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
        - name: userIdentifier
          in: query
          required: false
          description: Filter by user identifier
          schema:
            type: string
            example: "alice@example.com"
        - name: userIdentifierType
          in: query
          required: false
          description: Type of user identifier. Required when userIdentifier is set.
          schema:
            type: string
            enum: [EMAIL, PHONE, CUSTOM_ID, GUEST_ID, DISCORD, TWITTER, TELEGRAM, FARCASTER]
        - name: type
          in: query
          required: false
          description: Filter by blockchain network type
          schema:
            type: string
            enum: [EVM, SOLANA, COSMOS, STELLAR, SUI]
        - name: status
          in: query
          required: false
          description: Filter by wallet creation status
          schema:
            type: string
            enum: [creating, ready]
        - name: address
          in: query
          required: false
          description: Filter by on-chain wallet address
          schema:
            type: string
      responses:
        "200":
          description: Paginated list of wallets
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WalletListResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

    post:
      operationId: createWallet
      summary: Create Wallet
      description: Create a new partner-owned wallet for a user.
      tags:
        - Wallets
      parameters:
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWalletRequest"
      responses:
        "201":
          description: Wallet created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Wallet"
          headers:
            Location:
              schema:
                type: string
              description: Path to the created wallet
              example: /v1/wallets/0a1b2c3d-4e5f-6789-abcd-ef0123456789
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          description: |
            Conflict. Either:
            - A wallet with the same type + scheme + userIdentifier already exists (code: WALLET_ALREADY_EXISTS)
            - A request with this idempotency key is currently being processed (code: CONFLICT)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                walletExists:
                  summary: Duplicate wallet
                  value:
                    code: "WALLET_ALREADY_EXISTS"
                    message: "a wallet for this identifier and type already exists"
                    walletId: "0a1b2c3d-4e5f-6789-abcd-ef0123456789"
                idempotencyConflict:
                  summary: Idempotency key in-flight
                  value:
                    code: "CONFLICT"
                    message: "A request with this idempotency key is currently being processed"
        "422":
          $ref: "#/components/responses/Unprocessable"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}:
    get:
      operationId: getWallet
      summary: Get Wallet
      description: Retrieve wallet details by ID.
      tags:
        - Wallets
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Wallet details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Wallet"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"
    patch:
      operationId: updateWalletIdentifier
      summary: Update Wallet Identifier
      description: |
        Update the user identifier on an unclaimed (pregen) wallet.
        Only works on wallets that have not yet been claimed by a user.
      tags:
        - Wallets
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateWalletIdentifierRequest"
      responses:
        "200":
          description: Updated wallet
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Wallet"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          description: Wallet has already been claimed by a user
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                code: "WALLET_ALREADY_CLAIMED"
                message: "cannot update identifier on a claimed wallet"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/migrate-share:
    post:
      operationId: migrateShare
      summary: Migrate SDK Pregen Wallet Share
      description: |
        Migrate an SDK-pregenerated wallet to the REST API by persisting its user share
        in Para's hardware-isolated enclave.

        The request body must contain an `encryptedPayload` — the user share encrypted with
        the enclave's P-256 public key using ECIES. The SDK's `migrateWalletShare()` method
        handles encryption automatically. Non-SDK callers must implement ECIES-P256 encryption
        using the key from `GET /v1/enclave/public-key`.

        After migration the wallet's `sharesPersisted` flag becomes `true` and the wallet
        can be used with all REST API signing endpoints (`sign-raw`, `sign-transaction`,
        `sign-message`, `sign-typed-data`, `transfer`).

        Migration is additive — the original SDK signing flow continues to work.
      tags:
        - Wallets
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MigrateShareRequest"
      responses:
        "200":
          description: Wallet migrated successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Wallet"
        "400":
          description: Missing encryptedPayload in request body
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                code: "INVALID_REQUEST"
                message: "encryptedPayload is required"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          description: Wallet shares are already persisted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                code: "CONFLICT"
                message: "shares already persisted for this wallet"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/enclave/public-key:
    get:
      operationId: getEnclavePublicKey
      summary: Get Enclave Public Key
      description: |
        Retrieve the enclave's P-256 public key in PEM format. Non-SDK callers need this
        key to implement ECIES encryption for the `migrate-share` endpoint.

        SDK users do not need to call this endpoint directly — the `migrateWalletShare()`
        method fetches and caches the key automatically.
      tags:
        - Enclave
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Enclave public key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EnclavePublicKeyResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/balance:
    get:
      operationId: getWalletBalance
      summary: Get Wallet Balance
      description: |
        Retrieve the native or token balance for a wallet.

        **EVM wallets:** Provide `chainId`. Optionally provide `tokenAddress` to query an ERC-20 token balance
        instead of the native balance.

        **Solana wallets:** Optionally provide `network` (`SOLANA` or `SOLANA_DEVNET`; defaults based on environment).
        Provide `tokenAddress` (SPL mint address) to query an SPL token balance instead of native SOL.

        Not supported for COSMOS, STELLAR, or SUI wallets.
      tags:
        - Wallets
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
        - name: chainId
          in: query
          required: false
          description: "EVM chain ID. Required for EVM wallets."
          schema:
            type: string
            example: "1"
        - name: tokenAddress
          in: query
          required: false
          description: "Token contract address. EVM: ERC-20 contract. Solana: SPL mint address. Omit for native balance."
          schema:
            type: string
        - name: network
          in: query
          required: false
          description: "Solana network. In production, defaults to SOLANA. In beta, only SOLANA_DEVNET is allowed (and is the default)."
          schema:
            type: string
            enum: [SOLANA, SOLANA_DEVNET]
      responses:
        "200":
          description: Wallet balance
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WalletBalanceResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/sign-raw:
    post:
      operationId: signRaw
      summary: Sign Raw
      description: |
        Sign raw bytes with a wallet's private key share via MPC.

        The input `data` is hex-encoded bytes. The MPC protocol signs these bytes
        directly — no hashing or prefix is applied, and no chain-specific logic
        is involved. This makes `sign-raw` suitable for **any chain** that uses
        the same curve as the wallet's key scheme:

        - **DKLS/CGGMP wallets** (EVM, COSMOS) use secp256k1 — compatible with
          Bitcoin and any other secp256k1-based chain.
        - **ED25519 wallets** (SOLANA, STELLAR, SUI) use Ed25519 — compatible with any
          Ed25519-based chain.

        To use Para wallets with an unsupported chain, create a wallet with the
        matching scheme (e.g., an EVM wallet for secp256k1), pre-hash your
        transaction on the client side, then call `sign-raw` with the hash. Derive
        the chain-specific address from the wallet's `publicKey`.

        Use `sign-transaction` or `sign-message` instead if you want Para to
        handle chain-specific serialization for EVM, Solana, Stellar, or Sui.

        The response `signature` is a hex string (no `0x` prefix). For DKLS/CGGMP
        schemes this is 65 bytes: `r (32) + s (32) + v (1)`.
      tags:
        - Signing
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignRawRequest"
      responses:
        "200":
          description: Signature
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignRawResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: |
            Forbidden. Either the API key is invalid (`FORBIDDEN`) or a policy
            denied the operation (`POLICY_DENIED`).
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/PolicyDeniedError"
              examples:
                forbidden:
                  summary: Invalid API key
                  value:
                    code: "FORBIDDEN"
                    message: "invalid secret api key"
                policyDenied:
                  summary: Denied by policy
                  value:
                    code: "POLICY_DENIED"
                    message: "Transaction denied by policy"
                    deniedBy:
                      scopeName: "signing"
                      permissionType: "SIGN_MESSAGE"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/IdempotencyConflict"
        "422":
          $ref: "#/components/responses/Unprocessable"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/sign-transaction:
    post:
      operationId: signTransaction
      summary: Sign Transaction
      description: |
        Sign a transaction with a wallet's private key share via MPC.

        **EVM:** Provide unsigned transaction fields as a JSON object and the endpoint
        will RLP-encode them, sign via MPC, and return a fully serialized signed
        transaction (hex-encoded with `0x` prefix) ready for broadcast.

        **Solana:** Provide a base64-encoded serialized Solana transaction (as built
        by `@solana/web3.js`). Both legacy `Transaction` and versioned
        `VersionedTransaction` (v0, with Address Lookup Tables) are accepted; the
        endpoint auto-detects the format, signs via MPC, and returns the signed
        transaction as a base64 string ready for broadcast.

        Set top-level `broadcast: true` to broadcast the signed transaction for EVM
        or Solana wallets. Broadcasted calls create a persisted transaction record,
        return `txHash` and `transactionId`, and set `x-transaction-id`. Omitted
        `broadcast` remains sign-only and writes no transaction-history row. Stellar
        wallets do not support `broadcast: true`.

        **Stellar:** Provide a base64-encoded Stellar transaction envelope (XDR) in
        the `transaction` field and the network passphrase in `networkPassphrase`.
        The endpoint computes the transaction hash, signs it via MPC, and returns
        the signed transaction as a base64-encoded XDR string.

        **Sui:** Provide a base64-encoded, BCS-serialized transaction (`TransactionData`)
        in the `transaction` field. The endpoint computes the Sui intent digest, signs it
        via MPC, and returns the serialized Sui signature
        (`base64(flag || signature || publicKey)`) in `signature` alongside the echoed
        `transaction` bytes. Sui does not support `broadcast: true` or `networkPassphrase`.
      tags:
        - Signing
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignTransactionRequest"
      responses:
        "200":
          description: Signed transaction
          headers:
            x-transaction-id:
              schema:
                type: string
                format: uuid
              description: "Present only when `broadcast: true` created a persisted transaction record."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignTransactionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: |
            Forbidden. Either the API key is invalid (`FORBIDDEN`) or a policy
            denied the operation (`POLICY_DENIED`).
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/PolicyDeniedError"
              examples:
                forbidden:
                  summary: Invalid API key
                  value:
                    code: "FORBIDDEN"
                    message: "invalid secret api key"
                policyDenied:
                  summary: Denied by policy
                  value:
                    code: "POLICY_DENIED"
                    message: "Transaction denied by policy"
                    deniedBy:
                      scopeName: "transfers"
                      permissionType: "TRANSFER"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/IdempotencyConflict"
        "422":
          $ref: "#/components/responses/Unprocessable"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/sign-message:
    post:
      operationId: signMessage
      summary: Sign Message
      description: |
        Sign a human-readable message with a wallet's private key share via MPC.

        For EVM wallets, the EIP-191 prefix (`\x19Ethereum Signed Message:\n<length>`)
        is prepended and the result is keccak256-hashed before MPC signing. For Solana,
        Cosmos, and Stellar wallets, the raw UTF-8 bytes of the message are signed directly.
        For Sui wallets, the message is wrapped in the Sui `PersonalMessage` intent and
        blake2b-256 hashed before MPC signing.

        The response `signature` is hex-encoded without a `0x` prefix, except for Sui
        wallets, which return the serialized Sui signature
        (`base64(flag || signature || publicKey)`).
      tags:
        - Signing
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignMessageRequest"
      responses:
        "200":
          description: Signature
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignMessageResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: |
            Forbidden. Either the API key is invalid (`FORBIDDEN`) or a policy
            denied the operation (`POLICY_DENIED`).
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/PolicyDeniedError"
              examples:
                forbidden:
                  summary: Invalid API key
                  value:
                    code: "FORBIDDEN"
                    message: "invalid secret api key"
                policyDenied:
                  summary: Denied by policy
                  value:
                    code: "POLICY_DENIED"
                    message: "Transaction denied by policy"
                    deniedBy:
                      scopeName: "signing"
                      permissionType: "SIGN_MESSAGE"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/IdempotencyConflict"
        "422":
          $ref: "#/components/responses/Unprocessable"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/sign-typed-data:
    post:
      operationId: signTypedData
      summary: Sign Typed Data
      description: |
        Signs EIP-712 typed structured data. Only supported for EVM wallets.

        Computes the EIP-712 hash of the provided domain, types, and message, then
        signs via MPC. The `EIP712Domain` type is handled automatically and should
        be omitted from `types`.
      tags:
        - Signing
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignTypedDataRequest"
      responses:
        "200":
          description: Signature
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignTypedDataResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: |
            Forbidden. Either the API key is invalid (`FORBIDDEN`) or a policy
            denied the operation (`POLICY_DENIED`).
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/PolicyDeniedError"
              examples:
                forbidden:
                  summary: Invalid API key
                  value:
                    code: "FORBIDDEN"
                    message: "invalid secret api key"
                policyDenied:
                  summary: Denied by policy
                  value:
                    code: "POLICY_DENIED"
                    message: "Transaction denied by policy"
                    deniedBy:
                      scopeName: "signing"
                      permissionType: "SIGN_MESSAGE"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/IdempotencyConflict"
        "422":
          $ref: "#/components/responses/Unprocessable"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/sign-authorization:
    post:
      operationId: signAuthorization
      summary: Sign Authorization (EIP-7702)
      description: |
        Signs an EIP-7702 authorization for account delegation. Only supported for
        EVM wallets.

        EIP-7702 allows an EOA to delegate execution to a smart contract address
        for a single transaction, enabling features like gas sponsorship and
        batched calls without migrating to a new contract wallet address. This is
        used by account abstraction providers such as ZeroDev, Alchemy, Pimlico,
        and others operating in 7702 mode.

        The endpoint computes the authorization hash per EIP-7702
        (`keccak256(0x05 || rlp([chainId, address, nonce]))`), signs it via MPC,
        and returns the decomposed signature with `yParity` (0 or 1) — not
        the legacy `v` (27/28) used by other signing endpoints.

        Both `address` and `contractAddress` are accepted in the request body as
        the delegation target. This matches viem's `signAuthorization` API.
      tags:
        - Signing
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignAuthorizationRequest"
      responses:
        "200":
          description: Signed authorization
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignAuthorizationResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/IdempotencyConflict"
        "422":
          $ref: "#/components/responses/Unprocessable"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/transfer:
    post:
      operationId: transfer
      summary: Transfer
      description: |
        High-level transfer endpoint that handles nonce, gas, and fee estimation
        automatically. Supports native and token transfers on EVM and Solana.

        **EVM:** Provide `to`, `value`, and `chainId`. The endpoint auto-fills `nonce`
        (via `getTransactionCount`), `gasLimit` (via `estimateGas` with 20% buffer),
        and fee data (`maxFeePerGas`/`gasPrice` via `getFeeData`) if omitted. Transaction
        `type` is auto-detected from chain (EIP-1559 if supported, legacy otherwise).
        For ERC-20 transfers, provide `tokenAddress` — the endpoint encodes the
        `transfer(address,uint256)` calldata automatically.

        **Solana:** Provide `to` and `value` (in lamports for SOL, smallest unit for SPL).
        For SPL token transfers, also provide `tokenAddress` (the mint address).
        Blockhash is fetched automatically.

        Address screening is enabled by default when Para has screening configured.
        Set `addressScreening: false` to skip recipient screening for a request.

        Use `kind` to explicitly declare the transfer type (`NATIVE`, `ERC20`, or `SPL`),
        or omit it to infer from the presence of `tokenAddress` (backwards compatible).
      tags:
        - Signing
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransferRequest"
      responses:
        "200":
          description: Signed transaction
          headers:
            x-transaction-id:
              schema:
                type: string
                format: uuid
              description: "Set when the transfer was broadcast (default behavior). Matches the persisted transaction record id."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransferResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: |
            Forbidden. Either the API key is invalid (`FORBIDDEN`) or a policy
            denied the operation (`POLICY_DENIED`).
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/PolicyDeniedError"
              examples:
                forbidden:
                  summary: Invalid API key
                  value:
                    code: "FORBIDDEN"
                    message: "invalid secret api key"
                policyDenied:
                  summary: Denied by policy
                  value:
                    code: "POLICY_DENIED"
                    message: "Transaction denied by policy"
                    deniedBy:
                      scopeName: "transfers"
                      permissionType: "TRANSFER"
                      condition:
                        resource: "VALUE"
                        comparator: "GREATER_THAN"
                        reference: "1000000000000000000"
                sanctionedAddress:
                  summary: Sanctioned recipient
                  value:
                    code: "SANCTIONED_ADDRESS"
                    message: "Recipient address is sanctioned"
                screeningUnavailable:
                  summary: Address screening unavailable
                  value:
                    code: "ADDRESS_SCREENING_UNAVAILABLE"
                    message: "Address screening is unavailable"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/IdempotencyConflict"
        "422":
          $ref: "#/components/responses/Unprocessable"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /faucet:
    post:
      operationId: requestFaucet
      summary: Request Testnet Tokens
      description: |
        Send testnet tokens to a Para wallet. The wallet must belong to an
        authenticated user under the partner's API key. Rate limited to 10
        requests per API key per day, with a 24-hour cooldown per wallet.
      tags:
        - Faucet
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FaucetRequest"
      responses:
        "200":
          description: Testnet tokens sent
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FaucetResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/transactions:
    get:
      operationId: listRestTransactions
      summary: List Transactions
      description: |
        List the REST transaction history for a wallet, scoped to the authenticated partner.
        Results are ordered by `createdAt` DESC and paginated via opaque cursor.

        `POST /transfer` broadcasts by default and appears in this list. `POST /sign-transaction`
        appears only when called with `broadcast: true`. Sign-only requests (`broadcast: false`
        or omitted for `sign-transaction`) do not write history rows.
      tags:
        - Signing
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Cursor"
        - name: status
          in: query
          required: false
          description: Filter by transaction status.
          schema:
            type: string
            enum: [pending, submitted, confirmed, reverted, failed]
        - name: intentKind
          in: query
          required: false
          description: Filter by the REST operation that created the transaction record.
          schema:
            type: string
            enum: [transfer, sign_transaction]
      responses:
        "200":
          description: Paginated list of transactions
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RestTransactionListResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/transactions/{transactionId}:
    get:
      operationId: getRestTransaction
      summary: Get Transaction
      description: |
        Look up a single persisted REST transaction record by id. Returns `404` with an identical
        body for records that do not exist or that belong to a different partner.
      tags:
        - Signing
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
        - name: transactionId
          in: path
          required: true
          description: "Transaction record id returned by `POST /transfer` or `POST /sign-transaction` with `broadcast: true`."
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Transaction record
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RestTransaction"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/wallets/{walletId}/estimate-fee:
    post:
      operationId: estimateFee
      summary: Estimate Transfer Fee
      description: |
        Estimate the fee for a transfer without signing or broadcasting. Accepts
        the same core parameters as the transfer endpoint. Fees are advisory and
        subject to network conditions.

        **EVM:** Returns `gasLimit`, `maxFeePerGas`, `maxPriorityFeePerGas`, and
        `transactionType` alongside the `estimatedFee` in the native currency.

        **Solana:** Returns only `estimatedFee` in SOL.

        The response may include a `warning` field if estimation used fallback
        heuristics (e.g. when the RPC node could not simulate the transaction).
      tags:
        - Signing
      parameters:
        - $ref: "#/components/parameters/WalletId"
        - $ref: "#/components/parameters/RequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EstimateFeeRequest"
      responses:
        "200":
          description: Fee estimate
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EstimateFeeResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/ServerError"

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: Your partner secret key (server-side only)
      x-default: sk_your_secret_key_here

  parameters:
    WalletId:
      name: walletId
      in: path
      required: true
      description: Wallet ID
      schema:
        type: string
        example: "0a1b2c3d-4e5f-6789-abcd-ef0123456789"

    RequestId:
      name: X-Request-Id
      in: header
      required: false
      description: UUID for request tracing. Para returns one if omitted.
      schema:
        type: string
        format: uuid

    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: |
        Unique key for safe retries on POST endpoints. If a request with the same key
        is received within 24 hours, the original response is returned without re-executing.
        Recommended format: UUID v4. Max length 256 characters.
        Returns 422 if the same key is reused with a different request body.
        Returns 409 if a request with the same key is still being processed.
      schema:
        type: string
        maxLength: 256

    Limit:
      name: limit
      in: query
      required: false
      description: Maximum number of items to return (1-100)
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 50

    Cursor:
      name: cursor
      in: query
      required: false
      description: Opaque cursor for pagination. Use the value from a previous response's pagination.cursor field.
      schema:
        type: string

  schemas:
    CreateWalletRequest:
      type: object
      required:
        - type
        - userIdentifier
        - userIdentifierType
      properties:
        type:
          type: string
          enum: [EVM, SOLANA, COSMOS, STELLAR, SUI]
          description: Blockchain network type
        userIdentifier:
          type: string
          description: User identifier (email, phone, or custom ID)
          example: "alice@example.com"
        userIdentifierType:
          type: string
          enum: [EMAIL, PHONE, CUSTOM_ID, GUEST_ID, DISCORD, TWITTER, TELEGRAM, FARCASTER]
          description: Type of user identifier
        scheme:
          type: string
          enum: [DKLS, CGGMP, ED25519]
          description: Signature scheme (defaults based on wallet type)
        cosmosPrefix:
          type: string
          description: Bech32 prefix for Cosmos wallets
          example: "cosmos"

    UpdateWalletIdentifierRequest:
      type: object
      required:
        - userIdentifier
        - userIdentifierType
      properties:
        userIdentifier:
          type: string
          description: New user identifier (email, phone, or custom ID)
          example: "bob@example.com"
        userIdentifierType:
          type: string
          enum: [EMAIL, PHONE, CUSTOM_ID, GUEST_ID, DISCORD, TWITTER, TELEGRAM, FARCASTER]
          description: Type of user identifier

    MigrateShareRequest:
      type: object
      required:
        - encryptedPayload
      properties:
        encryptedPayload:
          type: string
          description: |
            JSON string containing the ECIES-encrypted user share. The SDK's
            `migrateWalletShare()` method produces this automatically. Non-SDK
            callers must encrypt the share with the enclave's P-256 public key
            (from `GET /v1/enclave/public-key`) using ECIES-P256-AES256-SHA256.
          example: "{\"encryptedData\":\"base64...\",\"ephemeral\":\"base64...\",\"algorithm\":\"ECIES-P256-AES256-SHA256\"}"

    EnclavePublicKeyResponse:
      type: object
      required:
        - publicKey
        - keyFingerprint
        - generatedAt
      properties:
        publicKey:
          type: string
          description: PEM-formatted P-256 public key for ECIES encryption
          example: "-----BEGIN PUBLIC KEY-----\nMFkwEwYH...\n-----END PUBLIC KEY-----"
        keyFingerprint:
          type: string
          description: SHA-256 fingerprint of the public key for verification
          example: "SHA256:abc123..."
        generatedAt:
          type: string
          format: date-time
          description: Timestamp when the key was generated
          example: "2025-01-15T00:00:00.000Z"

    Wallet:
      type: object
      required:
        - id
        - type
        - scheme
        - status
        - createdAt
      properties:
        id:
          type: string
          format: uuid
          description: Unique wallet identifier
          example: "0a1b2c3d-4e5f-6789-abcd-ef0123456789"
        type:
          type: string
          enum: [EVM, SOLANA, COSMOS, STELLAR, SUI]
          description: Blockchain network type
          example: "EVM"
        scheme:
          type: string
          enum: [DKLS, CGGMP, ED25519]
          description: Signature scheme
          example: "DKLS"
        status:
          type: string
          enum: [creating, ready]
          description: Wallet creation status
        address:
          type: string
          description: Wallet address. Present when status is ready, omitted otherwise.
          example: "0x742d35Cc6634C0532925a3b844Bc9e7595f..."
        publicKey:
          type: string
          description: Public key. Present when status is ready, omitted otherwise.
        userIdentifier:
          type: string
          description: The user identifier associated with this wallet
          example: "alice@example.com"
        userIdentifierType:
          type: string
          enum: [EMAIL, PHONE, CUSTOM_ID, GUEST_ID, DISCORD, TWITTER, TELEGRAM, FARCASTER]
          description: Type of user identifier
        createdAt:
          type: string
          format: date-time
          description: ISO 8601 timestamp
          example: "2024-01-15T09:30:00Z"

    WalletListResponse:
      type: object
      required:
        - data
        - pagination
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Wallet"
        pagination:
          $ref: "#/components/schemas/Pagination"

    SignRawRequest:
      type: object
      required:
        - data
      properties:
        data:
          type: string
          description: Hex-encoded data to sign (optional `0x` prefix)
          example: "0x48656c6c6f20576f726c64"
        encoding:
          type: string
          enum: [hex]
          default: hex
          description: Encoding of `data`. Currently only `hex` is supported.
        walletType:
          type: string
          enum: [EVM, SOLANA, COSMOS, STELLAR, SUI]
          description: Expected wallet type. If provided and the wallet is a different type, the request is rejected.

    SignRawResponse:
      type: object
      required:
        - signature
      properties:
        signature:
          type: string
          description: "Hex-encoded signature without `0x` prefix. For DKLS/CGGMP: 65 bytes (r + s + v)."
          example: "a1b2c3d4e5f6..."

    Pagination:
      type: object
      description: Cursor-based pagination metadata included in all list responses.
      required:
        - cursor
        - hasMore
        - limit
      properties:
        cursor:
          type: string
          nullable: true
          description: Cursor to fetch the next page. Null when no more results.
        hasMore:
          type: boolean
          description: Whether more results exist beyond this page
        limit:
          type: integer
          minimum: 1
          maximum: 100
          description: The limit that was applied to this request
          example: 50

    SignTransactionRequest:
      type: object
      required:
        - transaction
      properties:
        transaction:
          description: |
            EVM: JSON object with unsigned transaction fields.
            Solana: base64-encoded serialized transaction string.
            Stellar: base64-encoded XDR transaction envelope string.
            Sui: base64-encoded, BCS-serialized transaction (`TransactionData`) string.
            The API differentiates by the wallet's type (determined by `walletId`).
            Objects are treated as EVM transactions; strings are treated as Solana,
            Stellar, or Sui transactions depending on the wallet type. For Stellar
            wallets, `networkPassphrase` is required.
          oneOf:
            - $ref: "#/components/schemas/EvmTransaction"
            - type: string
              description: "Base64-encoded serialized Solana transaction. Accepts both legacy (`Transaction.serialize()`) and versioned (`VersionedTransaction.serialize()`, v0) formats, auto-detected on the server."
              example: "AQAAAA..."
            - type: string
              description: "Base64-encoded Stellar transaction envelope (XDR). Must also provide `networkPassphrase`."
              example: "AAAAAgAAAA..."
        networkPassphrase:
          type: string
          description: "Stellar network passphrase. Required when the wallet referenced by `walletId` is a STELLAR wallet. Ignored for other wallet types."
          example: "Public Global Stellar Network ; September 2015"
        network:
          type: string
          enum: [SOLANA, SOLANA_DEVNET]
          description: "Solana network to broadcast to when `broadcast: true`. Uses the same environment rules as `POST /transfer`."
        broadcast:
          type: boolean
          default: false
          description: "When true, broadcast the signed EVM or Solana transaction, create a persisted transaction record, and return `txHash` plus `transactionId`. Omitted or false remains sign-only. Stellar and Sui wallets reject `broadcast: true`."

    EvmTransaction:
      type: object
      required:
        - to
        - chainId
        - type
      properties:
        to:
          type: string
          description: Destination address (contract deployment not supported)
          example: "0x742d35Cc6634C0532925a3b844Bc9e7595f..."
        value:
          type: string
          description: Value in wei (decimal or 0x-prefixed hex)
          example: "0x2386f26fc10000"
        chainId:
          oneOf:
            - type: string
            - type: integer
          description: Chain ID (decimal or 0x-prefixed hex, e.g. "1" for Ethereum mainnet)
          example: "1"
        data:
          type: string
          description: Hex-encoded calldata (0x-prefixed)
        nonce:
          type: integer
          minimum: 0
          description: Transaction nonce
        gasLimit:
          type: string
          description: Gas limit (decimal or 0x-prefixed hex)
          example: "0x5208"
        maxFeePerGas:
          type: string
          description: EIP-1559 max fee per gas (decimal or 0x-prefixed hex)
        maxPriorityFeePerGas:
          type: string
          description: EIP-1559 max priority fee per gas (decimal or 0x-prefixed hex)
        gasPrice:
          type: string
          description: Legacy gas price (decimal or 0x-prefixed hex)
        type:
          type: integer
          enum: [0, 2]
          description: "Transaction type: 0 (legacy) or 2 (EIP-1559)"

    SignTransactionResponse:
      type: object
      required:
        - signedTransaction
      properties:
        signedTransaction:
          type: string
          description: "Signed transaction ready for broadcast. EVM: RLP-encoded hex string with `0x` prefix. Solana: base64-encoded string. Stellar: base64-encoded XDR string."
          example: "0x02f8..."
        txHash:
          type: string
          description: "Transaction hash. Present only when `broadcast: true` succeeds."
          example: "0x1234abcd..."
        transactionId:
          type: string
          format: uuid
          description: "Persisted transaction record id. Present only when `broadcast: true` creates a transaction-history row."
          example: "550e8400-e29b-41d4-a716-446655440000"

    SignMessageRequest:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          description: Human-readable message to sign
          example: "Hello World"

    SignMessageResponse:
      type: object
      required:
        - signature
      properties:
        signature:
          type: string
          description: "Hex-encoded signature without `0x` prefix."
          example: "a1b2c3d4e5f6..."

    SignTypedDataRequest:
      type: object
      required:
        - typedData
      properties:
        typedData:
          $ref: "#/components/schemas/EIP712TypedData"

    EIP712TypedData:
      type: object
      required:
        - domain
        - types
        - primaryType
        - message
      properties:
        domain:
          type: object
          description: EIP-712 domain separator fields
          example:
            name: "MyDApp"
            version: "1"
            chainId: 11155111
            verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
        types:
          type: object
          description: "Type definitions mapping type names to arrays of {name, type} field descriptors. Omit EIP712Domain — it is handled automatically."
          additionalProperties:
            type: array
            items:
              type: object
              properties:
                name:
                  type: string
                type:
                  type: string
          example:
            Mail:
              - name: "from"
                type: "address"
              - name: "to"
                type: "address"
              - name: "contents"
                type: "string"
        primaryType:
          type: string
          description: Primary type name that the message conforms to
          example: "Mail"
        message:
          type: object
          description: Structured data to sign
          example:
            from: "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"
            to: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
            contents: "Hello, world!"

    SignTypedDataResponse:
      type: object
      required:
        - signature
      properties:
        signature:
          type: string
          description: "Hex-encoded signature without `0x` prefix."
          example: "a1b2c3d4e5f6..."

    SignAuthorizationRequest:
      type: object
      required:
        - authorization
      properties:
        authorization:
          type: object
          required:
            - chainId
            - nonce
          properties:
            address:
              type: string
              description: "Contract address to delegate execution to. Mutually exclusive with `contractAddress`."
              example: "0x1234567890abcdef1234567890abcdef12345678"
            contractAddress:
              type: string
              description: "Alias for `address` (matches viem's API). Mutually exclusive with `address`."
              example: "0x1234567890abcdef1234567890abcdef12345678"
            chainId:
              type: integer
              minimum: 0
              description: "Chain ID for the authorization. Use 0 for chain-agnostic authorizations."
              example: 1
            nonce:
              type: integer
              minimum: 0
              description: "Account nonce for the authorization."
              example: 0

    SignAuthorizationResponse:
      type: object
      required:
        - address
        - chainId
        - nonce
        - r
        - s
        - yParity
        - signature
      properties:
        address:
          type: string
          description: "Contract address the authorization delegates to."
          example: "0x1234567890abcdef1234567890abcdef12345678"
        chainId:
          type: integer
          description: "Chain ID from the authorization."
          example: 1
        nonce:
          type: integer
          description: "Nonce from the authorization."
          example: 0
        r:
          type: string
          description: "ECDSA signature r component (hex with 0x prefix)."
          example: "0xa1b2c3..."
        s:
          type: string
          description: "ECDSA signature s component (hex with 0x prefix)."
          example: "0xd4e5f6..."
        yParity:
          type: integer
          enum: [0, 1]
          description: "EIP-7702 parity bit (0 or 1). Unlike other signing endpoints that return v (27/28), this uses yParity per the EIP-7702 spec."
          example: 0
        signature:
          type: string
          description: "Raw hex-encoded 65-byte signature without `0x` prefix (r + s + v)."
          example: "a1b2c3d4e5f6..."

    AddressScreeningStatus:
      type: object
      required:
        - address
        - isSanctioned
      properties:
        address:
          type: string
          description: Screened address.
          example: "0x000000000000000000000000000000000000dEaD"
        isSanctioned:
          type: boolean
          description: Whether the address is sanctioned.
          example: false

    TransferRequest:
      type: object
      required:
        - to
        - value
      properties:
        to:
          type: string
          description: Recipient address
          example: "0x742d35Cc6634C0532925a3b844Bc9e7595f..."
        value:
          type: string
          description: "Amount in smallest unit (wei for ETH, lamports for SOL)"
          example: "1000000000000000"
        chainId:
          oneOf:
            - type: string
            - type: integer
          description: "Chain ID. Required for EVM wallets, ignored for Solana."
          example: "1"
        kind:
          type: string
          enum: [NATIVE, ERC20, SPL]
          description: |
            Transfer type discriminator. EVM wallets accept `NATIVE` or `ERC20`.
            Solana wallets accept `NATIVE` or `SPL`. If omitted, inferred from
            `tokenAddress` presence (backwards compatible).
        tokenAddress:
          type: string
          description: "Token contract address. Required when kind is `ERC20` or `SPL`. EVM: ERC-20 contract. Solana: SPL mint address."
        nonce:
          type: integer
          minimum: 0
          description: "Transaction nonce. EVM only — auto-fetched via `getTransactionCount` if omitted."
        gasLimit:
          type: string
          description: "Gas limit. EVM only — auto-estimated with 20% buffer if omitted."
        maxFeePerGas:
          type: string
          description: "EIP-1559 max fee per gas. EVM only — auto-fetched if omitted."
        maxPriorityFeePerGas:
          type: string
          description: "EIP-1559 max priority fee per gas. EVM only — auto-fetched if omitted."
        gasPrice:
          type: string
          description: "Legacy gas price. EVM only — auto-fetched if omitted."
        type:
          type: integer
          enum: [0, 2]
          description: "Transaction type: 0 (legacy) or 2 (EIP-1559). EVM only — auto-detected from chain if omitted."
        network:
          type: string
          enum: [SOLANA, SOLANA_DEVNET]
          description: "Solana network. In production, defaults to SOLANA. In beta, only SOLANA_DEVNET is allowed (and is the default)."
        broadcast:
          type: boolean
          default: true
          description: "Whether to broadcast the signed transaction. Defaults to true. Set to false to receive only the signed transaction without broadcasting."
        addressScreening:
          type: boolean
          default: true
          description: "Whether to screen the recipient address before signing. Defaults to true when address screening is enabled for the API."

    TransferResponse:
      type: object
      required:
        - signedTransaction
      properties:
        signedTransaction:
          type: string
          description: "Signed transaction ready for broadcast. EVM: RLP-encoded hex string. Solana: base64-encoded string."
          example: "0x02f8..."
        txHash:
          type: string
          nullable: true
          description: "Transaction hash. Present when broadcast is true (default). Omitted when broadcast is false."
          example: "0x1234abcd..."
        transactionId:
          type: string
          format: uuid
          description: "Persisted transaction record id (present only when broadcasted). Use it to poll `GET /v1/wallets/{walletId}/transactions/{transactionId}` or match incoming `rest.transaction.confirmed` / `rest.transaction.failed` webhooks."
          example: "550e8400-e29b-41d4-a716-446655440000"

    RestTransaction:
      type: object
      required:
        - transactionId
        - walletId
        - walletType
        - intentKind
        - status
        - createdAt
      properties:
        transactionId:
          type: string
          format: uuid
          example: "550e8400-e29b-41d4-a716-446655440000"
        walletId:
          type: string
          format: uuid
        walletType:
          type: string
          enum: [EVM, SOLANA]
        intentKind:
          type: string
          enum: [transfer, sign_transaction]
          description: "REST operation that created the record."
        chainId:
          type: string
          description: "EVM chain ID (EVM records only)."
        network:
          type: string
          enum: [SOLANA, SOLANA_DEVNET]
          description: "Solana network (Solana records only)."
        to:
          type: string
          description: "Destination address. Absent for Solana `sign_transaction` records because Para does not decode arbitrary Solana instructions."
        value:
          type: string
          description: "Transfer amount in smallest unit (wei / lamports / token smallest unit), or EVM `sign_transaction` value when supplied."
        tokenAddress:
          type: string
          description: "Token contract (ERC-20 / SPL mint) address if a token transfer, else absent. Always absent for `sign_transaction` records."
        status:
          type: string
          enum: [pending, submitted, confirmed, reverted, failed]
          description: |
            Lifecycle state. `pending` = row inserted, not yet broadcast. `submitted` = accepted by the RPC.
            `confirmed` = included in a block, not reverted. `reverted` = included on-chain but execution
            failed. `failed` = never broadcast, or broadcast was rejected by the RPC. `submitted`,
            `confirmed`, `reverted`, and `failed` are terminal from the partner's POV except that
            `submitted` may still transition via monitoring. **Partners must treat unknown status values
            as non-terminal** — future versions may add `finalized` or `replaced`.
        hash:
          type: string
          description: "Transaction hash (present once submitted)."
        blockNumber:
          type: string
          description: "Block number as a decimal string (present when confirmed or reverted)."
        blockHash:
          type: string
        failureStage:
          type: string
          enum: [mpc_sign, signature_apply, signer_verify, broadcast, monitor_timeout]
          description: "Stage at which the pipeline failed (when `status = failed`)."
        failureCode:
          type: string
          description: "Structured error code from the broadcast helper when the RPC rejected the transaction."
        failureMessage:
          type: string
          description: "Truncated error message (max 512 chars)."
        createdAt:
          type: string
          format: date-time
        submittedAt:
          type: string
          format: date-time
          description: "Set when status first moves to `submitted`."
        resolvedAt:
          type: string
          format: date-time
          description: "Set when status reaches a terminal value (`confirmed`, `reverted`, `failed`)."

    RestTransactionListResponse:
      type: object
      required:
        - data
        - pagination
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/RestTransaction"
        pagination:
          $ref: "#/components/schemas/Pagination"

    EstimateFeeRequest:
      type: object
      required:
        - to
        - value
      properties:
        to:
          type: string
          description: Destination address
          example: "0x742d35Cc6634C0532925a3b844Bc9e7595f..."
        value:
          type: string
          description: "Transfer amount in smallest unit (wei for ETH, lamports for SOL)"
          example: "1000000000000000"
        chainId:
          oneOf:
            - type: string
            - type: integer
          description: "Chain ID. Required for EVM wallets, ignored for Solana."
          example: "1"
        tokenAddress:
          type: string
          description: "Token contract address. EVM: ERC-20 contract. Solana: SPL mint address."
        network:
          type: string
          enum: [SOLANA, SOLANA_DEVNET]
          description: "Solana network. In production, defaults to SOLANA. In beta, only SOLANA_DEVNET is allowed (and is the default)."

    EstimateFeeResponse:
      type: object
      required:
        - estimatedFee
        - transferAmount
        - currency
      properties:
        estimatedFee:
          type: string
          description: "Estimated fee in standard units of the native currency (e.g. ETH, SOL)"
          example: "0.000756"
        transferAmount:
          type: string
          description: "Transfer amount in standard units of the native currency"
          example: "0.001"
        currency:
          type: string
          description: "Native currency symbol (e.g. ETH, SOL)"
          example: "ETH"
        gasLimit:
          type: string
          description: "Estimated gas limit. EVM only."
          example: "25200"
        maxFeePerGas:
          type: string
          description: "EIP-1559 max fee per gas (in wei). EVM only."
          example: "30000000000"
        maxPriorityFeePerGas:
          type: string
          description: "EIP-1559 max priority fee per gas (in wei). EVM only."
          example: "2000000000"
        gasPrice:
          type: string
          description: "Legacy gas price (in wei). EVM legacy transactions only."
          example: "30000000000"
        warning:
          type: string
          description: "Present if estimation used fallback heuristics"

    WalletBalanceResponse:
      type: object
      required:
        - balance
        - symbol
        - rawBalance
      properties:
        balance:
          type: string
          description: "Human-readable balance in standard units (e.g. ETH, SOL)"
          example: "1.5"
        symbol:
          type: string
          description: "Token symbol (e.g. ETH, SOL, USDC) or mint/contract address for tokens without on-chain symbol"
          example: "ETH"
        rawBalance:
          type: string
          description: "Balance in smallest unit (wei for ETH, lamports for SOL, raw token units for ERC-20/SPL)"
          example: "1500000000000000000"

    PolicyDeniedError:
      type: object
      properties:
        code:
          type: string
          example: POLICY_DENIED
        message:
          type: string
          example: Transaction denied by policy
        deniedBy:
          type: object
          properties:
            scopeName:
              type: string
            permissionType:
              type: string
            condition:
              type: object
              properties:
                resource:
                  type: string
                comparator:
                  type: string
                reference: {}

    FaucetRequest:
      type: object
      required:
        - walletId
      properties:
        walletId:
          type: string
          description: ID of the wallet to receive testnet tokens
          example: "0a1b2c3d-4e5f-6789-abcd-ef0123456789"
        chain:
          type: string
          description: Target testnet chain
          default: ETHEREUM_SEPOLIA
          example: "ETHEREUM_SEPOLIA"

    FaucetResponse:
      type: object
      required:
        - transactionHash
        - amount
        - chain
        - walletId
        - address
      properties:
        transactionHash:
          type: string
          description: On-chain transaction hash of the faucet transfer
          example: "0x1234abcd..."
        amount:
          type: string
          description: Amount of testnet tokens sent in standard units
          example: "0.01"
        chain:
          type: string
          description: Chain the tokens were sent on
          example: "ETHEREUM_SEPOLIA"
        walletId:
          type: string
          description: ID of the wallet that received the tokens
          example: "0a1b2c3d-4e5f-6789-abcd-ef0123456789"
        address:
          type: string
          description: On-chain address that received the tokens
          example: "0x742d35Cc6634C0532925a3b844Bc9e7595f..."

    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Machine-readable error code for programmatic handling
          example: "INVALID_REQUEST"
        message:
          type: string
          description: Human-readable error message
        transactionId:
          type: string
          format: uuid
          description: "Persisted transaction record id. Present only when a broadcast request failed after a history row was created."
        failureStage:
          type: string
          enum: [mpc_sign, signature_apply, signer_verify, broadcast, monitor_timeout]
          description: "Which stage of the broadcast lifecycle failed. Present on persisted broadcast failures."
        failureCode:
          type: string
          description: "Machine-readable failure reason from the underlying broadcast helper (e.g. `INSUFFICIENT_NATIVE_BALANCE`, `EXECUTION_FAILED`). Present on broadcast-stage failures."
        signedTransaction:
          type: string
          description: "Signed transaction bytes. Present on broadcast failures that happen after signing completed."
      additionalProperties: true
      description: |
        All error responses include `code` and `message` fields. Some errors include extra fields (e.g. `walletId` on 409 Conflict).
        Broadcast failures that occur after a persisted transaction row is created include `transactionId`,
        `failureStage`, and (on broadcast-stage failures) `failureCode`, and also set the `x-transaction-id`
        response header. If signing completed before the failure, `signedTransaction` is included so callers
        can inspect or retry the already-signed bytes.

        Common error codes: `INVALID_REQUEST`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `CONFLICT`, `WALLET_ALREADY_EXISTS`, `WALLET_ALREADY_CLAIMED`, `RATE_LIMITED`, `INTERNAL_ERROR`.

  responses:
    BadRequest:
      description: Invalid request body
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: "INVALID_REQUEST"
            message: "type must be one of EVM, SOLANA, COSMOS, STELLAR"

    Unauthorized:
      description: API key not provided
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: "UNAUTHORIZED"
            message: "secret api key not provided"

    Forbidden:
      description: Invalid API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: "FORBIDDEN"
            message: "invalid secret api key"

    NotFound:
      description: Wallet not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: "NOT_FOUND"
            message: "wallet not found"

    TooManyRequests:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until the rate limit window resets
          example: 60
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: "RATE_LIMITED"
            message: "Rate limit exceeded, try again shortly."

    IdempotencyConflict:
      description: A request with this idempotency key is currently being processed
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: "CONFLICT"
            message: "A request with this idempotency key is currently being processed"

    Unprocessable:
      description: Idempotency key reused with a different request body
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: "UNPROCESSABLE"
            message: "Idempotency key has already been used with different request parameters"

    ServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: "INTERNAL_ERROR"
            message: "Internal Server Error"
