> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pods.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# Link a wallet to a KYC profile

> Links an extra wallet to an existing KYC profile so it can quote and ticket against the same kycUserId. walletAddress stays the primary address stored when the profile was created. Repeating the call for a wallet that is already linked returns the current list. A wallet can belong to only one kycUserId per customer.



## OpenAPI

````yaml /openapi.yaml post /api/v1/kyc/{kycUserId}/wallets
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

    - **MARKUP_RECIPIENT_CONFLICTS_WITH_PERFORMANCE_COLLECTOR** (422): Markup
    fee recipient cannot be the performance fee collector

    - **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_KEY** (400): The Pix key is invalid or was not found

    - **PIX_KEY_LOOKUP_FAILED** (502): Unable to validate the Pix key. Please
    try again later

    - **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

    - **INVALID_RAMP_PROVIDER** (400): Unknown rampProvider name

    - **RAMP_PROVIDER_NOT_ELIGIBLE** (400): rampProvider is not eligible for
    this fiat ramp route

    - **RAMP_AMOUNT_OUT_OF_RANGE** (422): Amount is outside the range this ramp
    provider accepts

    - **RAMP_ACCOUNT_ID_MISSING** (400): KYC profile is missing the selected
    ramp provider account id

    - **UNBLOCKPAY_NOT_CONFIGURED** (500): UnblockPay API key is not configured

    - **UNBLOCKPAY_API_ERROR** (502): UnblockPay API request failed

    - **INVALID_UNBLOCKPAY_QUOTE** (502): UnblockPay quote is missing required
    fields

    - **INVALID_UNBLOCKPAY_TICKET** (502): UnblockPay transaction is missing
    required fields

    - **INVALID_UNBLOCKPAY_CUSTOMER** (502): UnblockPay customer is missing
    required fields

    - **UNBLOCKPAY_KYC_NOT_PROVISIONED** (409): UnblockPay customer is not
    provisioned on this KYC profile

    - **UNBLOCKPAY_KYC_REJECTED** (409): UnblockPay KYC was rejected and cannot
    be retried from this profile

    - **UNBLOCKPAY_KYC_IN_PROGRESS** (409): UnblockPay KYC is already under
    review

    - **UNBLOCKPAY_USD_OFFRAMP_ACCOUNT_MISSING** (409): UnblockPay USD offramp
    needs a wire external account. POST /api/v1/kyc/currency-unlock with
    rampProvider=unblockpay and the bank fields listed on
    ramps.unblockpay.unlock.requiredBodyFields

    - **KYC_PROFILE_INCOMPLETE_FOR_RAMP** (400): KYC profile is missing fields
    required by the selected ramp provider

    - **KYC_WALLET_ALREADY_LINKED** (409): Wallet is already linked to another
    KYC profile for this customer

    - **KYC_WALLET_NOT_LINKED** (404): Wallet is not linked to this KYC profile

    - **KYC_LAST_WALLET** (409): Cannot unlink the last wallet from a KYC
    profile

    - **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
security: []
tags:
  - name: Health
    description: Health check endpoints
  - name: KYC
    description: >-
      KYC onboarding and status endpoints: Brazilian and foreign Sumsub import,
      BigDataCorp capture, external evidence, linked wallets, 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
paths:
  /api/v1/kyc/{kycUserId}/wallets:
    post:
      tags:
        - KYC
      summary: Link a wallet to a KYC profile
      description: >-
        Links an extra wallet to an existing KYC profile so it can quote and
        ticket against the same kycUserId. walletAddress stays the primary
        address stored when the profile was created. Repeating the call for a
        wallet that is already linked returns the current list. A wallet can
        belong to only one kycUserId per customer.
      parameters:
        - schema:
            type: string
            format: uuid
            description: Pods-generated KYC user id
            example: 550e8400-e29b-41d4-a716-446655440000
          required: true
          name: kycUserId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LinkKycWalletRequest'
      responses:
        '200':
          description: Wallet linked, or already linked to this profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KycWalletsResponse'
        '401':
          description: Unauthorized - missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: >-
            KYC_PROFILE_NOT_FOUND — no active profile for this kycUserId on the
            authenticated customer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: >-
            KYC_WALLET_ALREADY_LINKED — the wallet belongs to another KYC
            profile for this customer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Invalid kycUserId or walletAddress
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - ApiKeyAuth: []
components:
  schemas:
    LinkKycWalletRequest:
      type: object
      properties:
        walletAddress:
          $ref: '#/components/schemas/WalletAddress'
      required:
        - walletAddress
      description: >-
        Wallet to link to an existing KYC profile without creating a second
        profile
    KycWalletsResponse:
      type: object
      properties:
        kycUserId:
          type: string
          format: uuid
          description: Pods-generated public KYC user id
          example: 550e8400-e29b-41d4-a716-446655440000
        walletAddress:
          allOf:
            - $ref: '#/components/schemas/WalletAddress'
            - description: >-
                Primary wallet. Linking another address does not replace it.
                Unlinking the primary promotes the next linked wallet.
        walletAddresses:
          type: array
          items:
            $ref: '#/components/schemas/WalletAddress'
          description: >-
            Every wallet allowed to quote and ticket against this kycUserId. A
            wallet belongs to only one KYC profile per customer.
      required:
        - kycUserId
        - walletAddress
        - walletAddresses
      description: Wallets linked to one Pods KYC profile
    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
    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
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: API key for authentication. Obtain from your Pods dashboard.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.