openapi: 3.0.3
info:
  title: pigi.finance Vault Intelligence API
  version: "1.0.0"
  description: >
    Programmatic access to pigi.finance DeFi vault data: APR, APY, TVL
    (with 30-day moving averages) across daily and weekly/monthly/quarterly/yearly
    windows, plus holder counts and DeFi/T-Bill reference rates. Authenticate by exchanging your API key for a
    short-lived JWT, then send `Authorization: Bearer <JWT>` on every request.
servers:
  - url: https://pigi.finance/api/v1
tags:
  - name: meta
  - name: auth
  - name: vaults
  - name: rates
  - name: hacks
  - name: account
paths:
  /:
    get:
      tags: [meta]
      summary: API discovery index
      description: Version, docs link, and the list of available endpoints. No auth.
      security: []
      responses:
        "200":
          description: Discovery document
          content:
            application/json:
              schema:
                type: object
                properties:
                  version: { type: string, example: v1 }
                  docs: { type: string, example: /api-docs/openapi.yaml }
                  auth: { type: string }
                  endpoints:
                    type: array
                    items: { type: string }
  /auth/token:
    post:
      tags: [auth]
      summary: Exchange an API key for a short-lived JWT
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [apiKey]
              properties:
                apiKey:
                  type: string
                  example: pigi_live_xxxxxxxxxxxxxxxxxxxxxxxx
      responses:
        "200":
          description: A bearer token
          content:
            application/json:
              schema:
                type: object
                properties:
                  token: { type: string }
                  expiresIn: { type: integer, description: seconds }
                  plan: { type: string }
        "400": { description: missing_api_key }
        "401": { description: invalid_api_key }
  /vaults:
    get:
      tags: [vaults]
      summary: List vaults
      parameters:
        - { name: protocol_name, in: query, schema: { type: string } }
        - { name: chain_id, in: query, schema: { type: string } }
        - { name: strategy_id, in: query, schema: { type: string } }
        - { name: tvl_filter, in: query, schema: { type: string, enum: [gte_1m, gte_2m, gte_5m, gte_10m, non_na, na] } }
        - { name: apr_filter, in: query, schema: { type: string, enum: [lte_10, gt_5, gt_10, gt_15, gt_20, non_na, na] } }
        - { name: age_filter, in: query, schema: { type: string, enum: [new, 3mo, 6mo, 12mo, 18mo, 24mo, non_na, na] } }
        - { name: limit, in: query, schema: { type: integer, default: 100, maximum: 1000 } }
        - { name: offset, in: query, schema: { type: integer, default: 0 } }
      responses:
        "200":
          description: A page of vaults
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Vault" }
                  pagination:
                    type: object
                    properties:
                      total: { type: integer }
                      limit: { type: integer }
                      offset: { type: integer }
                      hasMore: { type: boolean }
                  availableFilters:
                    type: object
                    properties:
                      protocol_names: { type: array, items: { type: string } }
                      chain_ids: { type: object, additionalProperties: { type: array, items: { type: integer } } }
                      all_chain_ids: { type: array, items: { type: integer } }
        "401": { description: invalid_token }
  /vaults/{id}:
    get:
      tags: [vaults]
      summary: Get a single vault by pool id
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer } }
      responses:
        "200":
          description: The vault
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: "#/components/schemas/Vault" }
        "404": { description: not_found }
  /vaults/{id}/history:
    get:
      tags: [vaults]
      summary: Daily time-series for a vault (by strategy id)
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer }, description: strategy id }
        - { name: range, in: query, schema: { type: string, enum: ["7D", "30D", "90D", "180D"], default: "7D" } }
      responses:
        "200":
          description: Time-series + latest values
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        timestamp: { type: string }
                        tvl: { type: number }
                        apr: { type: number }
                        apy: { type: number }
                        ra_apr: { type: number, nullable: true, description: "risk-adjusted APR: apr minus a penalty derived from the vault's published overall risk score (0-100, higher = safer; piecewise-linear); null when the vault has no published assessment" }
                        tvl_30d_ma: { type: number }
                        apr_30d_ma: { type: number }
                        apy_30d_ma: { type: number }
                  lastTvl: { type: number }
                  lastApr: { type: number }
                  lastApy: { type: number }
                  lastRaApr: { type: number, nullable: true }
                  lastTvl30dMa: { type: number }
                  lastApr30dMa: { type: number }
                  lastApy30dMa: { type: number }
                  lastRaApr30dMa: { type: number, nullable: true }
  /vaults/{id}/stats:
    get:
      tags: [vaults]
      summary: Windowed stats for a vault (by strategy id)
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer }, description: strategy id }
        - { name: period, in: query, schema: { type: string, enum: [weekly, monthly, quarterly, yearly, lifetime] } }
      responses:
        "200":
          description: Windowed stats (all windows if period omitted)
          content:
            application/json:
              schema:
                type: object
                properties:
                  weekly: { $ref: "#/components/schemas/PeriodStats" }
                  monthly: { $ref: "#/components/schemas/PeriodStats" }
                  quarterly: { $ref: "#/components/schemas/PeriodStats" }
                  yearly: { $ref: "#/components/schemas/PeriodStats" }
                  lifetime: { $ref: "#/components/schemas/PeriodStats" }
  /vaults/{id}/holders:
    get:
      tags: [vaults]
      summary: Holder list for a vault (by strategy id)
      description: >
        The vault's holder list — one entry per holder ({ address, balance,
        share_pct }). (Vault.holders exposes just the holder count.)
      parameters:
        - { name: id, in: path, required: true, schema: { type: integer }, description: strategy id }
      responses:
        "200":
          description: Holder list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Holder" }
  /rates:
    get:
      tags: [rates]
      summary: DeFi Base Rate (stablecoin + ETH) and T-Bill risk-free rate history
      description: >
        Daily history of the pigi.finance DeFi Base Rate — the mean yield across
        the tracked stablecoin and ETH vault sets — alongside the 3-month U.S.
        T-Bill risk-free rate. Series are sorted oldest to newest.
      responses:
        "200":
          description: Rate history series
          content:
            application/json:
              schema:
                type: object
                properties:
                  stable:
                    type: array
                    items: { $ref: "#/components/schemas/RatePoint" }
                  eth:
                    type: array
                    items: { $ref: "#/components/schemas/RatePoint" }
                  tbills:
                    type: array
                    items: { $ref: "#/components/schemas/TBillPoint" }
  /hacks:
    get:
      tags: [hacks]
      summary: DeFi and TradFi hack / loss events
      description: >
        Hack and loss events: every tracked DeFi exploit plus a reference set of
        major TradFi banking losses. `summary` covers the entire filtered set (not
        just the returned page), so total losses for a window can be read without
        paging through every event.
      parameters:
        - in: query
          name: category
          schema: { type: string, enum: [defi, tradfi, all], default: all }
        - in: query
          name: type
          description: >
            What kind of protocol was hit. Case-insensitive. DeFi events only —
            TradFi rows carry no type, so this filter excludes them.
          schema: { type: string, enum: [DeFi, Dexes, Bridges, all], default: all }
        - in: query
          name: from
          description: Only events on/after this date (YYYY-MM-DD or ISO 8601).
          schema: { type: string }
        - in: query
          name: to
          description: Only events on/before this date, inclusive of the whole day.
          schema: { type: string }
        - in: query
          name: min_amount
          description: Only events at or above this USD amount.
          schema: { type: number }
        - in: query
          name: sort
          schema:
            type: string
            enum: [date_asc, date_desc, amount_desc, amount_asc]
            default: date_asc
        - in: query
          name: limit
          schema: { type: integer, default: 100, maximum: 1000 }
        - in: query
          name: offset
          schema: { type: integer, default: 0 }
      responses:
        "200":
          description: Hack events, a summary of the filtered set, and pagination
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Hack" }
                  summary:
                    type: object
                    description: Totals over the ENTIRE filtered set, not just the returned page.
                    properties:
                      count: { type: integer }
                      total_amount_hacked: { type: number, description: "Sum of amount_hacked (USD)." }
                      first_date: { type: string, nullable: true }
                      last_date: { type: string, nullable: true }
                  pagination:
                    type: object
                    properties:
                      total: { type: integer }
                      limit: { type: integer }
                      offset: { type: integer }
                      hasMore: { type: boolean }
        "400":
          description: Invalid query parameter
          content:
            application/json:
              schema:
                type: object
                properties:
                  error: { type: string, example: invalid_parameter }
                  detail: { type: string }
  /usage:
    get:
      tags: [account]
      summary: Your request usage for the current period
      responses:
        "200":
          description: Usage counters
          content:
            application/json:
              schema:
                type: object
                properties:
                  clientId: { type: integer }
                  plan: { type: string }
                  period:
                    type: object
                    properties:
                      start: { type: string }
                      end: { type: string }
                  used: { type: integer }
                  limit: { type: integer, nullable: true }
                  remaining: { type: integer, nullable: true }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  schemas:
    Vault:
      type: object
      properties:
        id: { type: integer }
        strategy_id: { type: integer }
        protocol_name: { type: string }
        chain_id: { type: integer }
        pool_name: { type: string }
        pool_address: { type: string }
        asset_address: { type: string, nullable: true }
        type: { type: string }
        tvl_30d_ma: { type: number, nullable: true }
        apr_30d_ma: { type: number, nullable: true }
        holders: { type: integer, nullable: true }
        pool_creation_date: { type: string, nullable: true }
        updated_at: { type: string, nullable: true }
        tvl_flow_1d: { type: number, nullable: true }
        tvl_flow_7d: { type: number, nullable: true }
        apr_trend_1d: { type: number, nullable: true }
        risk_band: { type: string, nullable: true, description: "Published pigi-risk band: A (safest) .. F. null when the vault has not been assessed yet." }
        active_addresses: { type: integer, nullable: true, description: "Count of active holder addresses." }
        deposit_count: { type: integer, nullable: true, description: "Number of deposit events." }
        tvl_concentration_top10pct: { type: number, nullable: true, description: "% of TVL held by the top 10% of holders." }
    PeriodStats:
      type: object
      description: Aggregated risk/return stats for a single time window.
      properties:
        tvl_low: { type: number, nullable: true }
        tvl_high: { type: number, nullable: true }
        apr: { type: number, nullable: true }
        apy: { type: number, nullable: true }
        inflows: { type: number, nullable: true, description: "Net TVL change over the window (last day − first day): positive = net inflow, negative = net outflow. TVL-based, so it also reflects yield earned over the window, not just deposits/withdrawals." }
        period_start: { type: string, nullable: true }
        period_end: { type: string, nullable: true }
        cagr: { type: number, nullable: true, description: "Compound annual growth rate (%) over the window." }
        volatility: { type: number, nullable: true, description: "Return volatility over the window." }
        sharpe: { type: number, nullable: true, description: "Sharpe ratio over the window." }
        sortino: { type: number, nullable: true, description: "Sortino ratio over the window." }
    RatePoint:
      type: object
      properties:
        timestamp: { type: string }
        apr: { type: number, nullable: true }
        apy: { type: number, nullable: true }
        apr_30d_ma: { type: number, nullable: true }
        apy_30d_ma: { type: number, nullable: true }
    TBillPoint:
      type: object
      properties:
        timestamp: { type: string }
        rate: { type: number, nullable: true }
    Holder:
      type: object
      properties:
        address: { type: string }
        balance: { type: number }
        share_pct: { type: number, description: "Share of the vault's TVL held by this address." }
    Hack:
      type: object
      description: A single hack / loss event.
      properties:
        id: { type: integer }
        date: { type: string, description: "ISO 8601 timestamp of the event." }
        name: { type: string, description: "Protocol (DeFi) or institution (TradFi) that suffered the loss." }
        amount_hacked: { type: number, description: "Amount lost, in USD." }
        category: { type: string, enum: [defi, tradfi] }
        type:
          type: string
          enum: [DeFi, Dexes, Bridges]
          description: >
            What kind of protocol was hit: DeFi (lending, yield, staking,
            stablecoins), Dexes, or Bridges. Present on DeFi events only —
            TradFi events are bank failures, which this taxonomy does not
            describe, so they omit the field.
security:
  - bearerAuth: []
