openapi: 3.1.0
info:
  title: RC-Agents — DeFi Automation Marketplace API
  version: 1.0.0
  description: |
    An automation marketplace where AI agents pay micropayments via x402 to purchase autonomous
    DeFi protection services powered by Reactive Smart Contracts.

    ## Available Services

    1. **Aave Liquidation Protection** ($0.30/day) — Monitors health factors every ~12 minutes
       and automatically supplies collateral or repays debt when the health factor drops below
       a configured threshold.
    2. **DCA Strategy** ($0.24/day) — Executes periodic Uniswap V3 swaps (e.g., USDC → WETH)
       on configurable intervals with slippage protection.

    ## How it works
    1. Call `GET /api/services` to see available services and pricing.
    2. Call `POST /api/quote` to get an exact price for your desired duration.
    3. Call `POST /api/protect/liquidation` with an x402 payment header to activate protection.
       - The endpoint is gated by the [x402 payment protocol](https://x402.org).
       - If no payment is included, the server returns `402 Payment Required` with payment details.
       - Payment is in USDC on Base Sepolia (eip155:84532).
    4. After payment, approve the `AaveProtectionCallback` contract (returned in the response)
       to spend your collateral and/or debt assets.
    5. Manage the config with pause/resume/cancel. Monitor it via the status endpoints.

    ## x402 Payment Flow
    Protected endpoints return `402 Payment Required` when called without payment.
    The response body contains `accepts` — an array of payment options. Agents must:
    1. Sign an EIP-3009 `transferWithAuthorization` over USDC.
    2. Encode and attach it as the `X-PAYMENT` header.
    3. Retry the request.

    See https://x402.org/docs for SDK helpers in TypeScript and Python.

    ## Networks
    - **Base Sepolia** (`eip155:84532`) — Aave positions + callback contract
    - **Lasna Testnet** (`eip155:5318007`) — Reactive contract (monitoring + automation)

    ## Key Addresses (Base Sepolia)
    - USDC (x402 payments): `0x036CbD53842c5426634e7929541eC2318f3dCF7e`
    - USDC (Aave debt asset): `0xba50Cd2A20f6DA35D788639E581bca8d0B5d4D5f`
    - WETH (collateral): `0x4200000000000000000000000000000000000006`
    - Aave Pool: `0x8bAB6d1b75f19e9eD9fCe8b9BD338844fF79aE27`
    - AaveProtectionCallback: `0x24df0bBC9c4b95e8643848EC6B7f0Ac638BD3476`
    - AaveProtectionReactive (Lasna): `0xb1d20ecA7e6e6998A985C41Ae69695125F67619D`

servers:
  - url: https://rc-agents.vercel.app
    description: Production server
  - url: http://localhost:3000
    description: Local development server

tags:
  - name: Discovery
    description: Service catalog and pricing — no payment required
  - name: Protection
    description: Create and manage liquidation protection configs
  - name: Status
    description: Query on-chain state — no payment required
  - name: DCA
    description: DCA (Dollar Cost Averaging) strategy on Uniswap V3
  - name: GOAT
    description: |
      DCA on GOAT Network Testnet3. No Reactive Network — the execution function
      is genuinely permissionless (anyone can call it, not just this server).
      Payment is still x402 USDC on Base Sepolia; execution is a real Uniswap V3
      Core swap on GOAT. See https://github.com/harshkas4na/rc-agents/tree/main/goat-research
  - name: Health
    description: Server health check

paths:
  /health:
    get:
      operationId: getServerHealth
      tags: [Health]
      summary: Server health check
      description: |
        Returns server status and whether the Reactive Contract on Lasna is sufficiently funded.
        If `reactiveContractFunded` is false, new protection configs will be rejected (503) because
        the monitoring callbacks won't fire.
      responses:
        "200":
          description: Server is healthy or degraded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
        "503":
          description: Server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/services:
    get:
      operationId: listServices
      tags: [Discovery]
      summary: List available services and pricing
      description: |
        Returns the full service catalog with pricing details. Use this to discover what
        protection services are available and their per-day costs before purchasing.
        No payment required.
      responses:
        "200":
          description: Service catalog
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServicesResponse"

  /api/quote:
    post:
      operationId: getQuote
      tags: [Discovery]
      summary: Get exact price quote for a service
      description: |
        Returns the exact USDC price (in base units, 6 decimals) for a given service and duration.
        Price includes a 20% gas buffer on top of the base rate.

        Base rate: $0.25/day for `aave-protection`.

        Use this before calling the protected endpoint so you know exactly how much USDC to authorize.
        No payment required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QuoteRequest"
            examples:
              oneDay:
                summary: Quote for 1 day
                value:
                  service: aave-protection
                  durationSeconds: 86400
              sevenDays:
                summary: Quote for 7 days
                value:
                  service: aave-protection
                  durationSeconds: 604800
      responses:
        "200":
          description: Price quote
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QuoteResponse"
        "400":
          description: Invalid service ID or duration out of range
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/protect/liquidation:
    post:
      operationId: createLiquidationProtection
      tags: [Protection]
      summary: Create Aave liquidation protection (x402-gated)
      description: |
        Creates an on-chain protection configuration. The Reactive Contract will then monitor
        the protected user's Aave health factor every ~12 minutes and execute protection
        (collateral deposit, debt repayment, or both) when it drops below the threshold.

        **This endpoint requires x402 payment in USDC on Base Sepolia (eip155:84532).**

        ### Without payment
        Returns `402 Payment Required` with an `accepts` array describing how to pay.
        The price is dynamic — computed from the `duration` field in the request body.

        ### With payment
        Attach the signed EIP-3009 authorization as the `X-PAYMENT` header (base64-encoded JSON).
        On success, returns the `configId` and the AaveProtectionCallback address that needs
        token approval before protection can execute.

        ### After creation
        You MUST approve the `AaveProtectionCallback` contract to spend:
        - `collateralAsset` tokens (for collateral deposit mode)
        - `debtAsset` tokens (for debt repayment mode)
        Both if `protectionType` is `2` (BOTH).

        ### Protection types
        | Value | Name | Description |
        |-------|------|-------------|
        | 0 | COLLATERAL_DEPOSIT | Supplies collateral to Aave to raise HF |
        | 1 | DEBT_REPAYMENT | Repays debt to raise HF |
        | 2 | BOTH | Tries collateral first, falls back to debt repayment |

        ### Health factor encoding
        Health factors are WAD-encoded (18 decimals). Examples:
        - `1.5 HF` → `"1500000000000000000"`
        - `2.0 HF` → `"2000000000000000000"`
      security:
        - x402: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateProtectionRequest"
            examples:
              defaultProtection:
                summary: Protect with collateral deposit, 1 day
                value:
                  protectedUser: "0xYourWalletAddress"
                  protectionType: 0
                  healthFactorThreshold: "1500000000000000000"
                  targetHealthFactor: "2000000000000000000"
                  collateralAsset: "0x4200000000000000000000000000000000000006"
                  debtAsset: "0xba50Cd2A20f6DA35D788639E581bca8d0B5d4D5f"
                  preferDebtRepayment: false
                  duration: 86400
      responses:
        "200":
          description: Protection config created successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateProtectionResponse"
        "400":
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
        "402":
          description: Payment required — attach x402 payment header and retry
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentRequiredResponse"
        "500":
          description: On-chain transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "503":
          description: Reactive Contract underfunded — service temporarily unavailable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/protect/liquidation/pause:
    post:
      operationId: pauseLiquidationProtection
      tags: [Protection]
      summary: Pause a protection config
      description: |
        Pauses an active protection config. While paused, the Reactive Contract will skip
        health factor checks for this config. No payment required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfigIdRequest"
      responses:
        "200":
          description: Config paused
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigActionResponse"
        "400":
          description: Invalid configId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: On-chain transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/protect/liquidation/resume:
    post:
      operationId: resumeLiquidationProtection
      tags: [Protection]
      summary: Resume a paused protection config
      description: |
        Resumes a previously paused protection config. Health factor monitoring will
        resume on the next Reactive callback cycle (~12 minutes). No payment required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfigIdRequest"
      responses:
        "200":
          description: Config resumed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigActionResponse"
        "400":
          description: Invalid configId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: On-chain transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/protect/liquidation/cancel:
    post:
      operationId: cancelLiquidationProtection
      tags: [Protection]
      summary: Cancel a protection config permanently
      description: |
        Permanently cancels a protection config. This action is irreversible — a cancelled
        config cannot be resumed. No payment required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfigIdRequest"
      responses:
        "200":
          description: Config cancelled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigActionResponse"
        "400":
          description: Invalid configId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: On-chain transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/status/config/{configId}:
    get:
      operationId: getProtectionConfig
      tags: [Status]
      summary: Get protection config details
      description: |
        Fetches the on-chain state of a protection config including its current status,
        execution history, and all configuration parameters. No payment required.
      parameters:
        - name: configId
          in: path
          required: true
          description: The numeric config ID returned when the config was created
          schema:
            type: integer
            minimum: 0
          example: 42
      responses:
        "200":
          description: Config details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProtectionConfigResponse"
        "400":
          description: Invalid config ID format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Failed to fetch from chain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/status/health/{userAddress}:
    get:
      operationId: getHealthFactor
      tags: [Status]
      summary: Get current Aave health factor for a user
      description: |
        Returns the current Aave health factor for a given wallet address on Base Sepolia.
        Health factor is read directly from the Aave lending pool via the callback contract.

        - `healthFactorDecimal` is null when the user has no Aave position.
        - `atRisk` is true when the health factor is below 1.5.
        - A health factor below 1.0 means the position is liquidatable.

        No payment required.
      parameters:
        - name: userAddress
          in: path
          required: true
          description: EVM wallet address (checksummed or lowercase)
          schema:
            type: string
            pattern: "^0x[a-fA-F0-9]{40}$"
          example: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
      responses:
        "200":
          description: Current health factor
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthFactorResponse"
        "400":
          description: Invalid address format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Failed to fetch from chain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/approve/permit:
    post:
      operationId: relayPermit
      tags: [Protection]
      summary: Relay an EIP-2612 permit signature on-chain (free)
      description: |
        HTTP-only agents (e.g. OpenClaw-style) can pay via x402 but cannot submit EVM transactions
        to approve the callback contract to spend their tokens. This endpoint accepts a pre-signed
        EIP-2612 permit and relays it on-chain — the server pays the Base Sepolia gas.

        **Only works for EIP-2612 tokens (USDC). WETH does not support permit.**

        This endpoint is free — no x402 payment needed. The fee is already included in the
        protection service price.

        ### Typical HTTP-only agent flow
        1. `GET /api/services` — discover service and CC address
        2. `POST /api/quote` — get price for duration
        3. Sign EIP-2612 `permit(owner, CC_address, amount, deadline)` off-chain
        4. `POST /api/approve/permit` — relay the permit (this endpoint)
        5. `POST /api/protect/liquidation` (with x402 payment) — register protection

        ### Limitation
        HTTP-only agents can only use `protectionType: 1` (DEBT_REPAYMENT with Aave USDC).
        `protectionType: 0` (COLLATERAL_DEPOSIT with WETH) requires a direct EVM approval
        transaction because WETH has no permit support.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PermitRequest"
            examples:
              usdcPermit:
                summary: Permit Aave USDC spend for debt repayment
                value:
                  token: "0xba50Cd2A20f6DA35D788639E581bca8d0B5d4D5f"
                  owner: "0xYourWalletAddress"
                  spender: "0xAaveProtectionCallbackAddress"
                  value: "1000000000"
                  deadline: 1712000000
                  v: 27
                  r: "0xabc123..."
                  s: "0xdef456..."
      responses:
        "200":
          description: Permit relayed successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PermitResponse"
        "400":
          description: Invalid parameters or WETH address provided
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: On-chain permit transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/dca/activate:
    post:
      operationId: activateDCAStrategy
      tags: [DCA]
      summary: Activate DCA automation (x402-gated)
      description: |
        Pays for Reactive Network automation gas to power periodic Uniswap V3 swaps.
        After payment, returns the DCA callback contract address and instructions for
        the agent to call `createDCAConfig()` directly on-chain.

        **This endpoint requires x402 payment in USDC on Base Sepolia (eip155:84532).**

        ### Flow (identical to Aave Liquidation Protection)
        1. Agent calls this endpoint with x402 payment (price based on `duration`)
        2. Server creates the DCA config on-chain via the callback contract
        3. Server funds the DCA Reactive Contract gas pool
        4. Server returns `configId`, tx hash, and token approval instructions
        5. Agent approves the CC to spend their `tokenIn`
        6. Reactive Network starts executing swaps on each CRON tick (~12 min)
      security:
        - x402: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DCAActivateRequest"
            examples:
              usdcToWeth:
                summary: DCA 10 USDC into WETH, 10 swaps, every 12 min
                value:
                  user: "0xYourWalletAddress"
                  tokenIn: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
                  tokenOut: "0x4200000000000000000000000000000000000006"
                  amountPerSwap: "10000000"
                  poolFee: 3000
                  totalSwaps: 10
                  swapInterval: 720
                  minAmountOut: "0"
                  duration: 86400
      responses:
        "200":
          description: DCA automation funded successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DCAActivateResponse"
        "400":
          description: Invalid parameters
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
        "402":
          description: Payment required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentRequiredResponse"
        "503":
          description: DCA Reactive Contract underfunded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/dca/pause:
    post:
      operationId: pauseDCAConfig
      tags: [DCA]
      summary: Pause a DCA config
      description: |
        Pauses an active DCA config. While paused, the Reactive Contract will skip
        swap execution for this config. Server calls this as admin (contract owner).
        No payment required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfigIdRequest"
      responses:
        "200":
          description: Config paused
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigActionResponse"
        "400":
          description: Invalid configId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: On-chain transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/dca/resume:
    post:
      operationId: resumeDCAConfig
      tags: [DCA]
      summary: Resume a paused DCA config
      description: |
        Resumes a previously paused DCA config. Swap execution resumes on the next
        CRON cycle (~12 minutes). No payment required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfigIdRequest"
      responses:
        "200":
          description: Config resumed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigActionResponse"
        "400":
          description: Invalid configId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: On-chain transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/dca/cancel:
    post:
      operationId: cancelDCAConfig
      tags: [DCA]
      summary: Cancel a DCA config permanently
      description: |
        Permanently cancels a DCA config. Remaining swaps will not execute.
        This action is irreversible. No payment required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfigIdRequest"
      responses:
        "200":
          description: Config cancelled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigActionResponse"
        "400":
          description: Invalid configId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: On-chain transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/dca/config/{configId}:
    get:
      operationId: getDCAConfig
      tags: [DCA]
      summary: Get DCA config details
      description: |
        Fetches the on-chain state of a DCA config including swap progress,
        cumulative output, and all configuration parameters. No payment required.
      parameters:
        - name: configId
          in: path
          required: true
          description: The numeric DCA config ID
          schema:
            type: integer
            minimum: 0
      responses:
        "200":
          description: DCA config details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DCAConfigResponse"
        "400":
          description: Invalid config ID
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Failed to fetch from chain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/dca/configs:
    get:
      operationId: listActiveDCAConfigs
      tags: [DCA]
      summary: List all active DCA config IDs
      description: |
        Returns all currently active DCA config IDs. No payment required.
      responses:
        "200":
          description: Active DCA config IDs
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActiveConfigsResponse"
        "500":
          description: Failed to fetch from chain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/dca/user/{userAddress}:
    get:
      operationId: getUserDCAConfigs
      tags: [DCA]
      summary: Get all DCA configs for a user
      description: |
        Returns all DCA config IDs (any status) belonging to the given wallet address.
        No payment required.
      parameters:
        - name: userAddress
          in: path
          required: true
          description: EVM wallet address
          schema:
            type: string
            pattern: "^0x[a-fA-F0-9]{40}$"
      responses:
        "200":
          description: User's DCA config IDs
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserDCAConfigsResponse"
        "400":
          description: Invalid address
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Failed to fetch from chain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/goat/dca/activate:
    post:
      operationId: activateGoatDCAStrategy
      tags: [GOAT]
      summary: Activate DCA automation on GOAT Testnet3 (x402-gated)
      description: |
        Pays for a DCA config on GOAT Network Testnet3. tokenIn/tokenOut are
        fixed (dUSDC → WGBTC, fee tier 3000) against a real, live Uniswap V3
        Core pool — see /goat-research/07-testnet3-deployment.md for the
        deployment record and addresses.

        **This endpoint requires x402 payment in USDC on Base Sepolia
        (eip155:84532) — the payment rail is unchanged; only DCA execution
        moves to GOAT.**

        ### Flow (no Reactive Network)
        1. Agent calls this endpoint with x402 payment (price based on `duration`)
        2. Server creates the DCA config on-chain on GOAT Testnet3
        3. Server returns `configId`, tx hash, and token approval instructions
        4. Agent approves the DCA contract to spend their `dUSDC` on GOAT Testnet3
        5. A permissionless on-chain function (`executeDCAOrders()`) executes
           swaps on a ~60s poll — this server's scheduler is the default
           caller, but it is NOT a privileged relay; anyone could call the
           same function and collect the same bounty (if funded)
      security:
        - x402: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GoatDCAActivateRequest"
            examples:
              dUsdcToWgbtc:
                summary: DCA 1 dUSDC into WGBTC, 5 swaps, every 60s
                value:
                  user: "0xYourWalletAddress"
                  amountPerSwap: "1000000"
                  totalSwaps: 5
                  swapInterval: 60
                  minAmountOut: "0"
                  duration: 3600
      responses:
        "200":
          description: GOAT DCA config created successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GoatDCAActivateResponse"
        "400":
          description: Invalid parameters
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidationErrorResponse"
        "402":
          description: Payment required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentRequiredResponse"
        "503":
          description: GOAT deployer wallet underfunded (needs testnet BTC for gas)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/goat/dca/pause:
    post:
      operationId: pauseGoatDCAConfig
      tags: [GOAT]
      summary: Pause a GOAT DCA config
      description: No payment required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfigIdRequest"
      responses:
        "200":
          description: Config paused
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigActionResponse"
        "400":
          description: Invalid configId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: On-chain transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/goat/dca/resume:
    post:
      operationId: resumeGoatDCAConfig
      tags: [GOAT]
      summary: Resume a paused GOAT DCA config
      description: No payment required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfigIdRequest"
      responses:
        "200":
          description: Config resumed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigActionResponse"
        "400":
          description: Invalid configId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: On-chain transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/goat/dca/cancel:
    post:
      operationId: cancelGoatDCAConfig
      tags: [GOAT]
      summary: Cancel a GOAT DCA config permanently
      description: Irreversible. No payment required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfigIdRequest"
      responses:
        "200":
          description: Config cancelled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigActionResponse"
        "400":
          description: Invalid configId
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: On-chain transaction failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/goat/dca/config/{configId}:
    get:
      operationId: getGoatDCAConfig
      tags: [GOAT]
      summary: Get GOAT DCA config details
      description: |
        Fetches on-chain state directly from DCAStrategyCallbackGoat on GOAT
        Testnet3. No payment required.
      parameters:
        - name: configId
          in: path
          required: true
          schema:
            type: integer
            minimum: 0
      responses:
        "200":
          description: GOAT DCA config details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GoatDCAConfigResponse"
        "400":
          description: Invalid config ID
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Failed to fetch from chain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/goat/dca/configs:
    get:
      operationId: listActiveGoatDCAConfigs
      tags: [GOAT]
      summary: List all active GOAT DCA config IDs
      description: No payment required.
      responses:
        "200":
          description: Active GOAT DCA config IDs
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActiveConfigsResponse"
        "500":
          description: Failed to fetch from chain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/goat/dca/user/{userAddress}:
    get:
      operationId: getUserGoatDCAConfigs
      tags: [GOAT]
      summary: Get all GOAT DCA configs for a user
      description: No payment required.
      parameters:
        - name: userAddress
          in: path
          required: true
          schema:
            type: string
            pattern: "^0x[a-fA-F0-9]{40}$"
      responses:
        "200":
          description: User's GOAT DCA config IDs
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserDCAConfigsResponse"
        "400":
          description: Invalid address
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Failed to fetch from chain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/status/configs:
    get:
      operationId: listActiveConfigs
      tags: [Status]
      summary: List all active protection config IDs
      description: |
        Returns all currently active (non-cancelled, non-paused) protection config IDs
        from the on-chain contract. No payment required.
      responses:
        "200":
          description: List of active config IDs
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ActiveConfigsResponse"
        "500":
          description: Failed to fetch from chain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

components:
  securitySchemes:
    x402:
      type: apiKey
      in: header
      name: X-PAYMENT
      description: |
        Base64-encoded JSON containing a signed EIP-3009 `transferWithAuthorization`
        over USDC on Base Sepolia. Signed locally by the agent's private key using the
        x402 SDK — no separate facilitator call needed by the client. The facilitator
        is called server-side to verify and settle the payment.
        See https://x402.org/docs for SDK helpers (`@x402/fetch`, `@x402/evm`).

  schemas:
    # ── Request schemas ──────────────────────────────────────────────────────────

    QuoteRequest:
      type: object
      required: [service, durationSeconds]
      properties:
        service:
          type: string
          description: Service ID from the catalog
          enum: [aave-protection]
          example: aave-protection
        durationSeconds:
          type: integer
          description: Desired protection duration in seconds (min 3600, max 2592000)
          minimum: 3600
          maximum: 2592000
          example: 86400

    CreateProtectionRequest:
      type: object
      required:
        - protectedUser
        - protectionType
        - healthFactorThreshold
        - targetHealthFactor
      properties:
        protectedUser:
          type: string
          pattern: "^0x[a-fA-F0-9]{40}$"
          description: Wallet address whose Aave position to protect
          example: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
        protectionType:
          type: integer
          enum: [0, 1, 2]
          description: |
            Protection strategy:
            - `0` = COLLATERAL_DEPOSIT — supply more collateral to raise HF
            - `1` = DEBT_REPAYMENT — repay debt to raise HF
            - `2` = BOTH — try collateral first, fall back to debt repayment
          example: 0
        healthFactorThreshold:
          type: string
          pattern: "^\\d+$"
          description: |
            Health factor trigger threshold in WAD (18 decimals).
            Protection fires when HF drops below this value.
            Example: "1500000000000000000" = 1.5 HF
          example: "1500000000000000000"
        targetHealthFactor:
          type: string
          pattern: "^\\d+$"
          description: |
            Target health factor after protection in WAD (18 decimals).
            The contract will supply/repay enough to reach this level.
            Example: "2000000000000000000" = 2.0 HF
          example: "2000000000000000000"
        collateralAsset:
          type: string
          pattern: "^0x[a-fA-F0-9]{40}$"
          description: |
            ERC-20 address of collateral to supply (used for protectionType 0 or 2).
            Defaults to WETH on Base Sepolia: `0x4200000000000000000000000000000000000006`
          default: "0x4200000000000000000000000000000000000006"
          example: "0x4200000000000000000000000000000000000006"
        debtAsset:
          type: string
          pattern: "^0x[a-fA-F0-9]{40}$"
          description: |
            ERC-20 address of debt token to repay (used for protectionType 1 or 2).
            Defaults to USDC on Base Sepolia: `0xba50Cd2A20f6DA35D788639E581bca8d0B5d4D5f` (Aave's USDC on Base Sepolia — different from x402 payment USDC)
          default: "0xba50Cd2A20f6DA35D788639E581bca8d0B5d4D5f"
          example: "0xba50Cd2A20f6DA35D788639E581bca8d0B5d4D5f"
        preferDebtRepayment:
          type: boolean
          description: |
            When protectionType is BOTH (2), prefer debt repayment over collateral deposit.
            Has no effect for protectionType 0 or 1.
          default: false
          example: false
        duration:
          type: integer
          description: |
            Protection duration in seconds. Determines the x402 payment amount.
            Min: 3600 (1 hour), Max: 2592000 (30 days). Defaults to 86400 (1 day).
          minimum: 3600
          maximum: 2592000
          default: 86400
          example: 86400

    ConfigIdRequest:
      type: object
      required: [configId]
      properties:
        configId:
          type: integer
          minimum: 0
          description: Numeric protection config ID
          example: 42

    # ── Response schemas ─────────────────────────────────────────────────────────

    HealthResponse:
      type: object
      properties:
        status:
          type: string
          enum: [ok, degraded, error]
          description: "`ok` = fully operational, `degraded` = RC underfunded, `error` = unreachable"
          example: ok
        reactiveContractBalance:
          type: string
          description: REACT balance of the Reactive Contract on Lasna (in wei), or "unreachable"
          example: "50000000000000000"
        reactiveContractFunded:
          oneOf:
            - type: boolean
            - type: string
              enum: [unknown]
          description: Whether the RC has enough REACT for callbacks to fire (min 0.01 REACT)
          example: true

    ServicesResponse:
      type: object
      properties:
        services:
          type: array
          items:
            $ref: "#/components/schemas/ServiceCatalogEntry"

    ServiceCatalogEntry:
      type: object
      properties:
        id:
          type: string
          example: aave-protection
        name:
          type: string
          example: Aave Liquidation Protection
        description:
          type: string
        trigger:
          type: string
          example: "Aave Health Factor < threshold"
        action:
          type: string
          example: "Supply collateral, repay debt, or both on your behalf"
        pricing:
          type: object
          properties:
            perDay:
              type: string
              description: Human-readable price per day
              example: "$0.25"
            perDayBaseUnits:
              type: integer
              description: Price per day in USDC base units (6 decimals)
              example: 250000
            example1Day:
              type: string
              description: Total price for 1 day (with gas buffer)
              example: "$0.3"
            example7Days:
              type: string
              description: Total price for 7 days (with gas buffer)
              example: "$2.1"
        limits:
          type: object
          properties:
            minDurationSeconds:
              type: integer
              example: 3600
            maxDurationSeconds:
              type: integer
              example: 2592000
        network:
          type: string
          description: CAIP-2 network identifier
          example: eip155:84532
        status:
          type: string
          example: live

    QuoteResponse:
      type: object
      properties:
        service:
          type: string
          example: aave-protection
        durationSeconds:
          type: integer
          example: 86400
        price:
          type: string
          description: Human-readable total price
          example: "$0.3"
        priceBaseUnits:
          type: string
          description: Total price in USDC base units (6 decimals) — use this for EIP-3009 authorization
          example: "300000"
        currency:
          type: string
          example: USDC
        network:
          type: string
          example: eip155:84532

    CreateProtectionResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        configId:
          type: string
          description: Numeric ID of the newly created on-chain config (store this for management)
          example: "42"
        txHash:
          type: string
          description: Base Sepolia transaction hash for the config creation
          example: "0xabc123..."
        protectedUser:
          type: string
          example: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
        protectionType:
          type: integer
          example: 0
        healthFactorThreshold:
          type: string
          example: "1500000000000000000"
        targetHealthFactor:
          type: string
          example: "2000000000000000000"
        collateralAsset:
          type: string
          example: "0x4200000000000000000000000000000000000006"
        debtAsset:
          type: string
          example: "0xba50Cd2A20f6DA35D788639E581bca8d0B5d4D5f"
        preferDebtRepayment:
          type: boolean
          example: false
        message:
          type: string
          description: Human-readable status message
          example: "Protection config #42 active. Health factor monitored every ~12 min."
        nextSteps:
          type: array
          items:
            type: string
          description: Required follow-up actions (token approvals)

    ConfigActionResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        configId:
          type: integer
          example: 42
        txHash:
          type: string
          description: Base Sepolia transaction hash
          example: "0xabc123..."
        action:
          type: string
          enum: [paused, resumed, cancelled]
          example: paused

    ProtectionConfigResponse:
      type: object
      properties:
        configId:
          type: string
          example: "42"
        protectedUser:
          type: string
          example: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
        protectionType:
          type: integer
          enum: [0, 1, 2]
          description: "0=COLLATERAL_DEPOSIT, 1=DEBT_REPAYMENT, 2=BOTH"
          example: 0
        healthFactorThreshold:
          type: string
          description: WAD-encoded HF trigger threshold
          example: "1500000000000000000"
        targetHealthFactor:
          type: string
          description: WAD-encoded target HF after protection
          example: "2000000000000000000"
        collateralAsset:
          type: string
          example: "0x4200000000000000000000000000000000000006"
        debtAsset:
          type: string
          example: "0xba50Cd2A20f6DA35D788639E581bca8d0B5d4D5f"
        preferDebtRepayment:
          type: boolean
          example: false
        status:
          type: string
          enum: [Active, Paused, Cancelled]
          example: Active
        createdAt:
          type: integer
          description: Unix timestamp of config creation
          example: 1710000000
        expiresAt:
          type: integer
          nullable: true
          description: Unix timestamp when the protection auto-expires, or null if no expiry
          example: 1710086400
        lastExecutedAt:
          type: integer
          description: Unix timestamp of last successful protection execution (0 if never)
          example: 0
        executionCount:
          type: integer
          description: Total number of times protection has been triggered and executed
          example: 0
        consecutiveFailures:
          type: integer
          description: Number of consecutive failed execution attempts
          example: 0

    HealthFactorResponse:
      type: object
      properties:
        userAddress:
          type: string
          example: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
        healthFactor:
          type: string
          description: WAD-encoded health factor, or "MAX" if no Aave position
          example: "1850000000000000000"
        healthFactorDecimal:
          type: string
          nullable: true
          description: Human-readable health factor (4 decimal places), null if no position
          example: "1.8500"
        atRisk:
          type: boolean
          description: True when health factor is below 1.5
          example: false
        noAavePosition:
          type: boolean
          description: True when the user has no active Aave position on Base Sepolia
          example: false

    ActiveConfigsResponse:
      type: object
      properties:
        activeConfigIds:
          type: array
          items:
            type: string
          description: String-encoded config IDs that are currently active
          example: ["1", "5", "42"]
        count:
          type: integer
          description: Total number of active configs
          example: 3

    PaymentRequiredResponse:
      type: object
      description: x402 payment required response. Attach payment to X-PAYMENT header and retry.
      properties:
        x402Version:
          type: integer
          example: 2
        accepts:
          type: array
          description: Supported payment options
          items:
            type: object
            properties:
              scheme:
                type: string
                example: exact
              network:
                type: string
                example: eip155:84532
              payTo:
                type: string
                description: Recipient wallet address
                example: "0xServerWallet..."
              amount:
                type: string
                description: Required USDC amount in base units (6 decimals)
                example: "300000"
              asset:
                type: string
                description: USDC token address on Base Sepolia
                example: "0xba50Cd2A20f6DA35D788639E581bca8d0B5d4D5f"
              extra:
                type: object
                properties:
                  name:
                    type: string
                    example: USDC
                  version:
                    type: string
                    example: "2"
        error:
          type: string
          example: "Payment required"

    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Error message
          example: "On-chain config creation failed"
        reason:
          type: string
          description: Detailed reason (often from the on-chain revert)
          example: "execution reverted: insufficient balance"

    PermitRequest:
      type: object
      required: [token, owner, spender, value, deadline, v, r, s]
      properties:
        token:
          type: string
          pattern: "^0x[a-fA-F0-9]{40}$"
          description: ERC-20 token address (must support EIP-2612; USDC only — not WETH)
          example: "0xba50Cd2A20f6DA35D788639E581bca8d0B5d4D5f"
        owner:
          type: string
          pattern: "^0x[a-fA-F0-9]{40}$"
          description: Agent wallet address (the token holder signing the permit)
          example: "0xYourWalletAddress"
        spender:
          type: string
          pattern: "^0x[a-fA-F0-9]{40}$"
          description: Address being approved — should be the AaveProtectionCallback address
          example: "0x24df0bBC9c4b95e8643848EC6B7f0Ac638BD3476"
        value:
          type: string
          pattern: "^\\d+$"
          description: Amount of tokens to approve (in base units, bigint as string)
          example: "1000000000"
        deadline:
          type: integer
          description: Unix timestamp after which the permit is invalid
          example: 1712000000
        v:
          type: integer
          minimum: 0
          maximum: 255
          description: ECDSA signature v component
          example: 27
        r:
          type: string
          pattern: "^0x[a-fA-F0-9]{64}$"
          description: ECDSA signature r component (32 bytes hex)
          example: "0xabc123..."
        s:
          type: string
          pattern: "^0x[a-fA-F0-9]{64}$"
          description: ECDSA signature s component (32 bytes hex)
          example: "0xdef456..."

    PermitResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        txHash:
          type: string
          description: Base Sepolia transaction hash of the permit call
          example: "0xabc123..."

    ValidationErrorResponse:
      type: object
      properties:
        error:
          type: string
          example: "Invalid parameters"
        details:
          type: object
          description: Per-field validation errors
          additionalProperties:
            type: array
            items:
              type: string
          example:
            protectedUser: ["Invalid address format"]
            protectionType: ["Expected number, received string"]

    # ── DCA Strategy schemas ──────────────────────────────────────────────────

    DCAActivateRequest:
      type: object
      required:
        - user
        - tokenIn
        - tokenOut
        - amountPerSwap
        - poolFee
      properties:
        user:
          type: string
          pattern: "^0x[a-fA-F0-9]{40}$"
          description: Wallet address whose tokens will be swapped (the agent)
          example: "0xYourWalletAddress"
        tokenIn:
          type: string
          pattern: "^0x[a-fA-F0-9]{40}$"
          description: Token to sell on each swap
          example: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
        tokenOut:
          type: string
          pattern: "^0x[a-fA-F0-9]{40}$"
          description: Token to buy on each swap
          example: "0x4200000000000000000000000000000000000006"
        amountPerSwap:
          type: string
          pattern: "^\\d+$"
          description: Amount of tokenIn per swap (in token base units, e.g., "10000000" = 10 USDC)
          example: "10000000"
        poolFee:
          type: integer
          enum: [500, 3000, 10000]
          description: "Uniswap V3 pool fee tier: 500 (0.05%), 3000 (0.3%), 10000 (1%)"
          example: 3000
        totalSwaps:
          type: integer
          minimum: 0
          description: Total swaps to execute (0 = unlimited, runs until expiry or cancel)
          default: 0
          example: 10
        swapInterval:
          type: integer
          minimum: 60
          description: Minimum seconds between swaps (min 60)
          default: 720
          example: 720
        minAmountOut:
          type: string
          pattern: "^\\d+$"
          description: Minimum tokenOut per swap for slippage protection (0 = no limit)
          default: "0"
          example: "0"
        duration:
          type: integer
          minimum: 3600
          maximum: 2592000
          description: |
            Duration in seconds. Determines the x402 payment amount and how long
            the Reactive Network will run CRON callbacks.
            Min: 3600 (1 hour), Max: 2592000 (30 days). Defaults to 86400 (1 day).
          default: 86400
          example: 86400

    DCAActivateResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        configId:
          type: string
          description: Numeric ID of the newly created on-chain DCA config
          example: "0"
        txHash:
          type: string
          description: Base Sepolia transaction hash for the config creation
          example: "0xabc123..."
        user:
          type: string
          example: "0xYourWalletAddress"
        tokenIn:
          type: string
          example: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
        tokenOut:
          type: string
          example: "0x4200000000000000000000000000000000000006"
        amountPerSwap:
          type: string
          example: "10000000"
        poolFee:
          type: integer
          example: 3000
        totalSwaps:
          type: integer
          example: 10
        swapInterval:
          type: integer
          example: 720
        minAmountOut:
          type: string
          example: "0"
        message:
          type: string
          example: "DCA config #0 active. Swaps execute every ~720s."
        nextSteps:
          type: array
          items:
            type: string
          description: Required follow-up actions (token approvals)

    DCAConfigResponse:
      type: object
      properties:
        configId:
          type: string
          example: "0"
        user:
          type: string
          description: Wallet address that created the config (the agent)
          example: "0xAgentWallet..."
        tokenIn:
          type: string
          description: Token being sold on each swap
          example: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
        tokenOut:
          type: string
          description: Token being bought on each swap
          example: "0x4200000000000000000000000000000000000006"
        amountPerSwap:
          type: string
          description: Amount of tokenIn per swap (in token base units)
          example: "10000000"
        poolFee:
          type: integer
          description: "Uniswap V3 fee tier: 500, 3000, or 10000"
          example: 3000
        totalSwaps:
          type: string
          description: Total swaps to execute (0 = unlimited)
          example: "10"
        swapsExecuted:
          type: string
          description: Number of swaps completed so far
          example: "3"
        totalAmountOut:
          type: string
          description: Cumulative tokenOut received across all swaps (in token base units)
          example: "12000000000000000"
        swapInterval:
          type: string
          description: Minimum seconds between swaps
          example: "720"
        minAmountOut:
          type: string
          description: Minimum tokenOut per swap (slippage protection)
          example: "0"
        status:
          type: string
          enum: [Active, Paused, Cancelled, Completed]
          example: Active
        createdAt:
          type: integer
          description: Unix timestamp of config creation
          example: 1710000000
        expiresAt:
          type: integer
          nullable: true
          description: Unix timestamp when the config auto-expires, or null if no expiry
        lastSwapAt:
          type: integer
          description: Unix timestamp of last successful swap (0 if never)
          example: 1710001400
        consecutiveFailures:
          type: integer
          description: Number of consecutive failed swap attempts (auto-cancels at 5)
          example: 0

    UserDCAConfigsResponse:
      type: object
      properties:
        userAddress:
          type: string
          example: "0xAgentWallet..."
        configIds:
          type: array
          items:
            type: string
          description: All DCA config IDs belonging to this user (any status)
          example: ["0", "3", "7"]

    GoatDCAActivateRequest:
      type: object
      required:
        - user
        - amountPerSwap
      properties:
        user:
          type: string
          pattern: "^0x[a-fA-F0-9]{40}$"
          description: Wallet address whose dUSDC will be swapped (the agent)
          example: "0xYourWalletAddress"
        amountPerSwap:
          type: string
          pattern: "^\\d+$"
          description: Amount of dUSDC per swap (6 decimals, e.g. "1000000" = 1 dUSDC). tokenIn/tokenOut/poolFee are fixed (dUSDC → WGBTC, fee 3000).
          example: "1000000"
        totalSwaps:
          type: integer
          minimum: 0
          description: Total swaps to execute (0 = unlimited, runs until expiry or cancel)
          default: 0
          example: 5
        swapInterval:
          type: integer
          minimum: 60
          description: Minimum seconds between swaps (min 60 — matches the scheduler's poll interval)
          default: 60
          example: 60
        minAmountOut:
          type: string
          pattern: "^\\d+$"
          description: Minimum WGBTC per swap for slippage protection (0 = no limit)
          default: "0"
          example: "0"
        duration:
          type: integer
          minimum: 3600
          maximum: 2592000
          description: Duration in seconds. Determines the x402 payment amount.
          default: 86400
          example: 3600

    GoatDCAActivateResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        configId:
          type: string
          example: "0"
        txHash:
          type: string
          description: GOAT Testnet3 transaction hash for the config creation
          example: "0x150a1e30..."
        network:
          type: string
          example: "eip155:48816"
        user:
          type: string
          example: "0xYourWalletAddress"
        tokenIn:
          type: string
          description: dUSDC (testnet-only demo token) address on GOAT Testnet3
          example: "0xF35b99BaE312FD59145F5eBE4482fD433d1C7E20"
        tokenOut:
          type: string
          description: WGBTC (canonical GOAT wrapped-native predeploy) address
          example: "0xbC10000000000000000000000000000000000000"
        amountPerSwap:
          type: string
          example: "1000000"
        message:
          type: string
          example: "GOAT DCA config #0 active. A permissionless scheduler polls every ~60s."
        nextSteps:
          type: array
          items:
            type: string

    GoatDCAConfigResponse:
      type: object
      properties:
        configId:
          type: string
          example: "0"
        user:
          type: string
          example: "0xAgentWallet..."
        tokenIn:
          type: string
          example: "0xF35b99BaE312FD59145F5eBE4482fD433d1C7E20"
        tokenOut:
          type: string
          example: "0xbC10000000000000000000000000000000000000"
        amountPerSwap:
          type: string
          example: "1000000"
        totalSwaps:
          type: string
          example: "5"
        swapsExecuted:
          type: string
          example: "1"
        totalAmountOut:
          type: string
          description: Cumulative WGBTC received across all swaps (18 decimals)
          example: "83772080973"
        status:
          type: string
          enum: [Active, Paused, Cancelled, Completed]
          example: Completed
        createdAt:
          type: integer
        expiresAt:
          type: integer
          nullable: true
        lastSwapAt:
          type: integer
        count:
          type: integer
          example: 3
