openapi: 3.0.0
info:
  version: 2.0.0
  title: Pods API
  description: >
    # Pods API Documentation


    Pods API is a comprehensive DeFi aggregation platform that enables
    cross-chain and same-chain transactions for yield strategies.


    ## Key Features


    - **Cross-Chain Swaps**: Swap tokens across different blockchain networks

    - **Same-Chain Swaps**: Optimal routing for swaps within the same network

    - **Yield Strategies**: Access to multiple DeFi protocols (Aave, Morpho,
    Lido, etc.)

    - **Wallet Tracking**: Monitor positions and yields across protocols

    - **Transaction Monitoring**: Real-time status updates for cross-chain
    transactions


    ## Authentication


    Most API endpoints require authentication using an API key in the
    `x-api-key` header:


    ```

    x-api-key: your-api-key-here

    ```


    Obtain your API key from the Pods dashboard.


    **Exceptions:**

    - `GET /health` is public and does not require authentication.

    - Customer dashboard endpoints (e.g. `GET /customers/me`,
    `/customers/:id/tokens`, `/customers/:id/token-groups`) require a **Bearer**
    token (Supabase or Magic JWT). API key alone is not sufficient for these
    routes because they enforce user role checks.


    ## Base URL


    Production: `https://api.pods.finance`


    ## Rate Limits


    - 100 requests per minute per API key

    - 1000 requests per hour per API key


    ## Quote Expiration


    Quotes expire after 5 minutes. The `deadline` field is a Unix timestamp in
    seconds and `deadlineDate` is an ISO 8601 string (UTC); request a new quote
    when the deadline has passed.


    ## Error Handling


    All errors follow a consistent format. The HTTP status line is the source of
    truth — the body does **not** include `httpStatus`:


    ```json

    {
      "error": {
        "code": "ERROR_CODE",
        "message": "Human-readable error message",
        "details": {}
      }
    }

    ```


    ## Common Error Codes


    - **QUOTE_NOT_FOUND** (404): Quote not found or expired

    - **ACTION_NOT_FOUND** (404): Action not found

    - **STRATEGY_NOT_FOUND** (404): Strategy not found

    - **RECURRING_COLLECTION_REQUIRES_FEE** (400): Recurring collection requires
    a performance or markup fee configured

    - **RECURRING_MARKUP_RECIPIENT_REQUIRED** (422): Live recurring collection
    with a markup fee requires a non-zero markup fee recipient

    - **RECURRING_RECIPIENT_ROTATION_PENDING_DISPATCH** (409): Markup fee
    recipient cannot be rotated while a recurring collection transaction is
    unresolved

    - **RECURRING_REGISTRY_CONTROLLER_NOT_CONFIGURED** (503): Recurring
    collection controller is not configured for this network

    - **RECURRING_REGISTRY_MISMATCH** (409): On-chain recurring collection
    registry is not synchronized

    - **INVALID_ACTION_TYPE** (400): Action type is not supported for this
    endpoint

    - **QUOTE_EXPIRED** (400): Quote has expired

    - **QUOTE_VALIDATION_ERROR** (400): Quote validation failed

    - **PIX_THIRD_PARTY_QUOTE_MISMATCH** (400): thirdParty must match the Pix
    offramp quote

    - **MISSING_PIX_KEY** (400): pixKey is required to create the BRL Pix payout
    ticket

    - **INVALID_PIX_BRCODE_FORMAT** (400): pixKey looks like a Pix BR Code but
    failed format/checksum validation

    - **PIX_BRCODE_EXPIRED** (400): This Pix BR Code has expired

    - **PIX_BRCODE_LOOKUP_FAILED** (502): Failed to look up Pix BR Code details
    from Avenia

    - **PIX_BRCODE_REQUOTE_REQUIRED** (400): This Pix BR Code has a fixed amount
    that was not priced into the existing quote; request a new quote

    - **PROVIDER_NOT_FOUND** (404): Swap provider not found

    - **NO_ROUTE_FOUND** (400): No swap route available between specified chains

    - **INVALID_PREFERRED_PROVIDER** (400): Unknown preferredProvider name

    - **PREFERRED_PROVIDER_NOT_ELIGIBLE** (400): preferredProvider is not
    eligible for this swap route

    - **PREFERRED_PROVIDER_QUOTE_UNAVAILABLE** (400): No quote was returned for
    the requested preferredProvider

    - **QUOTE_GENERATION_FAILED** (400): Could not generate quote for specified
    parameters

    - **ROUTER_EXACT_OUT_UNSUPPORTED** (400): Router public Pix/Yield quotes
    currently support amountIn only

    - **FEE_SPONSORSHIP_NOT_ALLOWED** (403): Fee sponsorship is not allowed for
    this customer

    - **CUSTOMER_STRATEGY_NOT_FOUND** (404): Customer strategy not found

    - **FEE_ACTIVATION_IN_PROGRESS** (409): Profit fee activation is already
    reconciling

    - **FEE_ACTIVATION_TASK_NOT_PERSISTED** (500): Fee activation task could not
    be persisted

    - **INVALID_TX_HASH** (400): Invalid transaction hash format

    - **TX_UPDATE_FAILED** (500): Failed to update transaction status

    - **INVALID_AMOUNT_PARAMS** (400): Cannot specify both amountIn and
    amountOut simultaneously

    - **MISSING_AMOUNT_PARAMS** (400): Either amountIn or amountOut is required

    - **INVALID_SLIPPAGE** (400): slippage must be a number between 0.0001 and
    0.5 (fraction of 1, e.g. 0.01 = 1%)

    - **AMOUNT_IN_TOO_LOW** (400): AmountIn is below the minimum for this route

    - **AMOUNT_IN_TOO_HIGH** (400): AmountIn exceeds available liquidity for
    this route. Try a smaller amount.

    - **AMOUNT_IN_TOO_LOW_OFFRAMP** (400): AmountIn must be greater than 1 USDC

    - **AMOUNT_IN_TOO_HIGH_OFFRAMP** (400): AmountIn must be less than 20k USDC

    - **AMOUNT_OUT_TOO_LOW_OFFRAMP** (400): AmountOut is below the route minimum
    (0.5 BRL on Pix BRLA/BRS, 5 BRLA on USDC Pix)

    - **AMOUNT_OUT_TOO_HIGH_OFFRAMP** (400): AmountOut must be less than 110k
    BRLA

    - **AMOUNT_EXCEEDS_PROVIDER_LIMIT** (409): Requested amount exceeds the
    remaining provider withdrawal limit

    - **AMOUNT_IN_TOO_LOW_ONRAMP** (400): AmountIn is below the route minimum
    (0.5 BRL on Pix BRLA/BRS, 1 BRLA on USDC Pix)

    - **AMOUNT_IN_TOO_HIGH_ONRAMP** (400): AmountIn must be at most 100k BRLA

    - **AMOUNT_OUT_TOO_LOW_ONRAMP** (400): AmountOut must be greater than 1 USDC

    - **AMOUNT_OUT_TOO_HIGH_ONRAMP** (400): AmountOut must be at most 20k USDC

    - **TRANSFER_VALIDATION_FAILED** (400): Token transfer validation failed

    - **REFUND_PROCESSING_FAILED** (500): Failed to process refund transaction

    - **INSUFFICIENT_AMOUNT_FOR_REFUND** (400): Transferred amount insufficient
    to cover refund fee

    - **FORBIDDEN** (403): Access denied

    - **INTERNAL_SERVER_ERROR** (500): An unexpected error occurred

    - **BYTECODE_GENERATION_FAILED** (500): Failed to generate transaction
    bytecode

    - **SWAP_EXECUTION_FAILED** (500): Failed to execute swap transaction

    - **KAMINO_SERVICE_UNAVAILABLE** (503): Lending service is temporarily
    unavailable. Please try again later.

    - **KAMINO_BAD_REQUEST** (400): Invalid request to lending service

    - **DEPOSIT_NOT_FOUND** (404): Bitcoin deposit not found

    - **SYMBIOSIS_API_ERROR** (502): Error communicating with Symbiosis API

    - **ONDO_GM_NOT_CONFIGURED** (503): Ondo GM API is not configured.

    - **ONDO_GM_ATTESTATION_FAILED** (502): Ondo GM hard attestation request
    failed.

    - **ONDO_GM_SOFT_QUOTE_UNAVAILABLE** (502): Ondo GM soft quote is
    unavailable.

    - **ONDO_GM_FLOOR_BREACH** (409): Ondo GM hard attestation is worse than the
    bytecode floor.


    ## Support


    - Documentation: https://docs.pods.finance

    - Support: support@pods.finance
  contact:
    name: Pods Support
    email: support@pods.finance
    url: https://pods.finance
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
servers:
  - url: https://api.pods.finance
    description: Production server
tags:
  - name: Health
    description: Health check endpoints
  - name: KYC
    description: "KYC onboarding and status endpoints: Brazilian and foreign Sumsub
      import, BigDataCorp capture, external evidence, and USD currency unlock"
  - name: Swap
    description: Token swap operations
  - name: Tracking
    description: Execution tracking for swaps, transfers, and strategy operations
  - name: Strategies
    description: DeFi yield strategy operations
  - name: Ondo
    description: Ondo Global Markets stock status and tradability
  - name: Tokens
    description: Token catalog and metadata
  - name: Transfer
    description: Token transfer transaction generation
  - name: Wallets
    description: Wallet position tracking
  - name: Customers
    description: Customer account management (Bearer auth)
  - name: Quotes
    description: Quote history and management
  - name: Smart Account
    description: ERC-4337 Gnosis Safe smart account provisioning
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: API key for authentication. Obtain from your Pods dashboard.
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Magic.link or Supabase JWT token
  schemas:
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            details:
              nullable: true
          required:
            - code
            - message
      required:
        - error
      example:
        error:
          code: QUOTE_NOT_FOUND
          message: Quote not found or expired
    Pagination:
      type: object
      properties:
        page:
          type: number
          example: 1
        limit:
          type: number
          example: 10
        total:
          type: number
          example: 24
        totalPages:
          type: number
          example: 3
        hasMore:
          type: boolean
          example: true
      required:
        - page
        - limit
        - total
        - totalPages
        - hasMore
      description: Standard list pagination metadata
    Address:
      type: string
      description: Ethereum address (EVM-compatible)
      example: "0xb794F5eA0ba39494cE839613fffBA74279579268"
    WalletAddress:
      type: string
      description: Wallet address stored on the Ramp KYC profile. Accepts a
        checksummed EVM address or a Solana public key. Later Pix quotes must
        use the same address as destinationAddress (onramp) or originAddress
        (offramp).
      example: EXYhCNamLPFfSMekSn7Lzc2cP32aZpRa7ok5gErdJzcj
    BigIntLike:
      type: string
      description: Large integer represented as string (for amounts with decimals)
      example: "1000000000000000000"
    Token:
      type: object
      properties:
        contract:
          $ref: "#/components/schemas/Address"
        symbol:
          type: string
          example: USDC
        decimals:
          type: number
          example: 6
        amount:
          $ref: "#/components/schemas/BigIntLike"
        chainId:
          type: number
          example: 1
        priceUSD:
          type: string
          example: "1.00"
      required:
        - contract
        - symbol
        - decimals
        - amount
        - chainId
      description: Token information with amount and pricing
    SwapFeeBreakdownLine:
      type: object
      properties:
        kind:
          type: string
          enum:
            - pods
            - customer
            - paymentRail
            - bridgeAndSlippage
            - externalProvider
        label:
          type: string
        amountRaw:
          type: string
        amountHumanized:
          type: string
          example: "0.05"
        decimals:
          type: integer
        symbol:
          type: string
        asset:
          type: string
          enum:
            - tokenIn
            - tokenOut
        amountInUSD:
          type: number
        signed:
          type: boolean
      required:
        - kind
        - label
        - amountRaw
        - amountHumanized
        - decimals
        - symbol
        - asset
        - amountInUSD
    SwapFeeBreakdownReference:
      type: object
      properties:
        amountOutRaw:
          type: string
        source:
          type: string
          enum:
            - spotUsdAtQuoteTime
            - binance
            - forex
            - uniswap
            - currencylayer
            - vault-fixed
            - ripio
            - chainlink
            - unavailable
        pricedAt:
          type: string
          format: date-time
        tokenInPriceInUSD:
          type: number
        tokenOutPriceInUSD:
          type: number
        amountInAfterPlatformFeesRaw:
          type: string
      required:
        - amountOutRaw
        - source
        - pricedAt
        - tokenInPriceInUSD
        - tokenOutPriceInUSD
        - amountInAfterPlatformFeesRaw
    SwapFeeBreakdown:
      type: object
      properties:
        chargeableAmountInRaw:
          type: string
        amountInAfterPlatformFeesRaw:
          type: string
        reference:
          $ref: "#/components/schemas/SwapFeeBreakdownReference"
        charges:
          type: array
          items:
            $ref: "#/components/schemas/SwapFeeBreakdownLine"
        summary:
          type: object
          properties:
            platformFeesTotalInRaw:
              type: string
            platformFeesTotalInUSD:
              type: number
            bridgeAndSlippageImpactOutRaw:
              type: string
            bridgeAndSlippageImpactOutUSD:
              type: number
      required:
        - chargeableAmountInRaw
        - amountInAfterPlatformFeesRaw
        - reference
        - charges
        - summary
    Quote:
      type: object
      properties:
        id:
          type: string
          description: Unique quote identifier
          example: 550e8400-e29b-41d4-a716-446655440000
        quoteId:
          type: string
          description: Quote ID (same as id)
          example: 550e8400-e29b-41d4-a716-446655440000
        chainIdIn:
          type: number
          description: Canonical source chain ID
          example: 1
        chainIdOut:
          type: number
          description: Canonical destination chain ID
          example: 137
        originChain:
          type: string
          example: ethereum
        destinationChain:
          type: string
          example: polygon
        tokenIn:
          $ref: "#/components/schemas/Token"
        tokenOut:
          $ref: "#/components/schemas/Token"
        provider:
          type: string
          description: Swap provider name
          example: teleswap
        rawQuote:
          nullable: true
          description: Provider-specific quote data
        deadline:
          type: integer
          description: Quote expiration Unix timestamp in seconds
          example: 1705753800
        deadlineDate:
          type: string
          format: date-time
          description: Quote expiration instant as ISO 8601 UTC
          example: 2024-01-20T12:30:00.000Z
        status:
          type: string
          enum:
            - pending
            - confirmed
            - fulfilled
            - expired
            - failed
            - refunded
            - processing
          example: pending
        feeSponsorship:
          type: boolean
          example: false
        customer:
          type: string
          description: Customer ID
        feeBreakdown:
          allOf:
            - $ref: "#/components/schemas/SwapFeeBreakdown"
            - description: Canonical itemized swap costs (Pods, customer, bridge + slippage).
                Use this field for all fee display and accounting in new
                integrations. Not returned on GET /v2/swap/status/:id.
      required:
        - id
        - quoteId
        - originChain
        - destinationChain
        - tokenIn
        - tokenOut
        - provider
        - deadline
        - deadlineDate
      description: Freshly generated swap quote returned by GET /v2/swap/quote
    PersistedQuoteTokenIn:
      type: object
      properties:
        contract:
          $ref: "#/components/schemas/Address"
        symbol:
          type: string
          example: USDC
        decimals:
          type: number
          example: 6
        amount:
          type: string
          example: "1000000"
        chainId:
          type: number
          example: 1
        amountInUSD:
          type: number
          example: 1
        priceInUSD:
          type: number
          example: 1
      required:
        - contract
        - symbol
        - decimals
        - amount
        - chainId
      description: Input token of a persisted quote document
    PersistedQuoteTokenOut:
      type: object
      properties:
        contract:
          $ref: "#/components/schemas/Address"
        symbol:
          type: string
          example: WETH
        decimals:
          type: number
          example: 18
        amount:
          type: string
          example: "332000000000000"
        chainId:
          type: number
          example: 1
        expectedAmountOut:
          type: string
          example: "332000000000000"
        minAmountOut:
          type: string
          example: "328680000000000"
        amountInUSD:
          type: number
          example: 1
        priceInUSD:
          type: number
          example: 3012.5
      required:
        - contract
        - symbol
        - decimals
        - amount
        - chainId
        - expectedAmountOut
        - minAmountOut
      description: Output token of a persisted quote document, including output guarantees
    QuoteUpdateEntry:
      type: object
      properties:
        action:
          type: string
          example: statusChanged
        changes:
          type: object
          additionalProperties:
            nullable: true
          description: Snapshot of the changed quote fields
        timestamp:
          type: string
          format: date-time
          example: 2026-06-23T00:00:00.000Z
        source:
          type: string
        metadata:
          nullable: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
      required:
        - action
        - changes
        - timestamp
      description: Entry in a quote document audit log
    PersistedQuote:
      type: object
      properties:
        _id:
          type: string
          description: MongoDB document id
          example: 6a3aba97dea3bdc9bc03845c
        id:
          type: string
          description: Stringified document id
          example: 6a3aba97dea3bdc9bc03845c
        __v:
          type: number
          example: 0
        quoteId:
          type: string
          example: 8ef5685b-280d-4b01-8a02-97cd05fe1d35
        customer:
          type: string
          description: Customer ID
          example: 6a3ab8c2dea3bdc9bc037071
        provider:
          type: string
          example: uniswap
        status:
          type: string
          enum: &a2
            - pending
            - confirmed
            - fulfilled
            - expired
            - failed
            - refunded
            - processing
          example: pending
        dex:
          type: string
          enum: &a3
            - uniswapv3
            - uniswapv4
            - orderbook
          example: uniswapv3
        originChain:
          type: string
          example: ethereum
        destinationChain:
          type: string
          example: ethereum
        tokenIn:
          $ref: "#/components/schemas/PersistedQuoteTokenIn"
        tokenOut:
          $ref: "#/components/schemas/PersistedQuoteTokenOut"
        deadline:
          type: string
          format: date-time
          description: Quote expiration instant (ISO 8601 UTC)
          example: 2026-06-23T17:00:50.662Z
        slippage:
          type: number
          example: 0.01
        feeSponsorship:
          type: boolean
          example: false
        shouldChargeSwapFees:
          type: boolean
          example: true
        bitcoinDeposit:
          nullable: true
        depositAddress:
          type: string
          nullable: true
        originAddress:
          type: string
          nullable: true
          example: "0xb794F5eA0ba39494cE839613fffBA74279579268"
        destinationAddress:
          type: string
          nullable: true
          example: "0xb794F5eA0ba39494cE839613fffBA74279579268"
        originBlockNumber:
          type: string
          nullable: true
        destinationBlockNumber:
          type: string
          nullable: true
        originTxHash:
          type: string
          nullable: true
        destinationTxHash:
          type: string
          nullable: true
        originTxUrl:
          type: string
          nullable: true
        destinationTxUrl:
          type: string
          nullable: true
        confirmedAt:
          type: string
          nullable: true
          format: date-time
        fulfilledAt:
          type: string
          nullable: true
          format: date-time
        failureReason:
          type: string
          nullable: true
        refundReason:
          type: string
          nullable: true
        refundTxHash:
          type: string
          nullable: true
        refundTxUrl:
          type: string
          nullable: true
        rawQuote:
          type: string
          nullable: true
          description: Provider-specific serialized quote data
        fees:
          nullable: true
          description: Fee context persisted with the quote
        deletedAt:
          type: string
          nullable: true
          format: date-time
        createdAt:
          type: string
          format: date-time
          example: 2026-06-23T16:30:50.662Z
        updatedAt:
          type: string
          format: date-time
          example: 2026-06-23T16:30:50.662Z
        updates:
          type: array
          items:
            $ref: "#/components/schemas/QuoteUpdateEntry"
          description: Audit log of quote state transitions
      required:
        - _id
        - id
        - quoteId
        - customer
        - provider
        - status
        - originChain
        - destinationChain
        - tokenIn
        - tokenOut
        - deadline
        - feeSponsorship
        - shouldChargeSwapFees
        - depositAddress
        - originAddress
        - destinationAddress
        - originBlockNumber
        - destinationBlockNumber
        - originTxHash
        - destinationTxHash
        - originTxUrl
        - destinationTxUrl
        - confirmedAt
        - fulfilledAt
        - failureReason
        - refundReason
        - refundTxHash
        - refundTxUrl
        - rawQuote
        - createdAt
        - updatedAt
      description: Persisted quote document returned by GET /quotes and GET
        /v2/swap/status/{id}
    TransactionData:
      type: object
      properties:
        chainId:
          allOf:
            - $ref: "#/components/schemas/BigIntLike"
            - description: Chain ID as decimal string
              example: "137"
        to:
          $ref: "#/components/schemas/Address"
        data:
          type: string
          description: Transaction calldata (hex)
          example: 0x...
        value:
          type: string
          description: ETH value to send
          example: "0"
        from:
          $ref: "#/components/schemas/Address"
      required:
        - chainId
        - to
        - data
      description: Transaction data for execution
    SolanaTransactionData:
      type: object
      properties:
        rawTransaction:
          type: string
          description: Base64-encoded Solana transaction
          example: AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAED...
        serialized:
          nullable: true
          description: Serialized transaction payload for clients that consume the
            provider-specific shape
      required:
        - rawTransaction
      description: Solana transaction data for execution
    CrossChainRoute:
      type: object
      properties:
        isCrossChain:
          type: boolean
          example: true
        chainIdIn:
          type: number
          example: 8453
        chainIdOut:
          type: number
          example: 42161
      required:
        - isCrossChain
        - chainIdIn
        - chainIdOut
      description: Canonical source and destination chain route
    TrackingSummary:
      type: object
      properties:
        id:
          type: string
          example: 65f1a2b3c4d5e6f789012345
        type:
          type: string
          enum:
            - CROSSCHAIN_SWAP
            - CROSSCHAIN_INVESTMENT_DEPOSIT
            - CROSSCHAIN_INVESTMENT_WITHDRAW
            - SAMECHAIN_SWAP
            - SAMECHAIN_INVESTMENT_DEPOSIT
            - SAMECHAIN_INVESTMENT_WITHDRAW
            - ON_RAMP_FIAT
            - OFF_RAMP_FIAT
            - TRANSFER_IN
            - TRANSFER_OUT
            - RECURRING_FEE_COLLECTION
          example: CROSSCHAIN_INVESTMENT_DEPOSIT
        status:
          type: string
          enum:
            - INITIAL
            - PENDING
            - FAILED
            - REFUND_REQUIRED
            - REFUNDED
            - SUCCESS
            - EXPIRED
          example: PENDING
        walletAddress:
          type: string
          example: "0xb794F5eA0ba39494cE839613fffBA74279579268"
        createdAt:
          type: string
          format: date-time
          example: 2026-05-19T14:30:00.000Z
        updatedAt:
          type: string
          format: date-time
          example: 2026-05-19T14:31:00.000Z
        finishedAt:
          type: string
          nullable: true
          format: date-time
        sourceApp:
          type: string
          example: deframe-api
        tags:
          type: array
          items:
            type: string
        fees:
          nullable: true
          description: Fee context for the tracked operation, when available
        initialDate:
          type: string
          format: date-time
          description: Timestamp of the first action event
          example: 2026-05-19T14:30:00.000Z
        underlyingAssetFeeValue:
          type: string
          description: Underlying-asset denominated fee value
          example: "0"
        stepsSummary:
          type: array
          items:
            type: object
            properties:
              stepIndex:
                type: number
                example: 0
              originChain:
                nullable: true
              chainId:
                type: number
                example: 8453
              status:
                type: string
                example: PENDING
              txHash:
                type: string
                nullable: true
                example: 0x123...
              legsCount:
                type: number
                example: 2
            required:
              - stepIndex
              - status
              - legsCount
      required:
        - id
        - type
        - status
      description: Tracking summary returned by the list endpoint
    TrackingDetail:
      type: object
      properties:
        id:
          type: string
          example: 65f1a2b3c4d5e6f789012345
        type:
          type: string
          example: CROSSCHAIN_INVESTMENT_DEPOSIT
        status:
          type: string
          example: PENDING
        strategyId:
          type: string
          example: 65a1b2c3d4e5f6a7b8c9d0e1
        strategySlug:
          type: string
          example: aave-usdc-polygon
        originAddress:
          type: string
          nullable: true
          example: "0xb794F5eA0ba39494cE839613fffBA74279579268"
        receiverAddress:
          type: string
          nullable: true
          example: "0xb794F5eA0ba39494cE839613fffBA74279579268"
        amounts:
          nullable: true
          description: Formatted asset amounts for the tracked operation
        transactions:
          type: array
          items:
            nullable: true
          description: Formatted transaction history for the tracked operation
        fees:
          nullable: true
          description: Fee context for the tracked operation, when available
        underlyingAssetFeeValue:
          type: string
          description: Underlying-asset denominated fee value
          example: "0"
        initialDate:
          type: string
          format: date-time
          description: Timestamp of the first action event
          example: 2026-05-19T14:30:00.000Z
        createdAt:
          type: string
          format: date-time
          example: 2026-05-19T14:30:00.000Z
        finishedAt:
          type: string
          nullable: true
          format: date-time
      required:
        - id
        - type
        - status
      description: Formatted tracking detail returned by GET /actions/{id}
    KycProfileStatus:
      type: string
      enum:
        - created
        - provider_pending
        - approved
        - rejected
        - rejected_retryable
        - blocked
      description: Normalized Pods KYC status
      example: provider_pending
    KycProvider:
      type: string
      enum:
        - sumsub
        - bigdatacorp
        - external_evidence
      description: KYC provider used for the profile. external_evidence means the
        capture was performed by the customer and submitted to Pods as attested
        evidence.
      example: sumsub
    KycEvidenceSource:
      type: string
      enum:
        - customer_attested
        - deframe_managed
      description: Who performed the capture. customer_attested means the customer ran
        the document or liveness check and attested to its result;
        deframe_managed means Pods ran it.
      example: customer_attested
    KycRejectionReason:
      type: object
      properties:
        code:
          type: string
          description: "Stable Pods rejection code. Program against this value: it does
            not change when a provider is added or replaced. Known codes are
            LIVENESS_FACE_MISMATCH, LIVENESS_FRAUD_SUSPECTED,
            LIVENESS_QUALITY_INSUFFICIENT, IMAGE_QUALITY_INSUFFICIENT,
            DOCUMENT_INVALID, IDENTITY_DATA_MISMATCH, IDENTITY_CPF_MISMATCH,
            TAX_ID_INVALID, TAX_ID_ALREADY_VERIFIED, IDENTITY_DECEASED,
            COMPLIANCE_BLOCKED, UNDER_18 and PROVIDER_ERROR."
          example: TAX_ID_ALREADY_VERIFIED
        message:
          type: string
          description: Human-readable explanation. Safe to show to the applicant.
          example: This person is already verified under another record.
        retryable:
          type: boolean
          description: Whether resubmitting can produce a different outcome. False means
            the rejection is terminal for this person.
          example: false
        retryFrom:
          type: string
          enum:
            - liveness
            - documents
            - applicant
          description: Which step to send the applicant back to. Present only when
            retryable is true.
          example: liveness
      required:
        - code
        - message
        - retryable
      description: Normalized rejection. The raw provider label is deliberately
        excluded — it belongs in logs, not in a contract.
    CreateKycSessionRequest:
      type: object
      properties:
        cpf:
          type: string
          description: Brazilian CPF. Pods normalizes and hashes this value; the session
            itself keeps only the hash and the last four digits. The CPF sent
            later with the evidence is held in the retry buffer for up to 7
            days.
          example: "52998224725"
        email:
          type: string
          format: email
          description: Applicant email
          example: user@example.com
        walletAddress:
          allOf:
            - $ref: "#/components/schemas/Address"
            - description: Applicant EVM wallet address
        externalUserId:
          type: string
          description: Optional id from your system for support correlation
          example: app-user-001
      required:
        - cpf
        - email
        - walletAddress
      description: External evidence session creation payload
    CreateKycSessionResponse:
      type: object
      properties:
        kycUserId:
          type: string
          format: uuid
          description: Pods-generated public KYC user id. Use it to submit evidence and to
            poll status.
          example: 550e8400-e29b-41d4-a716-446655440000
        status:
          allOf:
            - $ref: "#/components/schemas/KycProfileStatus"
            - example: created
        existing:
          type: boolean
          description: True when the same tax ID already had a session in this customer
            and it was returned instead of a new one. The call is idempotent per
            (customer, tax ID).
          example: false
      required:
        - kycUserId
        - status
        - existing
      description: External evidence session result
    KycEvidenceImage:
      type: object
      properties:
        base64:
          type: string
          description: Base64-encoded JPEG or PNG, without a data URI prefix. Pods streams
            it to the ramp provider in memory and never stores it — only a
            SHA-256 digest is kept. Provide this or url, never both.
          example: /9j/4AAQSkZJRgABAQAAAQ...
        mimeType:
          type: string
          enum:
            - image/jpeg
            - image/png
          description: Defaults to image/jpeg. The bytes are validated against their magic
            number regardless of what is declared here, on both transports.
          example: image/jpeg
        url:
          type: string
          description: HTTPS URL Pods fetches the image from, for callers whose capture
            already lives in object storage. Must be reachable without Pods
            credentials — a pre-signed URL — and must stay valid for the whole
            request. Provide this or base64, never both. Images in the same
            request may use different transports.
          example: https://s3.us-east-1.amazonaws.com/your-bucket/documents/abc123?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=...
      description: A single captured image, sent inline as base64 or fetched by Pods
        from a URL. Exactly one of the two is required.
    KycEvidenceAddress:
      type: object
      properties:
        country:
          type: string
          description: Country of residence. Only BRA is accepted; defaults to BRA.
          example: BRA
        state:
          type: string
          description: Brazilian state code
          example: SP
        city:
          type: string
          example: Sao Paulo
        zipCode:
          type: string
          example: "01310100"
        streetAddress:
          type: string
          example: Av. Paulista
        number:
          type: string
          example: "1000"
        complement:
          type: string
          example: Sala 1
      required:
        - state
        - city
        - zipCode
        - streetAddress
        - number
      description: Address forwarded to the ramp provider. Retained for up to 7 days
        as part of the retry buffer, then deleted automatically — see the
        retention note on the submit endpoint.
    SubmitKycEvidenceRequest:
      type: object
      properties:
        address:
          $ref: "#/components/schemas/KycEvidenceAddress"
        applicant:
          type: object
          properties:
            fullName:
              type: string
              description: Falls back to the name extracted from the document when omitted.
              example: MARIA DA SILVA
            dateOfBirth:
              type: string
              description: YYYY-MM-DD. Falls back to the birth date extracted from the
                document when omitted. The applicant must be 18 or older.
              example: 1990-05-14
            phone:
              type: string
              example: "+5511999999999"
          description: Applicant data. Both fields are optional because the document
            attestation already carries them.
        liveness:
          type: object
          properties:
            attestation:
              type: object
              properties:
                provider:
                  type: string
                  description: Identifier of the liveness provider you ran.
                  example: aws_rekognition_face_liveness
                status:
                  type: string
                  description: Result of your liveness check. Anything other than SUCCEEDED /
                    PASSED is rejected before any provider call is made.
                  example: SUCCEEDED
                confidence:
                  type: number
                  minimum: 0
                  maximum: 100
                  description: Liveness confidence, 0-100. Compared against the Pods minimum and
                    stored for audit; it is never returned by any endpoint and
                    must not be shown to the applicant.
                  example: 95.78
                sessionId:
                  type: string
                  description: Your provider session id, recorded so a capture can be correlated
                    with ours during a support investigation.
                  example: 7a1f1c9c-6d6e-4a5f-9c11-1f0f6d3d2b21
                completedAt:
                  type: string
                  example: 2026-08-13T20:08:57.000Z
                challenge:
                  type: object
                  properties:
                    type:
                      type: string
                      example: FaceMovementAndLightChallenge
                    version:
                      type: string
                      example: 2.0.0
              required:
                - provider
                - status
            image:
              allOf:
                - $ref: "#/components/schemas/KycEvidenceImage"
                - description: The selfie frame captured during the liveness check.
          required:
            - attestation
            - image
          description: Liveness evidence and the attestation of the check you ran
        documents:
          type: object
          properties:
            documentType:
              type: string
              enum:
                - RG
                - CNH
                - PASSPORT
              example: RG
            front:
              $ref: "#/components/schemas/KycEvidenceImage"
            back:
              allOf:
                - $ref: "#/components/schemas/KycEvidenceImage"
                - description: Required for RG and CNH; omit for PASSPORT.
            attestation:
              type: object
              properties:
                provider:
                  type: string
                  description: Identifier of the documentoscopy provider you ran.
                  example: bigdatacorp_documentoscopy
                onboardingId:
                  type: string
                  description: Your provider onboarding id, recorded for support correlation.
                  example: onboarding-001
                extracted:
                  type: object
                  properties:
                    cpf:
                      type: string
                      description: CPF read from the document. It must hash to the same value as the
                        CPF the session was created with, otherwise the request
                        is rejected as IDENTITY_CPF_MISMATCH.
                      example: "52998224725"
                    fullName:
                      type: string
                      example: MARIA DA SILVA
                    birthDate:
                      type: string
                      example: 1990-05-14
                    documentType:
                      type: string
                      example: RG
                  required:
                    - cpf
                    - fullName
                    - birthDate
                  description: Data your documentoscopy provider read from the document
              required:
                - provider
                - extracted
          required:
            - documentType
            - front
            - attestation
          description: Document images and the attestation of the documentoscopy you ran
      required:
        - address
        - applicant
        - liveness
        - documents
      description: Every piece of evidence for one applicant, submitted in a single request
    SubmitKycEvidenceResponse:
      type: object
      properties:
        kycUserId:
          type: string
          format: uuid
          description: Pods-generated public KYC user id
          example: 550e8400-e29b-41d4-a716-446655440000
        status:
          $ref: "#/components/schemas/KycProfileStatus"
        timings:
          type: object
          additionalProperties:
            type: number
          description: Per-phase duration in milliseconds (validate, identityScan, upload,
            level1, totalMs). Useful for diagnosing a slow submission; the image
            upload dominates.
          example:
            validate: 8
            identityScan: 0
            upload: 22182
            level1: 376
            totalMs: 22578
      required:
        - kycUserId
        - status
      description: External evidence submission result
    SubmitSumsubShareTokenRequest:
      type: object
      properties:
        cpf:
          type: string
          description: "Brazilian CPF. Optional, and accepted alongside any taxIdCountry —
            a foreign national resident in Brazil holds both. Send it whenever
            it is known: it is what lets Pods reuse an already-approved identity
            instead of creating a duplicate at Avenia, and it is what opens the
            BRL rail. Pods stores only a salted hash and the last four digits."
          example: "52998224725"
        taxId:
          type: string
          description: Tax identifier for an applicant verified with a document that
            carries no CPF, such as a passport, or whose jurisdiction is not
            Brazil. Supply either cpf or taxId; when taxIdCountry is BRA and the
            value is itself a valid CPF, Pods treats it as one. Pods stores only
            a salted hash and the last four digits.
          example: FX1234567
        taxIdCountry:
          type: string
          minLength: 3
          maxLength: 3
          description: ISO 3166-1 alpha-3 jurisdiction of the tax id (for example BRA or
            USA). Required whenever the tax id is not a CPF; it is inferred as
            BRA only when a cpf is sent or the taxId itself passes the CPF check
            digits. The country is part of the stored identity hash, so the same
            digits under two jurisdictions are two different identities — and a
            profile submitted under the wrong country cannot be corrected
            afterwards. A profile without a CPF does not receive the BRL ramp;
            unlock USD with POST /api/v1/kyc/currency-unlock.
          example: BRA
        shareToken:
          type: string
          description: Fresh Sumsub reusable KYC share token generated for the Avenia
            recipient
          example: SUMSUB_SHARE_TOKEN
        sumsubApplicantId:
          type: string
          description: Unique Sumsub applicant id from the approved applicantReviewed
            webhook. Pods uses it for customer-scoped idempotency.
          example: 6a3502bf7fee2fab5377cb1e
        email:
          type: string
          format: email
          description: Applicant email
          example: user@example.com
        walletAddress:
          allOf:
            - $ref: "#/components/schemas/WalletAddress"
            - description: Applicant wallet. Checksummed EVM address or Solana public key.
      required:
        - shareToken
        - sumsubApplicantId
        - email
        - walletAddress
      description: Reusable KYC share token import payload. At least one of cpf or
        taxId is required; sending both is allowed and cpf is what identity
        matching uses.
    SubmitSumsubShareTokenResponse:
      type: object
      properties:
        kycUserId:
          type: string
          format: uuid
          description: Pods-generated public KYC user id
          example: 550e8400-e29b-41d4-a716-446655440000
        status:
          $ref: "#/components/schemas/KycProfileStatus"
      required:
        - kycUserId
        - status
      description: KYC import submission result
    SubmitForeignSumsubShareTokenRequest:
      type: object
      properties:
        email:
          type: string
          format: email
          description: Applicant email
          example: user@example.com
        shareToken:
          type: string
          description: Fresh Sumsub reusable KYC share token generated for the Avenia
            recipient
          example: SUMSUB_SHARE_TOKEN
        sumsubApplicantId:
          type: string
          description: Unique Sumsub applicant id from the approved applicantReviewed
            webhook. Pods uses it for customer-scoped idempotency.
          example: 6a3502bf7fee2fab5377cb1e
        taxId:
          type: string
          description: Foreign tax identifier (for example SSN or national ID). Pods
            stores only a salted hash and the last four digits.
          example: "123456789"
        taxIdCountry:
          type: string
          minLength: 3
          maxLength: 3
          description: ISO 3166-1 alpha-3 country code for the tax id (for example USA)
          example: USA
        walletAddress:
          allOf:
            - $ref: "#/components/schemas/WalletAddress"
            - description: Applicant wallet. Checksummed EVM address or Solana public key.
      required:
        - email
        - shareToken
        - sumsubApplicantId
        - taxId
        - taxIdCountry
        - walletAddress
      description: Foreign reusable KYC share token import payload (non-Brazil tax id)
    SubmitCurrencyKycUnlockRequest:
      type: object
      properties:
        currency:
          type: string
          enum:
            - usd
          description: Foreign currency capability to unlock after Avenia approves the KYC
            profile
          example: usd
        kycUserId:
          type: string
          format: uuid
          description: Pods-generated KYC user id returned by the foreign share-token
            endpoint
          example: 550e8400-e29b-41d4-a716-446655440000
      required:
        - currency
        - kycUserId
      description: Request Avenia currency unlock for an approved foreign KYC profile
    StartBigDataCorpSessionRequest:
      type: object
      properties:
        cpf:
          type: string
          description: Brazilian CPF. Pods normalizes and hashes this value; the full CPF
            is not persisted.
          example: "52998224725"
        email:
          type: string
          format: email
          description: Applicant email
          example: user@example.com
        walletAddress:
          allOf:
            - $ref: "#/components/schemas/WalletAddress"
            - description: Applicant wallet. Checksummed EVM address or Solana public key.
        externalUserId:
          type: string
          description: Optional id from your system for support correlation
          example: app-user-001
        restartExistingSession:
          type: boolean
          description: When true, always create a fresh BigDataCorp iframe session for
            this KYC profile. Use this when retrying or remounting a mobile
            WebView that may cache an earlier iframe URL.
          example: true
      required:
        - cpf
        - email
        - walletAddress
      description: BigDataCorp iframe session creation payload
    StartBigDataCorpSessionResponse:
      type: object
      properties:
        kycUserId:
          type: string
          format: uuid
          description: Pods-generated public KYC user id
          example: 550e8400-e29b-41d4-a716-446655440000
        iframeUrl:
          type: string
          format: uri
          description: BigDataCorp iframe URL for document and liveness capture. When
            capture is not needed because an approved Avenia identity was
            reused, Pods returns a safe completion URL instead of null for
            mobile WebView compatibility.
          example: https://iframe.bigdatacorp.com.br/550e8400-e29b-41d4-a716-446655440000
        iframeSessionId:
          type: string
          nullable: true
          description: Current BigDataCorp onboarding id. Use it as the WebView remount
            key so a cached iframe cannot be displayed after Pods returns a
            newer session. Null when no capture is needed.
          example: bigdatacorp-onboarding-id
        requiresWebView:
          type: boolean
          description: Whether the client must mount the returned iframe URL for
            BigDataCorp capture.
          example: true
        status:
          allOf:
            - $ref: "#/components/schemas/KycProfileStatus"
            - example: created
      required:
        - kycUserId
        - iframeUrl
        - iframeSessionId
        - requiresWebView
        - status
      description: BigDataCorp session result. Requests reuse an active capture for up
        to seven minutes unless restartExistingSession is true, which creates a
        fresh iframe session in the same KYC profile. An approved Avenia
        identity reuse returns status approved, a safe completion iframe URL,
        null iframeSessionId, and requiresWebView false. Poll the returned
        kycUserId through the canonical status endpoint.
    BigDataCorpAveniaAddress:
      type: object
      properties:
        country:
          type: string
          description: Country of residence. Only BRA is accepted.
          example: BRA
        state:
          type: string
          description: Brazilian state code
          example: SP
        city:
          type: string
          example: Sao Paulo
        zipCode:
          type: string
          example: "01001000"
        streetAddress:
          type: string
          example: Rua Teste
        number:
          type: string
          example: "123"
        complement:
          type: string
          example: Sala 1
      required:
        - state
        - city
        - zipCode
        - streetAddress
        - number
      description: Address data forwarded to Avenia during the final BigDataCorp KYC
        submit. Pods does not persist this address.
    SubmitBigDataCorpKycRequest:
      type: object
      properties:
        kycUserId:
          type: string
          format: uuid
          description: Pods-generated KYC user id returned by the BigDataCorp session
            endpoint
          example: 550e8400-e29b-41d4-a716-446655440000
        phone:
          type: string
          description: Optional applicant phone forwarded to Avenia
          example: "+5511999999999"
        address:
          $ref: "#/components/schemas/BigDataCorpAveniaAddress"
      required:
        - kycUserId
        - address
      description: Final BigDataCorp KYC submission payload
    SubmitBigDataCorpKycResponse:
      type: object
      properties:
        kycUserId:
          type: string
          format: uuid
          description: Pods-generated public KYC user id
          example: 550e8400-e29b-41d4-a716-446655440000
        status:
          $ref: "#/components/schemas/KycProfileStatus"
      required:
        - kycUserId
        - status
      description: BigDataCorp KYC submission result
    KycStatusResponse:
      type: object
      properties:
        kycUserId:
          type: string
          format: uuid
          description: Pods-generated public KYC user id
          example: 550e8400-e29b-41d4-a716-446655440000
        provider:
          $ref: "#/components/schemas/KycProvider"
        email:
          type: string
          format: email
          example: user@example.com
        walletAddress:
          $ref: "#/components/schemas/WalletAddress"
        status:
          $ref: "#/components/schemas/KycProfileStatus"
        brlaEnabled:
          type: boolean
          example: false
        usdEnabled:
          type: boolean
          description: True when the profile is approved for USD WIRE/ACH ramps via Avenia
          example: false
        eurEnabled:
          type: boolean
          description: Reserved for a future EUR capability; always false today
          example: false
        rejectReason:
          type: string
          nullable: true
        providerStatus:
          type: string
          nullable: true
          example: COMPLETED
        providerResult:
          type: string
          nullable: true
          description: Provider-specific normalized result. BigDataCorp may return values
            such as WAITING_ADDRESS_TO_SUBMIT_TO_AVENIA, DUPLICATE_TAX_ID, or
            AVENIA_KYC_SUBMIT_FAILED before the final Pods status is approved.
          example: APPROVED
        providerMessage:
          type: string
          nullable: true
          description: Provider message persisted for support and retry decisions, when
            available.
        livenessStatus:
          type: string
          nullable: true
          example: passed
        documentStatus:
          type: string
          nullable: true
          example: passed
        birthDate:
          type: string
          nullable: true
          description: Resolved birth date in YYYY-MM-DD format for BigDataCorp profiles.
            Null while pending, unavailable, or not applicable to the provider.
          example: 1990-01-01
        ageAtVerification:
          type: number
          nullable: true
          example: 36
        bigDataCorpIframeCreatedAt:
          type: string
          nullable: true
          format: date-time
          description: When the current BigDataCorp iframe session was created.
          example: 2026-06-15T12:00:00.000Z
        bigDataCorpCompletedAt:
          type: string
          nullable: true
          format: date-time
          description: When BigDataCorp completed document, liveness, and identity
            verification.
          example: 2026-06-15T12:04:00.000Z
        aveniaSubmissionStartedAt:
          type: string
          nullable: true
          format: date-time
          description: When Pods started the final Avenia submission. Repeated submit
            requests return the existing provider_pending result while this
            operation is in progress.
          example: 2026-06-15T12:04:30.000Z
        aveniaSubmittedAt:
          type: string
          nullable: true
          format: date-time
          description: When the verified BigDataCorp profile was submitted to Avenia.
          example: 2026-06-15T12:05:00.000Z
        aveniaApprovedAt:
          type: string
          nullable: true
          format: date-time
          description: When Avenia approved the KYC attempt. For Brazil profiles this
            coincides with brlaEnabled; for foreign profiles poll usdEnabled
            after currency unlock.
          example: 2026-06-15T12:06:00.000Z
        updatedAt:
          type: string
          format: date-time
          example: 2026-06-15T12:01:00.000Z
        documentEvidenceSource:
          allOf:
            - $ref: "#/components/schemas/KycEvidenceSource"
            - nullable: true
              description: Who captured the document. Null for profiles created before
                evidence sources were recorded.
        livenessEvidenceSource:
          allOf:
            - $ref: "#/components/schemas/KycEvidenceSource"
            - nullable: true
              description: Who ran the liveness check.
        documentType:
          type: string
          nullable: true
          description: Type of identity document submitted, when known.
        reason:
          allOf:
            - $ref: "#/components/schemas/KycRejectionReason"
            - nullable: true
              description: Present only when status is rejected or rejected_retryable. Prefer
                this over rejectReason and providerResult, which carry raw
                provider values and vary between providers.
      required:
        - kycUserId
        - provider
        - email
        - walletAddress
        - status
        - brlaEnabled
        - usdEnabled
        - eurEnabled
        - rejectReason
        - providerStatus
        - providerResult
        - providerMessage
        - livenessStatus
        - documentStatus
        - birthDate
        - ageAtVerification
        - bigDataCorpIframeCreatedAt
        - bigDataCorpCompletedAt
        - aveniaSubmissionStartedAt
        - aveniaSubmittedAt
        - aveniaApprovedAt
        - documentEvidenceSource
        - livenessEvidenceSource
        - documentType
        - reason
      description: Current normalized KYC profile status
    DirectionalRampLimit:
      type: object
      properties:
        remaining:
          type: string
          description: Remaining allowance in the requested currency, rounded down to at
            most two decimal places. Omitted when Avenia does not expose a
            limit.
          example: "6000"
        remainingGlobalUsd:
          type: string
          description: Remaining global allowance for this direction in USD, rounded down
            to at most two decimal places. Omitted when Avenia does not expose a
            global limit.
          example: "1600"
    RampLimitsResponse:
      type: object
      properties:
        provider:
          type: string
          enum:
            - avenia
        currency:
          type: string
          description: Requested three-letter ISO currency code
          example: BRL
        remaining:
          type: string
          description: Backward-compatible alias for `offramp.remaining`
          deprecated: true
          example: "5500"
        remainingGlobalUsd:
          type: string
          description: Backward-compatible alias for `offramp.remainingGlobalUsd`
          deprecated: true
          example: "1100"
        onramp:
          $ref: "#/components/schemas/DirectionalRampLimit"
        offramp:
          $ref: "#/components/schemas/DirectionalRampLimit"
        blocked:
          type: boolean
          description: Whether Avenia has blocked the account. Both directional remaining
            values are zero when true.
          example: false
      required:
        - provider
        - currency
        - onramp
        - offramp
        - blocked
    NoRampLimitProviderResponse:
      type: object
      properties:
        provider:
          nullable: true
      required:
        - provider
    StrategyProtocolInfo:
      type: object
      properties:
        name:
          type: string
          example: Aave
        logo:
          type: string
          format: uri
          example: https://icons.llamao.fi/icons/protocols/aave
      required:
        - name
        - logo
    StrategyChain:
      type: object
      properties:
        name:
          type: string
          example: polygon
        id:
          type: number
          example: 137
        explorer:
          type: object
          properties:
            name:
              type: string
              example: Polygonscan
            url:
              type: string
              format: uri
              example: https://polygonscan.com
          required:
            - name
            - url
        logo:
          type: string
          nullable: true
          format: uri
      required:
        - name
        - id
        - explorer
    StrategyToken:
      type: object
      properties:
        name:
          type: string
          example: USD Coin
        address:
          type: string
          example: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
        symbol:
          type: string
          example: USDC
        decimals:
          type: number
          example: 6
        logo:
          type: string
          nullable: true
          format: uri
      required:
        - name
        - address
        - symbol
        - decimals
    StrategyMarketData:
      type: object
      properties:
        lastRefreshedAt:
          type: string
          format: date-time
        samples:
          type: array
          items:
            type: object
            properties:
              at:
                type: string
                format: date-time
              priceUsd:
                type: number
              tvl:
                type: string
              tvlUsd:
                type: number
            required:
              - at
              - tvl
              - tvlUsd
          maxItems: 2
        sharePrice:
          type: number
          nullable: true
        sharePriceChange24h:
          type: number
          nullable: true
        sharePriceRefreshedAt:
          type: string
          nullable: true
          format: date-time
        tvl:
          type: string
        tvlChange24h:
          type: number
          nullable: true
        tvlUsd:
          type: number
        usdRate:
          type: number
    StrategyListBasicItem:
      type: object
      properties:
        id:
          type: string
          example: Aave-USDC-polygon
        slug:
          type: string
          example: Aave-USDC-polygon
        asset:
          type: string
          description: Strategy asset contract address
        assetDecimals:
          type: number
          example: 6
        assetName:
          type: string
          example: USDC
        availableActions:
          type: array
          items:
            type: string
          example: &a1
            - lend
            - withdraw
        implementationSelector:
          type: string
          example: AavePolygon
        network:
          type: string
          example: polygon
        networkId:
          type: string
          example: "137"
        protocol:
          type: string
          example: Aave
        underlyingAsset:
          type: string
        underlyingDecimals:
          type: number
        isDefault:
          type: boolean
        paused:
          type: boolean
        startDate:
          type: string
        fee:
          type: string
          example: "0"
        performanceFeeBps:
          type: string
          example: "0"
        markupFeeBps:
          type: string
          description: Customer markup fee in basis points, charged separately from the
            platform performance fee at the contract level. Zero when no markup
            is configured.
          example: "0"
        metadata:
          type: object
          properties: {}
        marketData:
          allOf:
            - $ref: "#/components/schemas/StrategyMarketData"
            - nullable: true
        protocolInfo:
          $ref: "#/components/schemas/StrategyProtocolInfo"
        chain:
          $ref: "#/components/schemas/StrategyChain"
        assetToken:
          $ref: "#/components/schemas/StrategyToken"
        underlyingToken:
          $ref: "#/components/schemas/StrategyToken"
        logourl:
          type: string
        category:
          type: array
          items:
            type: string
      required:
        - id
        - slug
        - asset
        - assetDecimals
        - assetName
        - availableActions
        - implementationSelector
        - network
        - networkId
        - protocol
        - underlyingAsset
        - underlyingDecimals
        - isDefault
        - paused
        - startDate
        - fee
        - performanceFeeBps
        - metadata
      description: Strategy item returned by GET /strategies when details=BASIC
    StrategyListItem:
      allOf:
        - $ref: "#/components/schemas/StrategyListBasicItem"
        - type: object
          properties:
            apy:
              type: number
              description: Mirrors `grossAPY`. Kept for backward compatibility — read `netAPY`
                for expected earnings.
              example: 0.0285
            avgApy:
              type: number
              description: "Annualized average rate since the strategy start date. Gross: no
                Pods fee deducted."
              example: 0.0406
            grossAPY:
              type: number
              description: Raw protocol APY, before any Pods fee.
              example: 0.0285
            inceptionApy:
              type: number
              description: "Cumulative growth since inception, not annualized. Gross: no Pods
                fee deducted."
              example: 0.1468
            netAPY:
              type: number
              description: "Expected APY after Pods fees: `grossAPY * (1 - (performanceFeeBps
                + markupFeeBps) / 10000)`."
              example: 0.0285
            realizedApy7d:
              type: number
              nullable: true
              description: Strategy-level realized yield over the trailing 7 days, annualized,
                as a decimal ratio gross of performance fees. Null when recorded
                rate history does not cover the window.
              example: 0.0412
            realizedApy30d:
              type: number
              nullable: true
              description: Strategy-level realized yield over the trailing 30 days,
                annualized, as a decimal ratio gross of performance fees. Null
                when recorded rate history does not cover the window.
              example: 0.0388
          required:
            - protocolInfo
            - chain
            - assetToken
            - underlyingToken
            - apy
            - avgApy
            - grossAPY
            - inceptionApy
            - netAPY
            - realizedApy7d
            - realizedApy30d
      description: Strategy item returned by GET /strategies when details=FULL (default)
    StrategyMetadata:
      type: object
      properties:
        id:
          type: string
          example: Aave-USDC-polygon
        slug:
          type: string
          example: Aave-USDC-polygon
        asset:
          type: string
          description: Strategy asset contract address
        assetDecimals:
          type: number
          example: 6
        assetName:
          type: string
          example: USDC
        availableActions:
          type: array
          items:
            type: string
          example: *a1
        implementationSelector:
          type: string
          example: AavePolygon
        network:
          type: string
          example: polygon
        networkId:
          type: string
          example: "137"
        protocol:
          type: string
          example: Aave
        underlyingAsset:
          type: string
        underlyingDecimals:
          type: number
        isDefault:
          type: boolean
        paused:
          type: boolean
        startDate:
          type: string
        fee:
          type: string
          example: "0"
        performanceFeeBps:
          type: string
          example: "0"
        markupFeeBps:
          type: string
          description: Customer markup fee in basis points, charged separately from the
            platform performance fee at the contract level. Zero when no markup
            is configured.
          example: "0"
        metadata:
          type: object
          properties: {}
        marketData:
          allOf:
            - $ref: "#/components/schemas/StrategyMarketData"
            - nullable: true
        protocolInfo:
          $ref: "#/components/schemas/StrategyProtocolInfo"
        chain:
          $ref: "#/components/schemas/StrategyChain"
        assetToken:
          $ref: "#/components/schemas/StrategyToken"
        underlyingToken:
          $ref: "#/components/schemas/StrategyToken"
        logourl:
          type: string
        category:
          type: array
          items:
            type: string
      required:
        - id
        - slug
        - asset
        - assetDecimals
        - assetName
        - availableActions
        - implementationSelector
        - network
        - networkId
        - protocol
        - underlyingAsset
        - underlyingDecimals
        - isDefault
        - paused
        - startDate
        - fee
        - performanceFeeBps
        - metadata
      description: Strategy metadata nested in detail and wallet responses (no APY
        fields at this level)
    StrategyStatusActionLeg:
      type: object
      properties:
        kind:
          type: string
          example: bridge
        operator:
          type: string
          nullable: true
      required:
        - kind
        - operator
    StrategyStatusActionStep:
      type: object
      properties:
        stepIndex:
          type: number
          example: 0
        status:
          type: string
          example: PENDING
        legs:
          type: array
          items:
            $ref: "#/components/schemas/StrategyStatusActionLeg"
      required:
        - stepIndex
        - status
        - legs
    StrategyStatusAction:
      type: object
      properties:
        id:
          type: string
          example: 65f1a2b3c4d5e6f789012345
        type:
          type: string
          example: CROSSCHAIN_INVESTMENT_DEPOSIT
        status:
          type: string
          example: PENDING
        createdAt:
          type: string
          nullable: true
          format: date-time
        updatedAt:
          type: string
          nullable: true
          format: date-time
        currentStep:
          allOf:
            - $ref: "#/components/schemas/StrategyStatusActionStep"
            - nullable: true
        steps:
          type: array
          items:
            $ref: "#/components/schemas/StrategyStatusActionStep"
        suw:
          nullable: true
          description: Ondo single-use-wallet CoW snapshot, only for Ondo stock strategies
      required:
        - id
        - type
        - status
        - currentStep
        - steps
    StrategyStatusResponse:
      type: object
      properties:
        strategyId:
          type: string
          example: Aave-USDC-polygon
        wallet:
          type: string
          example: "0xb794F5eA0ba39494cE839613fffBA74279579268"
        hasPending:
          type: boolean
          example: false
        message:
          type: string
          example: No pending actions for this strategy. You are all caught up.
        orderBook:
          nullable: true
          description: CoW order-book availability, only for Ondo BSC stock strategies
        gmMarket:
          nullable: true
          description: Ondo GM market and asset tradability, only for Ethereum primary
            stock strategies
        actions:
          type: array
          items:
            $ref: "#/components/schemas/StrategyStatusAction"
      required:
        - strategyId
        - wallet
        - hasPending
        - message
        - actions
      description: Pending strategy investment status for a wallet
    SpotPositionApy:
      type: object
      properties:
        apy:
          type: number
          description: Mirrors `grossAPY`. Kept for backward compatibility — read `netAPY`
            for expected earnings.
          example: 0.0285
        avgApy:
          type: number
          description: "Annualized average rate since the strategy start date. Gross: no
            Pods fee deducted."
          example: 0.0406
        grossAPY:
          type: number
          description: Raw protocol APY, before any Pods fee.
          example: 0.0285
        inceptionApy:
          type: number
          description: "Cumulative growth since inception, not annualized. Gross: no Pods
            fee deducted."
          example: 0.1468
        netAPY:
          type: number
          description: "Expected APY after Pods fees: `grossAPY * (1 - (performanceFeeBps
            + markupFeeBps) / 10000)`."
          example: 0.0285
      required:
        - apy
        - avgApy
        - grossAPY
        - inceptionApy
        - netAPY
      description: APY-only position metrics (no wallet balances)
    AssetAmount:
      type: object
      properties:
        asset:
          type: string
        decimals:
          type: number
        humanized:
          type: string
        symbol:
          type: string
        value:
          $ref: "#/components/schemas/BigIntLike"
        valueUSD:
          type: string
      required:
        - asset
        - decimals
        - humanized
        - symbol
        - value
      description: Token or position amount serialized at the API boundary
    ProfitAmount:
      allOf:
        - $ref: "#/components/schemas/AssetAmount"
        - type: object
          properties:
            afterFees:
              type: string
              description: "Profit after Pods fees, in underlying base units: `value * (1 -
                (performanceFeeBps + markupFeeBps) / 10000)`, truncated. Fees
                are charged on profit only, so a loss is returned unchanged.
                `value` remains the gross figure."
              example: "40000000"
          required:
            - afterFees
      description: Profit amount, carrying both the gross value and the figure net of
        Pods fees
    WalletSpotPosition:
      type: object
      properties:
        apy:
          type: number
          description: Mirrors `grossAPY`. Kept for backward compatibility — read `netAPY`
            for expected earnings.
        avgApy:
          type: number
          description: "Annualized average rate since the strategy start date. Gross: no
            Pods fee deducted."
        grossAPY:
          type: number
          description: Raw protocol APY, before any Pods fee.
        inceptionApy:
          type: number
          description: "Cumulative growth since inception, not annualized. Gross: no Pods
            fee deducted."
        netAPY:
          type: number
          description: "Expected APY after Pods fees: `grossAPY * (1 - (performanceFeeBps
            + markupFeeBps) / 10000)`."
        cumulativeProfit:
          $ref: "#/components/schemas/AssetAmount"
        currentPosition:
          $ref: "#/components/schemas/AssetAmount"
        currentPositionInShares:
          $ref: "#/components/schemas/AssetAmount"
        principal:
          $ref: "#/components/schemas/AssetAmount"
        profit:
          $ref: "#/components/schemas/ProfitAmount"
        profitInUSD:
          type: number
          nullable: true
        receiptBalance:
          $ref: "#/components/schemas/AssetAmount"
        underlyingBalance:
          $ref: "#/components/schemas/AssetAmount"
        underlyingBalanceUSD:
          anyOf:
            - type: number
            - type: string
        availableToClaim:
          type: array
          items:
            type: object
            properties: {}
        availableToWithdraw:
          type: array
          items:
            type: object
            properties: {}
        requestedToLend:
          type: array
          items:
            type: object
            properties: {}
        requestedToLendInShares:
          type: array
          items:
            type: object
            properties: {}
        requestedToWithdraw:
          type: array
          items:
            type: object
            properties: {}
        requestedToWithdrawInShares:
          type: array
          items:
            type: object
            properties: {}
        realizedApy7d:
          type: number
          nullable: true
          description: Strategy-level realized yield over the trailing 7 days, annualized,
            as a decimal ratio gross of performance fees. Measured between daily
            conversion-rate snapshots ending at the latest snapshot, so it
            reflects the growth the strategy actually delivered rather than an
            average of advertised APY. Null when recorded rate history does not
            cover the window.
          example: 0.0412
        realizedApy30d:
          type: number
          nullable: true
          description: Strategy-level realized yield over the trailing 30 days,
            annualized, as a decimal ratio gross of performance fees. Measured
            between daily conversion-rate snapshots ending at the latest
            snapshot, so it reflects the growth the strategy actually delivered
            rather than an average of advertised APY. Null when recorded rate
            history does not cover the window.
          example: 0.0388
      required:
        - realizedApy7d
        - realizedApy30d
      description: Wallet-scoped position metrics including balances and profit
    AnalyticsItem:
      type: object
      properties:
        _id:
          type: string
        createdAt:
          type: string
          format: date-time
        dateString:
          type: string
        messageContent:
          type: object
          properties: {}
        strategy:
          type: object
          properties:
            _id:
              type: string
            assetName:
              type: string
            network:
              type: string
            networkId:
              type: string
            protocol:
              type: string
            underlyingAsset:
              type: string
            underlyingDecimals:
              type: number
          required:
            - _id
            - assetName
            - network
            - networkId
            - protocol
            - underlyingAsset
            - underlyingDecimals
        tokenPrice:
          type: object
          properties: {}
      required:
        - _id
        - createdAt
        - dateString
        - messageContent
        - strategy
      description: Analytics webhook event returned by GET /strategies/analytics
    HealthStrategyResponse:
      type: object
      properties:
        strategyId:
          type: string
          example: 67904d5061866a32dd2d39ed
        paused:
          type: boolean
          example: false
        source:
          type: string
          enum:
            - simulation
            - onChainFlags
          example: simulation
        from:
          allOf:
            - $ref: "#/components/schemas/Address"
            - description: Simulated wallet address when source=simulation
        lendTransactions:
          type: number
          example: 4
        withdrawTransactions:
          type: number
          example: 0
      required:
        - strategyId
        - paused
        - source
      description: Strategy health check result
    StrategyMarketDataHistoryResponse:
      type: object
      properties:
        data:
          type: array
          items:
            type: object
            properties:
              timestamp:
                type: integer
                description: Sample timestamp in Unix milliseconds
              value:
                type: number
                description: Closing share price in USD
              open:
                type: number
              high:
                type: number
              low:
                type: number
              close:
                type: number
            required:
              - timestamp
              - value
              - open
              - high
              - low
              - close
        pagination:
          $ref: "#/components/schemas/Pagination"
        range:
          type: string
          enum:
            - 1d
            - 1w
            - 1m
            - 3m
            - 1y
            - all
        resolution:
          type: string
          enum:
            - 15m
            - 1h
            - 1d
        earliestAvailable:
          type: string
          nullable: true
          format: date-time
        strategyId:
          type: string
      required:
        - data
        - pagination
        - range
        - resolution
        - earliestAvailable
        - strategyId
      description: Paginated strategy share-price candles for the requested range
    TokenCatalogResponse:
      type: object
      properties:
        tokens:
          type: array
          items:
            type: object
            properties: {}
        tokenGroups:
          type: array
          items:
            type: object
            properties: {}
        pagination:
          $ref: "#/components/schemas/Pagination"
        tokenGroupsPagination:
          $ref: "#/components/schemas/Pagination"
      required:
        - tokens
      description: Token catalog with optional token groups and pagination metadata
    SmartAccountResponse:
      type: object
      properties:
        address:
          $ref: "#/components/schemas/Address"
        owner:
          $ref: "#/components/schemas/Address"
        chainId:
          type: number
          example: 8453
      required:
        - address
        - owner
        - chainId
      description: Deterministic ERC-4337 Gnosis Safe smart account address
    SwapStatusDocument:
      type: object
      properties:
        _id:
          type: string
          description: MongoDB document id
          example: 6a3aba97dea3bdc9bc03845c
        id:
          type: string
          description: Stringified document id
          example: 6a3aba97dea3bdc9bc03845c
        __v:
          type: number
          example: 0
        quoteId:
          type: string
          example: 8ef5685b-280d-4b01-8a02-97cd05fe1d35
        customer:
          type: string
          description: Customer ID
          example: 6a3ab8c2dea3bdc9bc037071
        provider:
          type: string
          example: uniswap
        status:
          type: string
          enum: *a2
          example: pending
        dex:
          type: string
          enum: *a3
          example: uniswapv3
        originChain:
          type: string
          example: ethereum
        destinationChain:
          type: string
          example: ethereum
        tokenIn:
          $ref: "#/components/schemas/PersistedQuoteTokenIn"
        tokenOut:
          $ref: "#/components/schemas/PersistedQuoteTokenOut"
        deadline:
          type: string
          format: date-time
          description: Quote expiration instant (ISO 8601 UTC)
          example: 2026-06-23T17:00:50.662Z
        slippage:
          type: number
          example: 0.01
        feeSponsorship:
          type: boolean
          example: false
        shouldChargeSwapFees:
          type: boolean
          example: true
        bitcoinDeposit:
          nullable: true
        depositAddress:
          type: string
          nullable: true
        originAddress:
          type: string
          nullable: true
          example: "0xb794F5eA0ba39494cE839613fffBA74279579268"
        destinationAddress:
          type: string
          nullable: true
          example: "0xb794F5eA0ba39494cE839613fffBA74279579268"
        originBlockNumber:
          type: string
          nullable: true
        destinationBlockNumber:
          type: string
          nullable: true
        originTxHash:
          type: string
          nullable: true
        destinationTxHash:
          type: string
          nullable: true
        originTxUrl:
          type: string
          nullable: true
        destinationTxUrl:
          type: string
          nullable: true
        confirmedAt:
          type: string
          nullable: true
          format: date-time
        fulfilledAt:
          type: string
          nullable: true
          format: date-time
        failureReason:
          type: string
          nullable: true
        refundReason:
          type: string
          nullable: true
        refundTxHash:
          type: string
          nullable: true
        refundTxUrl:
          type: string
          nullable: true
        rawQuote:
          type: string
          nullable: true
          description: Provider-specific serialized quote data
        fees:
          nullable: true
          description: Fee context persisted with the quote
        deletedAt:
          type: string
          nullable: true
          format: date-time
        createdAt:
          type: string
          format: date-time
          example: 2026-06-23T16:30:50.662Z
        updatedAt:
          type: string
          format: date-time
          example: 2026-06-23T16:30:50.662Z
        updates:
          type: array
          items:
            $ref: "#/components/schemas/QuoteUpdateEntry"
          description: Audit log of quote state transitions
      required:
        - _id
        - id
        - quoteId
        - customer
        - provider
        - status
        - originChain
        - destinationChain
        - tokenIn
        - tokenOut
        - deadline
        - feeSponsorship
        - shouldChargeSwapFees
        - depositAddress
        - originAddress
        - destinationAddress
        - originBlockNumber
        - destinationBlockNumber
        - originTxHash
        - destinationTxHash
        - originTxUrl
        - destinationTxUrl
        - confirmedAt
        - fulfilledAt
        - failureReason
        - refundReason
        - refundTxHash
        - refundTxUrl
        - rawQuote
        - createdAt
        - updatedAt
      description: "Full saved quote document returned by GET /v2/swap/status/{id}.
        Note: feeBreakdown is not returned on this endpoint."
    StrategyPnlPoint:
      type: object
      properties:
        dateString:
          type: string
          description: UTC calendar day for this point (YYYY-MM-DD).
          example: 2026-07-20
        sharesBalance:
          allOf:
            - $ref: "#/components/schemas/BigIntLike"
            - description: Receipt/share token balance held at end of day, raw units.
        assetValue:
          allOf:
            - $ref: "#/components/schemas/BigIntLike"
            - description: Position valued in the underlying asset at the day's rate, raw
                units.
        costBasisAsset:
          allOf:
            - $ref: "#/components/schemas/BigIntLike"
            - description: Remaining cost basis of the position in underlying-asset raw units
                (reduced proportionally on withdrawals).
        pnlAsset:
          allOf:
            - $ref: "#/components/schemas/BigIntLike"
            - description: Unrealized PnL in underlying-asset raw units (assetValue -
                costBasisAsset). May be negative.
              example: "1234"
        pnlAssetAfterFees:
          allOf:
            - $ref: "#/components/schemas/BigIntLike"
            - description: Unrealized PnL after Pods fees, in underlying-asset raw units. Uses
                the same `performanceFeeBps + markupFeeBps` haircut as
                `spotPosition.profit.afterFees`; losses are returned unchanged.
              example: "925"
        cumulativeDeposited:
          allOf:
            - $ref: "#/components/schemas/BigIntLike"
            - description: Cumulative underlying deposited up to and including this day, raw
                units.
        cumulativeWithdrawn:
          allOf:
            - $ref: "#/components/schemas/BigIntLike"
            - description: Cumulative underlying withdrawn up to and including this day, raw
                units.
        unrealizedPnlPercentage:
          type: number
          nullable: true
          description: pnlAsset / costBasisAsset expressed in percentage points (5 = 5%).
            Null when cost basis is zero.
          example: 1.23
        unrealizedPnlPercentageAfterFees:
          type: number
          nullable: true
          description: pnlAssetAfterFees / costBasisAsset expressed in percentage points
            (5 = 5%). Null when cost basis is zero.
          example: 0.925
      required:
        - dateString
        - sharesBalance
        - assetValue
        - costBasisAsset
        - pnlAsset
        - pnlAssetAfterFees
        - cumulativeDeposited
        - cumulativeWithdrawn
        - unrealizedPnlPercentage
        - unrealizedPnlPercentageAfterFees
      description: One daily PnL data point for the wallet, based on its deposit and
        withdrawal cost basis and the strategy's daily share price.
    StrategyPnlResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/StrategyPnlPoint"
          description: Daily PnL points ordered oldest to newest, for the requested page.
        pagination:
          $ref: "#/components/schemas/Pagination"
        strategyId:
          type: string
          description: Resolved strategy ObjectId.
          example: 665f1a2b3c4d5e6f70819293
        walletAddress:
          allOf:
            - $ref: "#/components/schemas/Address"
            - description: Wallet the PnL series is scoped to.
      required:
        - items
        - pagination
        - strategyId
        - walletAddress
      description: Paginated wallet PnL timeline for a strategy. The list key is
        `items` (not `data`).
    StrategyV2SpotPosition:
      allOf:
        - $ref: "#/components/schemas/SpotPositionApy"
        - type: object
          properties:
            realizedApy7d:
              type: number
              nullable: true
              description: Strategy-level realized yield over the trailing 7 days, annualized
                as a decimal ratio gross of performance fees.
            realizedApy30d:
              type: number
              nullable: true
              description: Strategy-level realized yield over the trailing 30 days, annualized
                as a decimal ratio gross of performance fees.
          required:
            - realizedApy7d
            - realizedApy30d
      description: Strategy APY metrics enriched with trailing realized APY
    WalletBranchStaleness:
      type: object
      properties:
        stalePositions:
          type: number
          description: How many positions in this branch were served from last-known-good
            snapshots.
        oldestAsOf:
          type: string
          format: date-time
          description: Capture time of the oldest snapshot served in this branch.
      required:
        - stalePositions
        - oldestAsOf
      description: Present when the branch was served from a last-known-good snapshot
        after a live read failure — including when the captured snapshot holds
        zero positions (stalePositions is 0 but the branch data is still stale).
    WalletBranchError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
          required:
            - code
            - message
      required:
        - error
      description: Returned in place of the branch data when the live read failed and
        no last-known-good snapshot exists. The HTTP status stays 200.
    WalletV2SpotPosition:
      allOf:
        - $ref: "#/components/schemas/WalletSpotPosition"
        - type: object
          properties:
            underlyingBalanceUSD:
              type: number
            realizedApy7d:
              type: number
              nullable: true
              description: Strategy-level realized yield over the trailing 7 days, annualized
                as a decimal ratio gross of performance fees.
            realizedApy30d:
              type: number
              nullable: true
              description: Strategy-level realized yield over the trailing 30 days, annualized
                as a decimal ratio gross of performance fees.
            stale:
              type: boolean
              description: Present (true) when the live read failed and this position was
                served from the last successfully captured snapshot.
            asOf:
              type: string
              format: date-time
              description: Capture time of the last-known-good snapshot this position was
                served from. Present only alongside `stale`.
          required:
            - apy
            - avgApy
            - grossAPY
            - inceptionApy
            - netAPY
            - cumulativeProfit
            - currentPosition
            - principal
            - profit
            - profitInUSD
            - underlyingBalanceUSD
      description: Wallet-scoped strategy position with balances, profit, and realized APY
    StrategyV2HistoryItem:
      type: object
      properties:
        amounts:
          type: object
          properties:
            assetIn:
              type: object
              nullable: true
              properties:
                amountRaw:
                  type: string
              required:
                - amountRaw
            assetOut:
              type: object
              nullable: true
              properties:
                amountRaw:
                  type: string
              required:
                - amountRaw
        id:
          type: string
        status:
          type: string
        transactions:
          type: array
          items:
            type: object
            properties: {}
        type:
          type: string
      required:
        - amounts
        - id
        - status
        - transactions
        - type
    StrategyV2HistoryResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/StrategyV2HistoryItem"
        pagination:
          $ref: "#/components/schemas/Pagination"
      required:
        - items
        - pagination
    StrategyPnlEmbeddedResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/StrategyPnlPoint"
        pagination:
          $ref: "#/components/schemas/Pagination"
      required:
        - items
        - pagination
      description: Paginated wallet PnL embedded in a strategy detail response
    StrategyRatePoint:
      type: object
      properties:
        date:
          type: string
          example: 2026-07-20
        rate:
          type: number
          nullable: true
        apy:
          type: number
          nullable: true
        conversionRateApy:
          type: number
          nullable: true
        timestamp:
          type: string
          format: date-time
      required:
        - date
        - rate
        - apy
        - conversionRateApy
        - timestamp
    StrategyRateHistoryEmbeddedResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/StrategyRatePoint"
        pagination:
          $ref: "#/components/schemas/Pagination"
      required:
        - items
        - pagination
      description: Paginated strategy rate history embedded in a detail response
    StrategyV2DetailResponse:
      type: object
      properties:
        strategy:
          $ref: "#/components/schemas/StrategyMetadata"
        spotPosition:
          $ref: "#/components/schemas/StrategyV2SpotPosition"
        rateHistory:
          $ref: "#/components/schemas/StrategyRateHistoryEmbeddedResponse"
      required:
        - strategy
        - spotPosition
        - rateHistory
      description: Enriched strategy detail with rate history
    WalletStrategyV2DetailResponse:
      type: object
      properties:
        history:
          $ref: "#/components/schemas/StrategyV2HistoryResponse"
        pnl:
          $ref: "#/components/schemas/StrategyPnlEmbeddedResponse"
        rateHistory:
          $ref: "#/components/schemas/StrategyRateHistoryEmbeddedResponse"
        spotPosition:
          allOf:
            - $ref: "#/components/schemas/WalletV2SpotPosition"
            - allOf:
                - $ref: "#/components/schemas/WalletSpotPosition"
                - type: object
                  nullable: true
                  properties:
                    underlyingBalanceUSD:
                      type: number
                    realizedApy7d:
                      type: number
                      nullable: true
                      description: Strategy-level realized yield over the trailing 7 days, annualized
                        as a decimal ratio gross of performance fees.
                    realizedApy30d:
                      type: number
                      nullable: true
                      description: Strategy-level realized yield over the trailing 30 days, annualized
                        as a decimal ratio gross of performance fees.
                    stale:
                      type: boolean
                      description: Present (true) when the live read failed and this position was
                        served from the last successfully captured snapshot.
                    asOf:
                      type: string
                      format: date-time
                      description: Capture time of the last-known-good snapshot this position was
                        served from. Present only alongside `stale`.
                  required:
                    - apy
                    - avgApy
                    - grossAPY
                    - inceptionApy
                    - netAPY
                    - cumulativeProfit
                    - currentPosition
                    - principal
                    - profit
                    - profitInUSD
                    - underlyingBalanceUSD
        strategy:
          $ref: "#/components/schemas/StrategyMetadata"
      required:
        - history
        - pnl
        - rateHistory
        - spotPosition
        - strategy
      description: Enriched strategy detail scoped to a customer-owned wallet
    StrategyRateHistoryResponse:
      allOf:
        - $ref: "#/components/schemas/StrategyRateHistoryEmbeddedResponse"
        - type: object
          properties:
            strategyId:
              type: string
          required:
            - strategyId
      description: Paginated daily strategy rate history
    WalletV2TokenPosition:
      type: object
      properties:
        address:
          type: string
        amount:
          type: string
        amountInUSD:
          type: string
        chainId:
          type: number
        decimals:
          type: number
        humanized:
          type: string
        id:
          type: string
        logoURI:
          type: string
          nullable: true
        name:
          type: string
        price:
          type: number
        symbol:
          type: string
        stale:
          type: boolean
          description: Present (true) when the live read failed and this position was
            served from the last successfully captured snapshot.
        asOf:
          type: string
          format: date-time
          description: Capture time of the last-known-good snapshot this position was
            served from. Present only alongside `stale`.
      required:
        - address
        - amount
        - amountInUSD
        - chainId
        - decimals
        - humanized
        - id
        - logoURI
        - name
        - price
        - symbol
    WalletV2HistoryItem:
      type: object
      properties:
        amounts:
          type: object
          properties:
            assetIn:
              type: object
              nullable: true
              properties: {}
            assetOut:
              type: object
              nullable: true
              properties: {}
            breakdown:
              type: array
              items:
                type: object
                properties: {}
          required:
            - assetIn
            - assetOut
        createdAt:
          type: string
          format: date-time
        fees:
          type: object
          properties: {}
        finishedAt:
          type: string
          nullable: true
          format: date-time
        id:
          type: string
        initialDate:
          type: string
          format: date-time
        originAddress:
          type: string
          nullable: true
        receiverAddress:
          type: string
          nullable: true
        status:
          type: string
        strategyId:
          type: string
          nullable: true
        strategySlug:
          type: string
          nullable: true
        transactions:
          type: array
          items:
            type: object
            properties: {}
        type:
          type: string
        underlyingAssetFeeValue:
          type: string
      required:
        - amounts
        - createdAt
        - finishedAt
        - id
        - initialDate
        - originAddress
        - receiverAddress
        - status
        - strategyId
        - strategySlug
        - transactions
        - type
        - underlyingAssetFeeValue
    WalletV2EarnPosition:
      type: object
      properties:
        investmentActions:
          type: array
          items:
            type: object
            properties: {}
          description: Raw investment actions used to calculate the position. Omitted when
            withActions=false.
        spotPosition:
          $ref: "#/components/schemas/WalletV2SpotPosition"
        strategy:
          $ref: "#/components/schemas/StrategyMetadata"
      required:
        - spotPosition
        - strategy
    WalletV2TokensResponse:
      anyOf:
        - type: object
          properties:
            positions:
              type: array
              items:
                $ref: "#/components/schemas/WalletV2TokenPosition"
            summary:
              type: object
              properties:
                totalAmountInUSD:
                  type: string
              required:
                - totalAmountInUSD
            staleness:
              $ref: "#/components/schemas/WalletBranchStaleness"
          required:
            - positions
            - summary
        - $ref: "#/components/schemas/WalletBranchError"
    WalletV2EarnSnapshot:
      type: object
      properties:
        state:
          type: string
          enum:
            - fresh
            - stale
          description: Whether the snapshot reflects every known action (fresh) or a
            refresh is pending (stale).
        computedAt:
          type: string
          nullable: true
          description: ISO 8601 instant at which the snapshot was projected.
        staleReason:
          type: string
          nullable: true
          description: Why the snapshot is stale. Null when fresh.
        deltaApplied:
          type: boolean
          description: True when actions newer than the snapshot were folded into the
            response.
        deltaReason:
          type: string
          nullable: true
          description: Why newer actions are not fully reflected in the response. Set when
            actions remain pending (including partially applied deltas) or when
            the delta computation failed. Null only when the computation
            completed with no pending actions.
        pendingActionCount:
          type: integer
          nullable: true
          description: Actions newer than the snapshot that are not yet reflected in the
            response. Null when the delta computation failed and the count is
            unknown.
      required:
        - state
        - computedAt
        - staleReason
        - deltaApplied
        - deltaReason
        - pendingActionCount
      description: Provenance of the earn branch when served from the wallet PnL
        snapshot read model. Omitted when computed live.
    WalletV2EarnResponse:
      anyOf:
        - type: object
          properties:
            positions:
              type: array
              items:
                $ref: "#/components/schemas/WalletV2EarnPosition"
            snapshot:
              $ref: "#/components/schemas/WalletV2EarnSnapshot"
            summary:
              type: object
              properties:
                totalProfitInUSD:
                  type: number
                totalUnderlyingBalanceUSD:
                  type: number
              required:
                - totalProfitInUSD
                - totalUnderlyingBalanceUSD
            staleness:
              $ref: "#/components/schemas/WalletBranchStaleness"
            errors:
              type: array
              items:
                type: object
                properties:
                  code:
                    type: string
                    nullable: true
                  message:
                    type: string
                  strategyId:
                    type: string
                    nullable: true
                required:
                  - code
                  - message
                  - strategyId
              description: Positions whose live read failed with no last-known-good snapshot.
                Each failed position is omitted from `positions` and its amounts
                are excluded from `summary`.
          required:
            - positions
            - summary
        - $ref: "#/components/schemas/WalletBranchError"
    WalletV2HistoryResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/WalletV2HistoryItem"
        pagination:
          $ref: "#/components/schemas/Pagination"
      required:
        - items
        - pagination
    WalletOverviewResponse:
      type: object
      properties:
        tokens:
          $ref: "#/components/schemas/WalletV2TokensResponse"
        earn:
          $ref: "#/components/schemas/WalletV2EarnResponse"
        history:
          allOf:
            - $ref: "#/components/schemas/WalletV2HistoryResponse"
            - nullable: true
              description: Paginated wallet activity. Omitted when includeHistory=false.
      description: Wallet token balances, earn positions, and transaction history
    OndoAssetTradability:
      type: object
      properties:
        symbol:
          type: string
          description: Ondo GM token symbol ending in lowercase `on`
          example: NVDAon
        tradable:
          type: boolean
          description: Whether Deframe considers the symbol safe to trade right now
          example: false
        limited:
          type: boolean
          description: True when an active ASSET_LIMITED restriction exists; trade is
            still allowed
          example: false
        blockingReason:
          type: object
          nullable: true
          properties:
            code:
              type: string
              example: ONDO_ASSET_PAUSED
            message:
              type: string
              example: "NVDAon trading is paused: earnings."
          required:
            - code
            - message
          description: Null when tradable; otherwise a concrete ONDO_* reason
        paused:
          type: boolean
          example: true
        status:
          nullable: true
          description: Matching asset pause/limit entry when present
        marketData:
          nullable: true
          description: Raw per-asset market data when available
      required:
        - symbol
        - tradable
        - limited
        - blockingReason
        - paused
      description: Per-symbol tradability nested under market status when `symbol` is
        provided
    OndoMarketStatus:
      type: object
      properties:
        isOpen:
          type: boolean
          example: true
        marketStatus:
          type: string
          example: regular
        nextOpen:
          type: string
        nextClose:
          type: string
        nextOpenSession:
          type: string
          example: premarket
        offhours:
          type: object
          properties:
            isOpen:
              type: boolean
              example: false
          required:
            - isOpen
        reason:
          type: object
          nullable: true
          properties:
            code:
              type: string
              example: MARKET_CLOSED
            message:
              type: string
              example: Weekend/Holiday
            documentation:
              type: string
          required:
            - code
            - message
        timestamp:
          type: string
          example: 2025-08-20T21:03:02Z
        asset:
          allOf:
            - $ref: "#/components/schemas/OndoAssetTradability"
            - nullable: true
              description: Null when `symbol` is omitted; otherwise the per-symbol tradability
                snapshot
      required:
        - isOpen
        - asset
      description: Ondo GM market-wide status plus optional nested asset tradability
  parameters: {}
paths:
  /api/v1/kyc/sumsub-share-token:
    post:
      tags:
        - KYC
      summary: Import Sumsub reusable KYC share token
      description: Submit a fresh Sumsub reusable KYC share token after Sumsub
        approves the applicant. Send the CPF whenever it is known; supply taxId
        instead when the applicant was verified with a document that carries no
        CPF, such as a passport. Pods validates the identifier, stores only a
        salted hash plus the last four digits, and revalidates any unique
        approved Avenia identity already associated with it. An approved match
        is reused in a new customer-scoped profile without consuming the fresh
        token. Otherwise, Pods creates or reuses the customer profile and Avenia
        subaccount, imports the token into Avenia, and returns a Pods-generated
        kycUserId. A submission without a CPF cannot be matched against
        identities approved under one, so it may be rejected downstream by
        Avenia as a duplicate tax id.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubmitSumsubShareTokenRequest"
      responses:
        "200":
          description: KYC token import submitted or approved Avenia identity reused
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubmitSumsubShareTokenResponse"
        "401":
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: CPF conflicts with the existing customer and Sumsub applicant profile
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: Avenia import failed or returned an invalid response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/kyc/foreign/sumsub-share-token:
    post:
      tags:
        - KYC
      summary: Import foreign Sumsub reusable KYC share token
      description: Submit a fresh Sumsub reusable KYC share token for a non-Brazil
        applicant after Sumsub approves the verification. Pods validates the
        foreign tax id, stores only a salted hash plus the last four digits,
        creates or reuses the customer profile and Avenia subaccount, imports
        the token into Avenia, and returns a Pods-generated kycUserId. Poll GET
        /api/v1/kyc/status until status is approved, then call POST
        /api/v1/kyc/currency-unlock to enable USD ramps.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubmitForeignSumsubShareTokenRequest"
      responses:
        "200":
          description: Foreign KYC token import submitted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubmitSumsubShareTokenResponse"
        "401":
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Tax id conflicts with an existing customer profile
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: Avenia import failed or returned an invalid response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/kyc/currency-unlock:
    post:
      tags:
        - KYC
      summary: Unlock foreign currency capability
      description: After Avenia approves a foreign KYC profile, submit the currency
        unlock request for USD. Pods forwards the unlock to Avenia, refreshes
        account metadata, and returns the normalized KYC status including
        usdEnabled. Idempotent when the capability is already enabled.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubmitCurrencyKycUnlockRequest"
      responses:
        "200":
          description: Currency unlock submitted or already enabled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KycStatusResponse"
        "401":
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: KYC profile not approved for this currency
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: KYC profile not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: Avenia currency unlock failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/kyc/bigdatacorp/sessions:
    post:
      tags:
        - KYC
      summary: Create BigDataCorp KYC iframe session
      description: Starts the BigDataCorp document and liveness capture flow. Pods
        creates a KYC profile with provider bigdatacorp, stores only safe
        references and CPF hash/last4, and returns the iframe URL plus the
        Pods-generated kycUserId. Requests reuse an active capture for up to
        seven minutes unless restartExistingSession is true, which creates a
        fresh iframe session in the same KYC profile. If the verified CPF maps
        to exactly one approved local BigDataCorp KYC profile or approved Avenia
        identity, Pods creates a customer-scoped approved profile from the
        existing Avenia IDs without starting a new capture. The response
        provides a safe completion URL and requiresWebView false for mobile
        compatibility. Otherwise, Pods can perform a bounded Avenia lookup while
        the CPF is present in this request.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StartBigDataCorpSessionRequest"
      responses:
        "200":
          description: BigDataCorp iframe session created, reused, or resolved as an
            approved reusable identity
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StartBigDataCorpSessionResponse"
        "401":
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/kyc/bigdatacorp/submit:
    post:
      tags:
        - KYC
      summary: Submit completed BigDataCorp KYC to Avenia
      description: Finalizes a completed BigDataCorp capture by fetching the provider
        images and identity data on demand, forwarding them to Avenia, and
        moving the Pods KYC profile to provider_pending when Avenia accepts the
        submit. If an exact approved Avenia identity is already associated with
        this profile, Pods returns approved and enables BRLA without submitting
        duplicate documents. Repeated requests while the Avenia submission is in
        progress return the existing provider_pending state and do not duplicate
        document uploads. A durable Pods background task continues to refresh
        the Avenia attempt after the response. If Avenia rejects the final
        submit before a normal pending attempt is created, Pods persists the
        provider failure on the KYC profile as rejected_retryable so GET
        /api/v1/kyc/status can return providerResult and providerMessage. Pods
        does not persist full CPF, address, raw images, or base64 payloads.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubmitBigDataCorpKycRequest"
      responses:
        "200":
          description: BigDataCorp KYC submitted to Avenia
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubmitBigDataCorpKycResponse"
        "400":
          description: Avenia rejected the final submit. The KYC profile is persisted as
            rejected_retryable and the status endpoint exposes
            providerResult/providerMessage.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: BigDataCorp capture is incomplete, under age, or the profile uses
            another provider. For BIGDATACORP_CAPTURE_INCOMPLETE, inspect
            error.details.providerResult to decide whether capture, provider
            review, or identity data is still missing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: Avenia submit failed or returned an invalid response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/kyc/sessions:
    post:
      tags:
        - KYC
      summary: Open a KYC session for externally captured evidence
      description: "Opens a verification for an applicant whose document and liveness
        capture you run yourself. Pods creates a KYC profile with provider
        external_evidence, stores only the CPF hash and last four digits, and
        returns the kycUserId used by the submit and status endpoints. The call
        is idempotent per customer and tax ID: a repeated request returns the
        same session with existing true. Reusing a tax ID under a different
        email is refused, because binding one person to two records is far more
        often a mistake than an intent."
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateKycSessionRequest"
      responses:
        "200":
          description: Session created, or the existing session for this tax ID returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateKycSessionResponse"
        "401":
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: KYC_PROFILE_EMAIL_MISMATCH - this tax ID already has a session
            under a different email
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid payload, including a malformed CPF
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/kyc/sessions/{kycUserId}/submit:
    post:
      tags:
        - KYC
      summary: Submit externally captured KYC evidence
      description: "Submits every piece of evidence for one applicant in a single
        request: the liveness selfie, the document images, and the attestations
        of the checks you ran. Pods validates the attestations, streams the
        images to the ramp provider without persisting them, and runs the
        verification within this request — so the response already carries the
        provider verdict, including a rejection. Everything in this payload
        except the images is kept for up to 7 days and then deleted
        automatically, so a failure on the provider side can be retried without
        sending the applicant back through capture. Images are never retained.
        Expect roughly 20 to 30 seconds; the image upload dominates. Set a
        client timeout of at least 60 seconds. The request is safe to repeat:
        while a submission is in flight, or once one has completed, Pods returns
        the current state instead of uploading the documents a second time. The
        one exception is a session in rejected_retryable, where a repeated
        request is a full new attempt: Pods re-uploads all three images and asks
        the ramp provider for a fresh verdict, reusing the record it already
        created for this person rather than opening a second one, and the
        session returns to provider_pending. Sessions in rejected, blocked or
        approved answer with the state they are already in. Rejections arrive as
        a normalized reason object on this response and on GET
        /api/v1/kyc/status; read reason.retryable to decide whether to send the
        applicant back through capture."
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Pods-generated KYC user id returned by POST /api/v1/kyc/sessions
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          name: kycUserId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubmitKycEvidenceRequest"
      responses:
        "200":
          description: Evidence accepted by the ramp provider. status is provider_pending
            while the verdict is pending, or approved when an already-verified
            identity was matched.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubmitKycEvidenceResponse"
        "400":
          description: The ramp provider rejected the submission. The profile is persisted
            as rejected or rejected_retryable and GET /api/v1/kyc/status returns
            the normalized reason.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: No KYC session with this kycUserId in this customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: IDENTITY_CPF_MISMATCH - the document belongs to a different tax ID
            than the session; UNDER_18 - the applicant is under 18;
            INVALID_KYC_PROVIDER - this session was not opened for external
            evidence.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: LIVENESS_NOT_SUCCEEDED, LIVENESS_CONFIDENCE_MISSING or
            LIVENESS_BELOW_THRESHOLD - the attested liveness did not pass;
            INVALID_EVIDENCE_IMAGE or UNSUPPORTED_EVIDENCE_IMAGE_FORMAT - an
            image is malformed, over 8MB, or not a JPEG/PNG, on either
            transport; DOCUMENT_BACK_REQUIRED or DOCUMENT_BACK_NOT_ALLOWED - the
            back image does not match the document type; INVALID_BIRTH_DATE;
            EVIDENCE_URL_NOT_ALLOWED - a url is not https, its host is not
            allowlisted, or it resolves to a private address;
            EVIDENCE_URL_EXPIRED - the pre-signed signature had already expired,
            re-sign and submit again; EVIDENCE_URL_FORBIDDEN or
            EVIDENCE_URL_NOT_FOUND - the storage provider refused the request or
            has no such object; EVIDENCE_URL_UNREACHABLE - the storage provider
            could not be reached; EVIDENCE_URL_TIMEOUT - fetching the images
            exceeded the time budget.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: The ramp provider failed or returned an invalid response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/kyc/status:
    get:
      tags:
        - KYC
      summary: Get KYC status
      description: "Returns the normalized Pods KYC status for a Pods-generated
        kycUserId. This is the canonical status endpoint for every KYC flow:
        Brazilian Sumsub share-token profiles, foreign Sumsub share-token
        profiles, BigDataCorp iframe profiles, and external evidence profiles.
        BigDataCorp profiles are polled while capture is pending; pending KYC
        attempts are refreshed before returning when possible. Pods also runs a
        durable background task to update the stored KYC state when clients stop
        polling. When a profile is rejected, read the reason object rather than
        rejectReason or providerResult — it is the only field guaranteed to stay
        stable across providers. For foreign USD flows, poll until status is
        approved and usdEnabled is true after currency unlock."
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Pods-generated KYC user id returned by
              /api/v1/kyc/sumsub-share-token,
              /api/v1/kyc/foreign/sumsub-share-token,
              /api/v1/kyc/bigdatacorp/sessions or /api/v1/kyc/sessions
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          name: kycUserId
          in: query
      responses:
        "200":
          description: KYC status retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KycStatusResponse"
        "401":
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: KYC profile not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid query
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/limits:
    get:
      tags:
        - KYC
      summary: Get fiat ramp limits
      description: Returns both on-ramp and off-ramp limits in one request. Each
        direction contains the effective remaining amount in the requested
        currency after applying both the matching local cap and the global USD
        cap, plus the remaining global cap in USD. The top-level remaining
        fields are deprecated aliases for the off-ramp values.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            pattern: ^[A-Z]{3}$
            description: Three-letter ISO currency code used for local limit values
            example: BRL
          required: true
          name: currency
          in: query
        - schema:
            type: string
            description: Wallet address linked to the approved KYC profile
            example: "0x0000000000000000000000000000000000000001"
          required: true
          name: originAddress
          in: query
      responses:
        "200":
          description: Directional ramp limits retrieved, or no applicable provider found
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/RampLimitsResponse"
                  - $ref: "#/components/schemas/NoRampLimitProviderResponse"
                example:
                  provider: avenia
                  currency: BRL
                  remaining: "5500"
                  remainingGlobalUsd: "1100"
                  onramp:
                    remaining: "6000"
                    remainingGlobalUsd: "1600"
                  offramp:
                    remaining: "5500"
                    remainingGlobalUsd: "1100"
                  blocked: false
        "401":
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid query
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /health:
    get:
      tags:
        - Health
      summary: API health check
      description: Check if the API is running
      responses:
        "200":
          description: API is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - ok
                required:
                  - status
                example:
                  status: ok
  /health/{strategyId}:
    get:
      tags:
        - Health
      summary: Strategy health check
      description: Test a specific strategy by simulating transactions
      parameters:
        - schema:
            type: string
            description: Strategy ID or slug
          required: true
          name: strategyId
          in: path
        - schema:
            type: string
            default: Picnic
            description: Customer name
          required: false
          name: customer
          in: query
      responses:
        "200":
          description: Strategy health check result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthStrategyResponse"
        "404":
          description: Strategy not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Strategy health check failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/swap/quote:
    get:
      tags:
        - Swap
      summary: Get swap quote
      description: Returns a persisted quote for swapping tokens on same or different
        chains, including Pods Ramp fiat rails (`originChain` or
        `destinationChain` of `fiat`). Brazil Pix supports Pix↔USDC on
        `base`/`monad`, Pix↔BRLA on `polygon`, Pix↔BRS on `solana`, and
        Pix↔Morpho hyperUSDCa on `monad`. Crypto BRLA↔BRS inventory rails (no
        Avenia/fiat leg) are supported on both `polygon` and `base`. Foreign USD
        supports WIRE/ACH↔USDC on `ethereum`, `base`, and `polygon` (Avenia
        direct delivery; see Ramp guides). Provide either `originChain` +
        `destinationChain` or `chainIdIn` + `chainIdOut`. Provide exactly one of
        `amountIn` or `amountOut`. When `originAddress` is provided with
        `destinationAddress` (crypto / fiat onramp), `pixKey` (Pix offramp), or
        flat USD bank account fields (USD offramp), the response also includes
        executable fields (`transactionData`, `paymentInstructions`, `id`) —
        preferred for new integrations instead of `POST /v2/swap/bytecode`.
        Optional `usdPaymentMethod` selects WIRE (default) or ACH on USD rails.
        Quotes expire after 5 minutes; `quote.deadline` is a Unix timestamp in
        seconds and `quote.deadlineDate` is an ISO 8601 string (UTC).
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Origin blockchain network name. Use with destinationChain.
            example: ethereum
          required: false
          name: originChain
          in: query
        - schema:
            type: number
            description: Origin chain ID. Alternative to originChain.
            example: 1
          required: false
          name: chainIdIn
          in: query
        - schema:
            type: string
            description: Destination blockchain network name. Use with originChain.
            example: polygon
          required: false
          name: destinationChain
          in: query
        - schema:
            type: number
            description: Destination chain ID. Alternative to destinationChain.
            example: 137
          required: false
          name: chainIdOut
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Input token contract address
          required: true
          name: tokenIn
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Output token contract address
          required: true
          name: tokenOut
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/BigIntLike"
              - description: Input amount (in token smallest unit). Provide either amountIn or
                  amountOut, not both.
          required: false
          name: amountIn
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/BigIntLike"
              - description: Desired output amount (in token smallest unit). Provide either
                  amountIn or amountOut, not both.
          required: false
          name: amountOut
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/WalletAddress"
              - description: Recipient on the destination chain (checksummed EVM or Solana
                  public key). Omit for quote-only requests; use pixKey instead
                  for fiat destinations.
          required: false
          name: destinationAddress
          in: query
        - schema:
            type: string
            description: PIX key for BRL fiat off-ramp destinations. Use instead of
              destinationAddress when destinationChain is fiat and tokenOut is
              BRL.
            example: user@bank.com
          required: false
          name: pixKey
          in: query
        - schema:
            type: string
            description: USD beneficiary alias. Defaults to bankBeneficiaryName when
              omitted. Used with bank account fields to resolve-or-create the
              Avenia beneficiary on quote.
            example: Payroll ACH
          required: false
          name: alias
          in: query
        - schema:
            type: string
            description: USD bank account number for USDC→USD offramp. Required with the
              other bank fields for USD offramp destinations.
            example: "123456789"
          required: false
          name: bankAccountNumber
          in: query
        - schema:
            type: string
            description: USD ABA routing number for USDC→USD offramp.
            example: "021000021"
          required: false
          name: bankRoutingNumber
          in: query
        - schema:
            type: string
            description: Legal name on the USD bank account. ASCII only — Avenia
              rejects accents (ã, é, …) with bankBeneficiaryName is invalid.
            example: John Smith
          required: false
          name: bankBeneficiaryName
          in: query
        - schema:
            type: string
            description: Bank name for the USD beneficiary.
            example: Chase
          required: false
          name: bankName
          in: query
        - schema:
            type: string
            description: Beneficiary street address line 1.
            example: 1 Main St
          required: false
          name: beneficiaryStreetLine1
          in: query
        - schema:
            type: string
            description: Beneficiary street address line 2 (optional).
          required: false
          name: beneficiaryStreetLine2
          in: query
        - schema:
            type: string
            description: Beneficiary city.
            example: New York
          required: false
          name: beneficiaryCity
          in: query
        - schema:
            type: string
            description: Beneficiary state / province.
            example: NY
          required: false
          name: beneficiaryState
          in: query
        - schema:
            type: string
            description: Beneficiary postal code.
            example: "10001"
          required: false
          name: beneficiaryPostalCode
          in: query
        - schema:
            type: string
            description: Beneficiary country (ISO-style code as accepted by Avenia).
            example: USA
          required: false
          name: beneficiaryCountry
          in: query
        - schema:
            type: string
            description: Optional Avenia beneficiary description.
          required: false
          name: description
          in: query
        - schema:
            type: string
            enum:
              - ACH
              - WIRE
            description: USD payment rail for foreign USD onramp/offramp. Defaults to WIRE
              when omitted.
            example: WIRE
          required: false
          name: usdPaymentMethod
          in: query
        - schema:
            type: boolean
            default: false
            description: Set to true for PIX off-ramp quotes paid to a third party. The
              value must match the bytecode request.
            example: false
          required: false
          name: thirdParty
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/WalletAddress"
              - description: Sender address on the origin chain (checksummed EVM or Solana
                  public key). When provided with destinationAddress, pixKey, or
                  USD bank account fields, the response also includes executable
                  payload fields.
          required: false
          name: originAddress
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Address for refunds if swap fails
          required: false
          name: refundAddress
          in: query
        - schema:
            type: string
            enum:
              - bytecode
              - userOperation
              - fireblocks
              - instructions
            default: bytecode
            description: Response format when both originAddress and destinationAddress are
              provided. Defaults to `bytecode`. `userOperation` and `fireblocks`
              are EVM-origin only. `instructions` is Solana-origin only.
          required: false
          name: output
          in: query
        - schema:
            type: string
            description: Fireblocks vault account ID. Required when output=fireblocks.
            example: "12"
          required: false
          name: accountId
          in: query
        - schema:
            type: boolean
            description: Request fee sponsorship on eligible Pods bridge routes
            example: false
          required: false
          name: feeSponsorship
          in: query
        - schema:
            type: string
            description: Registered provider name to force when eligible for the route
            example: relay
          required: false
          name: preferredProvider
          in: query
        - schema:
            type: string
            description: Slippage tolerance as a fraction of 1. Valid range is 0.0001 to
              0.5.
            example: "0.01"
          required: false
          name: slippage
          in: query
        - schema:
            type: string
            enum:
              - fast
              - standard
            description: Transfer speed for CCTP cross-chain swaps
            example: fast
          required: false
          name: transferSpeed
          in: query
        - schema:
            type: string
            format: uri
            description: Optional per-quote webhook endpoint. When set, quote lifecycle
              webhooks are delivered to this URL instead of the customer default
              webhookURL.
            example: https://example.com/webhooks/swap-quote
          required: false
          name: webhookURL
          in: query
      responses:
        "200":
          description: Quote generated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  quote:
                    $ref: "#/components/schemas/Quote"
                  id:
                    type: string
                    description: Action ID for execution tracking when bytecode is included
                  chainId:
                    type: number
                    description: Origin chain ID when bytecode is included
                  transactionData:
                    anyOf:
                      - type: array
                        items:
                          $ref: "#/components/schemas/TransactionData"
                      - $ref: "#/components/schemas/SolanaTransactionData"
                    description: Executable transactions when output=bytecode (default) and both
                      addresses are provided
                  userOperation:
                    nullable: true
                    description: ERC-4337 user operation when output=userOperation and both
                      addresses are provided
                  instructions:
                    type: array
                    items:
                      nullable: true
                    description: Solana instructions when output=instructions (Solana-origin only)
                  lutsByAddress:
                    nullable: true
                    description: Solana lookup tables keyed by address when output=instructions
                required:
                  - quote
        "400":
          description: Bad request - invalid parameters or no route found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized - invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Origin wallet does not belong to the authenticated customer when
            executable output is requested
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/swap/bytecode:
    post:
      tags:
        - Swap
      summary: Generate transaction bytecode
      description: Generate executable transaction bytecode from a quote. The quote
        must not be expired.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                quoteId:
                  type: string
                  description: Quote ID from /v2/swap/quote
                  example: 550e8400-e29b-41d4-a716-446655440000
                accountId:
                  type: string
                  description: Fireblocks vault account ID. Required when output=fireblocks.
                  example: "12"
                originAddress:
                  allOf:
                    - $ref: "#/components/schemas/WalletAddress"
                    - description: Address executing the swap on the origin chain (checksummed EVM or
                        Solana public key)
                destinationAddress:
                  allOf:
                    - $ref: "#/components/schemas/WalletAddress"
                    - description: Address receiving tokens on the destination chain (checksummed EVM
                        or Solana public key). Required for onramps unless
                        pixKey or USD bank account fields are provided for fiat
                        offramps.
                output:
                  type: string
                  enum:
                    - bytecode
                    - userOperation
                    - fireblocks
                    - instructions
                  default: bytecode
                  description: Response format. Fireblocks output requires accountId.
                    `instructions` is Solana-origin only.
                pixKey:
                  type: string
                  description: PIX key for BRL fiat off-ramp routes. Use instead of
                    destinationAddress.
                  example: user@bank.com
                bankAccountNumber:
                  type: string
                  description: USD bank account number for USDC→USD offramp.
                bankRoutingNumber:
                  type: string
                  description: USD ABA routing number for USDC→USD offramp.
                bankBeneficiaryName:
                  type: string
                  description: Legal name on the USD bank account. ASCII only — Avenia
                    rejects accents.
                bankName:
                  type: string
                  description: Bank name for the USD beneficiary.
                beneficiaryStreetLine1:
                  type: string
                  description: Beneficiary street address line 1.
                beneficiaryStreetLine2:
                  type: string
                  description: Beneficiary street address line 2 (optional).
                beneficiaryCity:
                  type: string
                  description: Beneficiary city.
                beneficiaryState:
                  type: string
                  description: Beneficiary state / province.
                beneficiaryPostalCode:
                  type: string
                  description: Beneficiary postal code.
                beneficiaryCountry:
                  type: string
                  description: Beneficiary country.
                alias:
                  type: string
                  description: USD beneficiary alias (defaults to bankBeneficiaryName).
                description:
                  type: string
                  description: Optional Avenia beneficiary description.
                usdPaymentMethod:
                  type: string
                  enum:
                    - ACH
                    - WIRE
                  description: USD payment rail. Must match the quote when present.
                  example: WIRE
                rawQuote:
                  nullable: true
                  description: Provider-specific raw quote data from the quote response. Pass this
                    when the quote includes it.
                thirdParty:
                  type: boolean
                  default: false
                  description: Set to true for third-party PIX payouts. It must match the value
                    used for the quote.
                webhookURL:
                  type: string
                  format: uri
                  description: Optional per-quote webhook endpoint override. When set, it updates
                    the quote webhookURL and is copied to the created action.
                  example: https://example.com/webhooks/swap-action
              required:
                - quoteId
      responses:
        "200":
          description: Bytecode generated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    description: ID created for execution tracking
                    example: 65f1a2b3c4d5e6f789012345
                  chainId:
                    type: number
                    example: 1
                  transactionData:
                    anyOf:
                      - type: array
                        items:
                          $ref: "#/components/schemas/TransactionData"
                      - $ref: "#/components/schemas/SolanaTransactionData"
                    description: EVM routes return an array of transactions. Solana routes return a
                      transaction object with rawTransaction.
                  instructions:
                    type: array
                    items:
                      nullable: true
                    description: Solana instructions when output=instructions (Solana-origin only)
                  lutsByAddress:
                    nullable: true
                    description: Solana lookup tables keyed by address when output=instructions
                required:
                  - id
                  - chainId
                  - transactionData
        "400":
          description: Bad request - quote expired or invalid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Origin wallet does not belong to the authenticated customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Quote not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/swap/status/{id}:
    get:
      tags:
        - Swap
      summary: Get swap status
      description: Check the status of a saved swap quote by quote ID
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Quote ID
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Bitcoin transaction hash for Bitcoin deposit routes
            example: b4f1...
          required: false
          name: btcTxHash
          in: query
      responses:
        "200":
          description: Status retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SwapStatusDocument"
        "404":
          description: Transaction not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/swap/status/{id}/update/{txHash}:
    post:
      tags:
        - Swap
      summary: Update swap status with a transaction hash
      description: Submit a mined EVM transaction hash for a saved swap quote. The
        transaction is verified on-chain. Deposit-address quotes are replayed
        through the swap payment validator synchronously and are only confirmed
        if payment validation succeeds.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Quote ID
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          name: id
          in: path
        - schema:
            type: string
            pattern: ^0x[a-fA-F0-9]{64}$
            description: Hash of the mined transaction to validate
            example: "0x61160691804e39f80871573195a87d3be519129ed6c2c73fd3338b5f00f45ee1"
          required: true
          name: txHash
          in: path
      responses:
        "200":
          description: Status updated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - pending
                      - confirmed
                      - fulfilled
                      - expired
                      - failed
                      - refunded
                      - processing
                    description: Quote status after validation
                    example: confirmed
                  originTxHash:
                    type: string
                    example: "0x61160691804e39f80871573195a87d3be519129ed6c2c73fd3338b5f00f45ee1"
                  originTxUrl:
                    type: string
                    example: https://polygonscan.com/tx/0x...
                  destinationTxHash:
                    type: string
                    example: 0x...
                  destinationTxUrl:
                    type: string
                    example: https://gnosisscan.io/tx/0x...
                required:
                  - status
        "403":
          description: Quote belongs to another customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Quote or transaction not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Transaction not mined, reverted, unsupported, or did not pass
            payment validation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/swap/{id}/fulfill:
    get:
      tags:
        - Swap
      summary: Fulfill a swap quote by transaction hash
      description: Report a mined transaction for a quote so the API processes it
        immediately instead of waiting for indexer webhooks. The transaction is
        verified on-chain and replayed through the same detection pipelines a
        real indexer event would take. Processing is asynchronous; poll GET
        /v2/swap/status/{id} for the outcome. Safe to retry; resubmissions are
        idempotent.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Quote ID
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          name: id
          in: path
        - schema:
            type: string
            pattern: ^(0x[a-fA-F0-9]{64}|[1-9A-HJ-NP-Za-km-z]{80,90})$
            description: Hash of the mined EVM transaction or Solana transaction signature
              to replay
            example: "0x61160691804e39f80871573195a87d3be519129ed6c2c73fd3338b5f00f45ee1"
          required: true
          name: txHash
          in: query
        - schema:
            type: string
            description: Optional chain override for the transaction. Defaults to the
              quote's origin chain; pass it to replay a leg that lives on a
              different chain (e.g. a crosschain destination tx).
            example: base
          required: false
          name: chain
          in: query
      responses:
        "200":
          description: Transaction accepted for processing
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: accepted
                required:
                  - status
        "403":
          description: Quote belongs to another customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Quote or transaction not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Transaction not mined yet, reverted, or chain unsupported
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /actions:
    get:
      tags:
        - Tracking
      summary: List tracked operations
      description: Lists tracked swap, transfer, and strategy operations for the
        authenticated customer. Supply walletAddress to retrieve activity for a
        specific wallet.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Filter by wallet address
          required: false
          name: walletAddress
          in: query
        - schema:
            type: string
            enum:
              - CROSSCHAIN_SWAP
              - CROSSCHAIN_INVESTMENT_DEPOSIT
              - CROSSCHAIN_INVESTMENT_WITHDRAW
              - SAMECHAIN_SWAP
              - SAMECHAIN_INVESTMENT_DEPOSIT
              - SAMECHAIN_INVESTMENT_WITHDRAW
              - ON_RAMP_FIAT
              - OFF_RAMP_FIAT
              - TRANSFER_IN
              - TRANSFER_OUT
              - RECURRING_FEE_COLLECTION
            description: Filter by operation type
          required: false
          name: type
          in: query
        - schema:
            type: string
            enum:
              - INITIAL
              - PENDING
              - FAILED
              - REFUND_REQUIRED
              - REFUNDED
              - SUCCESS
              - EXPIRED
            description: Filter by operation status
          required: false
          name: status
          in: query
        - schema:
            type: string
            description: Filter by numeric chain ID
            example: "8453"
          required: false
          name: chainId
          in: query
        - schema:
            type: string
            description: Filter by origin chain name
            example: base
          required: false
          name: originChain
          in: query
        - schema:
            type: string
            default: "1"
            description: Page number
          required: false
          name: page
          in: query
        - schema:
            type: string
            default: "20"
            description: Results per page
          required: false
          name: limit
          in: query
        - schema:
            type: string
            default: createdAt
            description: Field to sort by
          required: false
          name: sortBy
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
            description: Sort direction
          required: false
          name: sortOrder
          in: query
      responses:
        "200":
          description: Tracked operations retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/TrackingSummary"
                  pagination:
                    $ref: "#/components/schemas/Pagination"
                required:
                  - data
                  - pagination
        "401":
          description: Unauthorized - invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /actions/{id}:
    get:
      tags:
        - Tracking
      summary: Get tracked operation
      description: Get formatted execution status by ID. Cross-chain swaps and
        cross-chain strategy bytecode responses return the `id` for this
        endpoint.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: ID
            example: 65f1a2b3c4d5e6f789012345
          required: true
          name: id
          in: path
      responses:
        "200":
          description: Tracked operation retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TrackingDetail"
        "404":
          description: Tracked operation not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /actions/{id}/cancel:
    get:
      tags:
        - Tracking
      summary: Cancel an unfilled client-limit investment action
      description: Cancels a funded, unfilled Ondo BSC client-limit order
        (`clientLimitPriceUsd > 0`) and refunds the single-use wallet.
        ExpireAt-only market orders are not cancellable — they expire and refund
        at `orderValidTo`. Poll `GET /actions/{id}` until `REFUNDED`. The cancel
        reason `ONDO_CLIENT_LIMIT_CANCELLED` is stored on the settlement step
        (`steps[].failureReason`).
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - schema:
            type: string
            description: Action ID returned by strategy bytecode
            example: 65f1a2b3c4d5e6f789012345
          required: true
          name: id
          in: path
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Requesting wallet. Must match the action walletAddress.
          required: true
          name: wallet
          in: query
      responses:
        "200":
          description: Action cancelled and refunded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TrackingDetail"
        "403":
          description: wallet does not match the action requesting wallet
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Action cannot be cancelled (unsupported, not funded, already
            filled, or not open)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /strategies:
    get:
      tags:
        - Strategies
      summary: List strategies
      description: Get the paginated catalog of available DeFi yield strategies for
        the authenticated customer.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            default: "1"
            description: Page number
          required: false
          name: page
          in: query
        - schema:
            type: string
            default: "100"
            description: Results per page
          required: false
          name: limit
          in: query
        - schema:
            type: string
            enum:
              - BASIC
              - FULL
            default: FULL
          required: false
          name: details
          in: query
        - schema:
            type: string
            description: Filter by network slug
          required: false
          name: network
          in: query
        - schema:
            type: string
            description: Filter by protocol name
          required: false
          name: protocol
          in: query
        - schema:
            type: string
            description: Filter by category slug
          required: false
          name: category
          in: query
      responses:
        "200":
          description: Strategies retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/StrategyListItem"
                  pagination:
                    $ref: "#/components/schemas/Pagination"
                required:
                  - data
                  - pagination
  /strategies/{strategyId}/bytecode:
    get:
      tags:
        - Strategies
      summary: Generate strategy transaction bytecode
      description: Generate transactions for strategy operations. Supports same-chain
        deposits and withdrawals, swaps before deposits, cross-chain operations,
        Fireblocks output, user operations, and Solana instructions where
        supported.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Strategy ID or slug
          required: true
          name: strategyId
          in: path
        - schema:
            type: string
            enum:
              - lend
              - request-lend
              - buy
              - withdraw
              - request-withdraw
              - sell
              - quick-withdraw
              - claim-airdrop
              - borrow
              - repay
            description: Strategy action type. buy and sell are aliases of request-lend and request-withdraw on Ondo stock strategies.
          required: true
          name: action
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/BigIntLike"
              - description: Amount in token smallest unit. Omit when amountInShares supplies
                  the withdrawal amount.
          required: false
          name: amount
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/BigIntLike"
              - description: Withdrawal amount in strategy share-token units.
          required: false
          name: amountInShares
          in: query
        - schema:
            type: boolean
            default: false
            description: Redeem the complete wallet share balance for supported ERC-4626
              strategies. Use for an exact full exit; amount remains required as
              the expected underlying amount.
          required: false
          name: withdrawAll
          in: query
        - schema:
            type: boolean
            default: false
            description: Skip on-chain wallet balance checks while generating bytecode (fee
              transfers, native transfers, and Aave lend). The returned
              transactions can still revert at execution if the wallet cannot
              fund them.
          required: false
          name: skipBalanceCheck
          in: query
        - schema:
            type: string
            description: User wallet address
          required: true
          name: wallet
          in: query
        - schema:
            type: number
            description: Source chain ID alias for cross-chain deposits
            example: 8453
          required: false
          name: chainIdIn
          in: query
        - schema:
            type: number
            description: Source chain ID for cross-chain deposits
            example: 8453
          required: false
          name: fromChainId
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Source token for same-chain swap or cross-chain deposit
          required: false
          name: fromTokenAddress
          in: query
        - schema:
            type: number
            description: Destination chain ID alias for cross-chain withdrawals
            example: 8453
          required: false
          name: chainIdOut
          in: query
        - schema:
            type: number
            description: Destination chain ID for cross-chain withdrawals
            example: 8453
          required: false
          name: toChainId
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Destination token for cross-chain withdrawals
          required: false
          name: toTokenAddress
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Destination wallet address when different from wallet
          required: false
          name: destinationAddress
          in: query
        - schema:
            type: string
            description: Registered swap provider name to use for any swap or bridge leg.
            example: pods
          required: false
          name: preferredProvider
          in: query
        - schema:
            type: string
            enum:
              - bytecode
              - userOperation
              - fireblocks
              - instructions
            default: bytecode
            description: Response format
          required: false
          name: output
          in: query
        - schema:
            type: string
            description: Fireblocks vault account ID. Required when output=fireblocks.
            example: "12"
          required: false
          name: accountId
          in: query
        - schema:
            type: string
            description: Solana reserve address for supported borrow/repay operations
          required: false
          name: reserveAddress
          in: query
        - schema:
            type: string
            format: uri
            description: Optional per-action webhook endpoint override.
            example: https://example.com/webhooks/strategy-action
          required: false
          name: webhookURL
          in: query
        - schema:
            anyOf:
              - type: string
              - type: number
            description: Ondo BSC CoW request-lend / request-withdraw only. Client limit
              price as integer USD cents (17050 = $170.50). Independent of
              expireAt. Other strategies return 400 PRICE_IN_USD_NOT_SUPPORTED.
            example: 17050
          required: false
          name: priceInUsd
          in: query
        - schema:
            type: integer
            description: Ondo BSC CoW request-lend / request-withdraw only. CoW validTo as a
              UTC Unix timestamp in seconds (not milliseconds). Must be in the
              future and at most 168 hours from now. Independent of priceInUsd.
              Other strategies return 400 EXPIRE_AT_NOT_SUPPORTED.
            example: 1778000000
          required: false
          name: expireAt
          in: query
      responses:
        "200":
          description: Bytecode generated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  feeCharged:
                    $ref: "#/components/schemas/BigIntLike"
                  chainIdIn:
                    type: number
                    example: 8453
                  chainIdOut:
                    type: number
                    example: 42161
                  id:
                    type: string
                    description: ID created for execution tracking
                    example: 6a04f2f75bf7233c6220b3ca
                  crossChain:
                    allOf:
                      - $ref: "#/components/schemas/CrossChainRoute"
                      - nullable: true
                        description: Cross-chain route metadata, or null for same-chain strategy actions
                  quote:
                    nullable: true
                    description: Bridge or swap quote metadata when the strategy action includes a
                      swap or bridge. Ondo BSC client-limit quotes also include
                      clientLimitPriceUsd (dollars), spotPriceUsd, and
                      distanceBps.
                  bytecode:
                    type: array
                    items:
                      $ref: "#/components/schemas/TransactionData"
                  userOperation:
                    nullable: true
                    description: User operation when output=userOperation
                  transactionData:
                    nullable: true
                    description: Fireblocks transaction request when output=fireblocks
                  transaction:
                    type: string
                    description: Base64 Solana transaction when output=bytecode on Solana
                  instructions:
                    type: array
                    items:
                      nullable: true
                    description: Solana instructions when output=instructions
                  lutsByAddress:
                    nullable: true
                    description: Solana lookup tables keyed by address
                required:
                  - feeCharged
                  - id
        "400":
          description: Bad request - invalid parameters
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Strategy not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/strategies/{strategyId}:
    get:
      tags:
        - Strategies
      summary: Get strategy details
      description: Returns enriched strategy metadata, spot position metrics, and rate
        history. Supply wallet to include wallet-scoped history and PnL.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Strategy ID or slug
          required: true
          name: strategyId
          in: path
        - schema:
            type: integer
            minimum: 7
            maximum: 365
            default: 7
          required: false
          name: days
          in: query
        - schema:
            type: string
          required: false
          name: from
          in: query
        - schema:
            type: string
          required: false
          name: to
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Wallet address for position, history, and PnL enrichment
          required: false
          name: wallet
          in: query
        - schema:
            type: number
            minimum: 0
            exclusiveMinimum: true
            default: 1
          required: false
          name: historyPage
          in: query
        - schema:
            type: number
            minimum: 1
            maximum: 500
            default: 100
          required: false
          name: historyLimit
          in: query
        - schema:
            type: string
            enum: &a4
              - INITIAL
              - PENDING
              - FAILED
              - REFUNDED
              - SUCCESS
          required: false
          name: historyStatus
          in: query
        - schema:
            type: string
          required: false
          name: historyType
          in: query
        - schema:
            type: string
            format: date-time
          required: false
          name: historyDateFrom
          in: query
        - schema:
            type: string
            format: date-time
          required: false
          name: historyDateTo
          in: query
        - schema:
            type: string
            enum: &a5
              - createdAt
              - updatedAt
              - finishedAt
            default: createdAt
          required: false
          name: historySortBy
          in: query
        - schema:
            type: string
            enum: &a6
              - asc
              - desc
            default: desc
          required: false
          name: historySortOrder
          in: query
      responses:
        "200":
          description: Enriched strategy details retrieved
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: "#/components/schemas/StrategyV2DetailResponse"
                  - $ref: "#/components/schemas/WalletStrategyV2DetailResponse"
                description: Without wallet, returns APY-only strategy detail. With wallet, also
                  returns wallet-scoped history and PnL.
        "403":
          description: Wallet does not belong to the authenticated customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Strategy not found or not enabled for the customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/wallets/{address}/strategies/{strategyId}:
    get:
      tags:
        - Strategies
      summary: Get wallet strategy details
      description: Returns enriched strategy details, position, history, PnL, and rate
        history for the wallet supplied in the path.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Customer-owned wallet address
          required: true
          name: address
          in: path
        - schema:
            type: string
            description: Strategy ID or slug
          required: true
          name: strategyId
          in: path
        - schema:
            type: integer
            minimum: 7
            maximum: 365
            default: 7
          required: false
          name: days
          in: query
        - schema:
            type: string
          required: false
          name: from
          in: query
        - schema:
            type: string
          required: false
          name: to
          in: query
        - schema:
            type: number
            minimum: 0
            exclusiveMinimum: true
            default: 1
          required: false
          name: historyPage
          in: query
        - schema:
            type: number
            minimum: 1
            maximum: 500
            default: 100
          required: false
          name: historyLimit
          in: query
        - schema:
            type: string
            enum: *a4
          required: false
          name: historyStatus
          in: query
        - schema:
            type: string
          required: false
          name: historyType
          in: query
        - schema:
            type: string
            format: date-time
          required: false
          name: historyDateFrom
          in: query
        - schema:
            type: string
            format: date-time
          required: false
          name: historyDateTo
          in: query
        - schema:
            type: string
            enum: *a5
            default: createdAt
          required: false
          name: historySortBy
          in: query
        - schema:
            type: string
            enum: *a6
            default: desc
          required: false
          name: historySortOrder
          in: query
      responses:
        "200":
          description: Wallet-scoped strategy details retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WalletStrategyV2DetailResponse"
        "403":
          description: Wallet does not belong to the authenticated customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Strategy not found or not enabled for the customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/strategies/{strategyId}/rate-history:
    get:
      tags:
        - Strategies
      summary: Get strategy rate history
      description: Returns paginated daily rate and APY history. The window ends on
        the previous UTC day so the response excludes an incomplete current-day
        snapshot.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Strategy ID or slug
          required: true
          name: strategyId
          in: path
        - schema:
            type: integer
            minimum: 7
            maximum: 365
            default: 7
          required: false
          name: days
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
          required: false
          name: limit
          in: query
        - schema:
            type: integer
            minimum: 0
            exclusiveMinimum: true
            default: 1
          required: false
          name: page
          in: query
      responses:
        "200":
          description: Strategy rate history retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StrategyRateHistoryResponse"
        "404":
          description: Strategy not found or not enabled for the customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/wallets/{address}:
    get:
      tags:
        - Wallets
      summary: Get wallet overview
      description: Returns token balances, earn positions, and paginated wallet
        activity for a customer-owned wallet. History and raw investment actions
        remain included by default for backwards compatibility and can be
        omitted independently.
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Customer-owned wallet address
          required: true
          name: address
          in: path
        - schema:
            type: string
            enum:
              - tokens
              - earn
              - all
            default: all
          required: false
          name: include
          in: query
        - schema:
            type: boolean
            default: true
            description: Include paginated transaction history in the response.
          required: false
          name: includeHistory
          in: query
        - schema:
            type: boolean
            default: true
            description: Include raw investment actions inside each earn position.
          required: false
          name: withActions
          in: query
        - schema:
            type: number
            minimum: 0
            exclusiveMinimum: true
            default: 1
          required: false
          name: historyPage
          in: query
        - schema:
            type: number
            minimum: 1
            maximum: 500
            default: 100
          required: false
          name: historyLimit
          in: query
        - schema:
            type: string
            enum:
              - INITIAL
              - PENDING
              - FAILED
              - REFUNDED
              - SUCCESS
          required: false
          name: historyStatus
          in: query
        - schema:
            type: string
          required: false
          name: historyType
          in: query
        - schema:
            type: string
            format: date-time
          required: false
          name: historyDateFrom
          in: query
        - schema:
            type: string
            format: date-time
          required: false
          name: historyDateTo
          in: query
        - schema:
            type: string
            enum:
              - createdAt
              - updatedAt
              - finishedAt
            default: createdAt
          required: false
          name: historySortBy
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
          required: false
          name: historySortOrder
          in: query
      responses:
        "200":
          description: Wallet overview retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WalletOverviewResponse"
        "403":
          description: Wallet does not belong to the authenticated customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid path or query parameters
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /transfer/bytecode:
    get:
      tags:
        - Transfer
      summary: Generate transfer transaction bytecode
      description: Generate ready-to-sign transaction data for token transfers.
        Supports EVM native/ERC-20 transfers and Solana native/SPL transfers.
        The response includes an `id` for execution tracking.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            allOf:
              - $ref: "#/components/schemas/BigIntLike"
              - description: Amount in token smallest unit
          required: true
          name: amount
          in: query
        - schema:
            allOf:
              - $ref: "#/components/schemas/BigIntLike"
              - description: Source chain ID. Use `0` for Solana transfers.
                example: "137"
          required: true
          name: chainId
          in: query
        - schema:
            type: string
            description: Address that signs and sends the transfer
          required: true
          name: originAddress
          in: query
        - schema:
            type: string
            description: Address receiving the transferred token
          required: true
          name: destinationAddress
          in: query
        - schema:
            type: string
            description: Token address. Use the native token sentinel for native-token
              transfers.
          required: true
          name: token
          in: query
        - schema:
            type: string
            enum:
              - bytecode
              - userOperation
              - instructions
            default: bytecode
            description: "`bytecode` returns EVM transaction array or Solana transaction;
              `userOperation` is EVM only; `instructions` is Solana only."
          required: false
          name: output
          in: query
        - schema:
            type: string
            description: Deprecated alias for originAddress
            deprecated: true
          required: false
          name: walletOrigin
          in: query
        - schema:
            type: string
            description: Deprecated alias for destinationAddress
            deprecated: true
          required: false
          name: walletDestination
          in: query
      responses:
        "200":
          description: Transfer bytecode generated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    description: ID created for execution tracking
                    example: 65f1a2b3c4d5e6f789012345
                  chainId:
                    allOf:
                      - $ref: "#/components/schemas/BigIntLike"
                      - description: EVM chain ID for EVM transfer outputs
                        example: "137"
                  bytecode:
                    type: array
                    items:
                      $ref: "#/components/schemas/TransactionData"
                    description: EVM transfer transaction followed by tracking transaction when
                      output=bytecode
                  userOperation:
                    nullable: true
                    description: EVM user operation when output=userOperation
                  transaction:
                    type: string
                    description: Base64 Solana transaction when output=bytecode and chainId=0
                  instructions:
                    type: array
                    items:
                      nullable: true
                    description: Serialized Solana instructions when output=instructions and
                      chainId=0
                required:
                  - id
        "400":
          description: Bad request - invalid parameters
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/strategies/{strategyId}/market-data:
    get:
      tags:
        - Strategies
      summary: Get strategy share-price history
      description: "Returns paginated share-price candles for a strategy. Resolution
        is selected by range: 1d uses 15-minute points, 1w/1m use hourly points,
        and longer ranges use daily points."
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Strategy ID or slug
          required: true
          name: strategyId
          in: path
        - schema:
            type: string
            enum:
              - 1d
              - 1w
              - 1m
              - 3m
              - 1y
              - all
            default: 1d
          required: false
          name: range
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 1000
          required: false
          name: limit
          in: query
        - schema:
            type: integer
            minimum: 0
            exclusiveMinimum: true
            default: 1
          required: false
          name: page
          in: query
      responses:
        "200":
          description: Share-price series retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StrategyMarketDataHistoryResponse"
        "404":
          description: Strategy not found or not enabled for the customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /strategies/analytics:
    get:
      tags:
        - Strategies
      summary: Get strategy analytics
      description: Returns paginated analytics webhook events for the authenticated
        customer. Requires API key or Bearer token. Pass `period` explicitly
        (`all-time`, `24h`, `7d`, or `30d`). Minimum `limit` is 10.
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - schema:
            type: string
            enum:
              - all-time
              - 24h
              - 7d
              - 30d
            default: all-time
            description: Analytics time window
            example: all-time
          required: false
          name: period
          in: query
        - schema:
            type: integer
            minimum: 0
            exclusiveMinimum: true
            default: 1
            description: Page number
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 10
            maximum: 1000
            default: 100
            description: Results per page (minimum 10, maximum 1000)
            example: 100
          required: false
          name: limit
          in: query
      responses:
        "200":
          description: Analytics retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/AnalyticsItem"
                  pagination:
                    $ref: "#/components/schemas/Pagination"
                required:
                  - data
                  - pagination
        "401":
          description: Unauthorized — missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Validation failed — e.g. limit below 10 or invalid period
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/strategies/{strategyId}/pnl:
    get:
      tags:
        - Strategies
      summary: Get wallet PnL history for a strategy
      description: Returns a daily unrealized-PnL timeline for a wallet in a strategy,
        based on the wallet's deposit and withdrawal cost basis and the
        strategy's daily share price. The `wallet` query parameter is required
        and must be owned by the authenticated customer, who must also have the
        strategy enabled. Amounts are underlying-asset raw units serialized as
        strings. Provide either `days` or an explicit `from`/`to` range.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Strategy ID or slug
          required: true
          name: strategyId
          in: path
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Wallet address to scope the PnL series to. Required for this route
                  form.
          required: true
          name: wallet
          in: query
        - schema:
            type: integer
            minimum: 7
            maximum: 365
            default: 7
            description: Rolling window size in days ending yesterday (UTC). Minimum 7,
              maximum 365. Ignored when from/to are provided.
            example: 7
          required: false
          name: days
          in: query
        - schema:
            type: string
            description: Inclusive range start (YYYY-MM-DD, UTC). When set, overrides days.
            example: 2026-07-01
          required: false
          name: from
          in: query
        - schema:
            type: string
            description: Inclusive range end (YYYY-MM-DD, UTC). Cannot be in the future.
            example: 2026-07-20
          required: false
          name: to
          in: query
        - schema:
            type: integer
            minimum: 0
            exclusiveMinimum: true
            default: 1
            description: Page number
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
            description: Results per page (maximum 500)
            example: 100
          required: false
          name: limit
          in: query
      responses:
        "200":
          description: PnL timeline retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StrategyPnlResponse"
        "400":
          description: Missing wallet address or invalid date range
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Strategy not found or not enabled for the customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v2/wallets/{address}/strategies/{strategyId}/pnl:
    get:
      tags:
        - Strategies
      summary: Get wallet PnL history for a strategy (wallet in path)
      description: Same as GET /v2/strategies/{strategyId}/pnl, but the wallet is
        supplied as the `address` path parameter instead of the `wallet` query
        parameter. The wallet must be owned by the authenticated customer, who
        must also have the strategy enabled.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Wallet address to scope the PnL series to.
          required: true
          name: address
          in: path
        - schema:
            type: string
            description: Strategy ID or slug
          required: true
          name: strategyId
          in: path
        - schema:
            type: integer
            minimum: 7
            maximum: 365
            default: 7
            description: Rolling window size in days ending yesterday (UTC). Minimum 7,
              maximum 365. Ignored when from/to are provided.
            example: 7
          required: false
          name: days
          in: query
        - schema:
            type: string
            description: Inclusive range start (YYYY-MM-DD, UTC). When set, overrides days.
            example: 2026-07-01
          required: false
          name: from
          in: query
        - schema:
            type: string
            description: Inclusive range end (YYYY-MM-DD, UTC). Cannot be in the future.
            example: 2026-07-20
          required: false
          name: to
          in: query
        - schema:
            type: integer
            minimum: 0
            exclusiveMinimum: true
            default: 1
            description: Page number
          required: false
          name: page
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
            description: Results per page (maximum 500)
            example: 100
          required: false
          name: limit
          in: query
      responses:
        "200":
          description: PnL timeline retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StrategyPnlResponse"
        "400":
          description: Invalid date range
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Strategy not found or not enabled for the customer
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /customers/me:
    get:
      tags:
        - Customers
      summary: Get customer profile
      description: Get authenticated customer information. Requires a Supabase or
        Magic **Bearer** token — API key authentication is not supported on this
        endpoint.
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Customer profile retrieved
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  email:
                    type: string
                  feeCollectorAddress:
                    type: string
                    nullable: true
                  customerFeeCollectorAddress:
                    type: string
                    nullable: true
                  apiKey:
                    type: string
                    nullable: true
                  plan:
                    type: string
                  role:
                    type: string
                    nullable: true
                  isActive:
                    type: boolean
                  createdAt:
                    type: string
                required:
                  - id
                  - name
                  - email
        "401":
          description: Unauthorized — missing or invalid Bearer token
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Validation failed — Authorization header missing or malformed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /quotes:
    get:
      tags:
        - Quotes
      summary: List quotes
      description: Get list of quotes with optional filtering
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
          required: false
          name: originChain
          in: query
        - schema:
            type: string
          required: false
          name: destinationChain
          in: query
        - schema:
            type: string
          required: false
          name: provider
          in: query
        - schema:
            type: string
            default: "1"
          required: false
          name: page
          in: query
        - schema:
            type: string
            default: "10"
          required: false
          name: limit
          in: query
      responses:
        "200":
          description: Quotes retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PersistedQuote"
                  pagination:
                    $ref: "#/components/schemas/Pagination"
                required:
                  - data
                  - pagination
  /ondo/stocks/market-status:
    get:
      tags:
        - Ondo
      summary: GET /ondo/stocks/market-status
      description: "Returns Ondo GM market status with a stable response shape. The
        market-wide fields (`isOpen`, session fields, optional close reason) are
        always present. `asset` is `null` when `symbol` is omitted; when
        `symbol` is provided (GM token ending in lowercase `on`, e.g. `NVDAon`),
        `asset` nests per-symbol pause/limit status, market data, and a concrete
        `tradable` / `blockingReason` decision. `asset.tradable` is false for
        market closed/paused (`ONDO_MARKET_CLOSED` / `ONDO_MARKET_PAUSED`,
        unless off-hours are open) or an active asset pause
        (`ONDO_ASSET_PAUSED`). An active `ASSET_LIMITED` restriction only sets
        `limited: true` and does not block trading."
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            pattern: ^[A-Za-z0-9.]{1,}on$
            description: Optional Ondo GM token symbol ending in lowercase `on` (e.g.
              NVDAon). When omitted, `asset` is null.
            example: NVDAon
          required: false
          name: symbol
          in: query
      responses:
        "200":
          description: Market status with optional nested asset tradability
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OndoMarketStatus"
        "401":
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid symbol (must end in lowercase `on`)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: Upstream Ondo GM market status request failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          description: Ondo GM API is not configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /tokens:
    get:
      tags:
        - Tokens
      summary: List tokens
      description: Returns the paginated token catalog available to the authenticated
        customer. Use includeExtended for coingeckoId and listedIn fields;
        enhanced responses also include token groups.
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - schema:
            type: string
            default: "1"
            description: Page number
          required: false
          name: page
          in: query
        - schema:
            type: string
            default: "100"
            description: Results per page
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Filter by chain ID
          required: false
          name: chainId
          in: query
        - schema:
            type: string
            default: "false"
            description: Include coingeckoId and listedIn fields
          required: false
          name: includeExtended
          in: query
        - schema:
            type: string
            default: "true"
            description: Return token groups with the token catalog
          required: false
          name: enhanced
          in: query
        - schema:
            type: string
            default: "1"
            description: Token groups page number for enhanced responses
          required: false
          name: tokenGroupsPage
          in: query
        - schema:
            type: string
            default: "100"
            description: Token groups results per page for enhanced responses
          required: false
          name: tokenGroupsLimit
          in: query
        - schema:
            type: string
            description: Search tokens by name or symbol
          required: false
          name: search
          in: query
        - schema:
            type: string
            description: Comma-separated token categories, optionally combined with chainId
          required: false
          name: category
          in: query
        - schema:
            type: string
            description: Comma-separated token addresses, optionally as address:chainId
              entries
          required: false
          name: addresses
          in: query
        - schema:
            type: string
            default: "false"
            description: Return the full catalog instead of a customer-curated token list
          required: false
          name: catalog
          in: query
      responses:
        "200":
          description: Tokens retrieved successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokenCatalogResponse"
        "401":
          description: Unauthorized - missing or invalid credentials
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Invalid query parameters
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /strategies/{strategyId}/status:
    get:
      tags:
        - Strategies
      summary: Get pending strategy status
      description: Returns pending investment actions for a wallet on a specific
        strategy. Pending cross-chain and CoW actions are refreshed against
        their providers before responding.
      security:
        - ApiKeyAuth: []
      parameters:
        - schema:
            type: string
            description: Strategy ID or slug
          required: true
          name: strategyId
          in: path
        - schema:
            allOf:
              - $ref: "#/components/schemas/Address"
              - description: Wallet address to check pending actions for
          required: true
          name: wallet
          in: query
      responses:
        "200":
          description: Pending status retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StrategyStatusResponse"
        "404":
          description: Strategy not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /api/v1/kyc/bigdatacorp/already-verified:
    get:
      tags:
        - KYC
      summary: Render the Pods verified identity WebView page
      description: Public, PII-free completion page returned as iframeUrl when a
        BigDataCorp session request reuses an approved Avenia identity.
      responses:
        "200":
          description: Static verification completion page
          content:
            text/html:
              schema:
                type: string
  /smart-account:
    post:
      tags:
        - Smart Account
      summary: Create or retrieve smart account
      description: Create or retrieve a generic ERC-4337 Gnosis Safe smart account for
        a given owner address and chain. Deployment is asynchronous; the
        deterministic CREATE2 address is returned immediately.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                owner:
                  allOf:
                    - $ref: "#/components/schemas/Address"
                    - description: Owner EOA address
                chainId:
                  type: number
                  description: Target chain ID
                  example: 8453
              required:
                - owner
                - chainId
      responses:
        "200":
          description: Smart account address returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SmartAccountResponse"
        "401":
          description: Unauthorized - invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /aa/send-user-operation:
    post:
      tags:
        - Smart Account
      summary: Send user operation
      description: Submit a signed ERC-4337 v0.6 user operation to the bundler.
        Request the user operation payload from GET /v2/swap/quote or GET
        /strategies/:id/bytecode with output=userOperation, sign it, then send
        it here. Returns the bundler userOpHash. Only the v0.6 EntryPoint is
        supported.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                chainId:
                  type: number
                  description: Target chain ID for the user operation
                  example: 137
                entryPoint:
                  allOf:
                    - $ref: "#/components/schemas/Address"
                    - description: Optional ERC-4337 EntryPoint. Only the v0.6 EntryPoint
                        (0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789) is
                        supported; a mismatch returns UNSUPPORTED_ENTRYPOINT.
                userOperation:
                  type: object
                  properties:
                    sender:
                      allOf:
                        - $ref: "#/components/schemas/Address"
                        - example: "0x1111111111111111111111111111111111111111"
                    nonce:
                      type: string
                      example: "0"
                    initCode:
                      type: string
                      example: 0x
                    callData:
                      type: string
                      example: 0x...
                    callGasLimit:
                      type: string
                      example: "200000"
                    verificationGasLimit:
                      type: string
                      example: "150000"
                    preVerificationGas:
                      type: string
                      example: "50000"
                    maxFeePerGas:
                      type: string
                      example: "1000000000"
                    maxPriorityFeePerGas:
                      type: string
                      example: "1000000000"
                    paymasterAndData:
                      type: string
                      example: 0x
                    signature:
                      type: string
                      example: 0x...
                  required:
                    - sender
                    - nonce
                    - initCode
                    - callData
                    - callGasLimit
                    - verificationGasLimit
                    - preVerificationGas
                    - maxFeePerGas
                    - maxPriorityFeePerGas
                    - paymasterAndData
                    - signature
                  description: Signed ERC-4337 v0.6 user operation. Numeric fields may be decimal
                    or 0x-hex strings; the endpoint normalizes them. Send the
                    fields exactly as returned by the bytecode/quote step, with
                    the signature attached.
              required:
                - chainId
                - userOperation
      responses:
        "200":
          description: User operation accepted by the bundler
          content:
            application/json:
              schema:
                type: object
                properties:
                  userOpHash:
                    type: string
                    description: Bundler user operation hash
                    example: 0xabc123...
                required:
                  - userOpHash
        "400":
          description: Bundler rejected the user operation, or unsupported chain/entryPoint
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Unauthorized - invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
