openapi: 3.0.3
info:
  title: Tansy API (ACA 2026) — PostgREST RPC
  version: "2026.1"
  description: |
    Read-only ACA 2026 prescription drug coverage API via PostgREST RPC.

    All endpoints follow the pattern:
      POST /rest/v1/rpc/{function_name}

servers:
  - url: https://api.tansyapi.com
    description: Production

tags:
  - name: RPC
    description: PostgREST RPC functions

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: apikey
      description: Supabase anon API key
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Use the same anon key as a Bearer token

  schemas:
    DrugSearchRequest:
      type: object
      required: [p_query, p_limit]
      properties:
        p_query:
          type: string
          example: metformin
        p_limit:
          type: integer
          example: 10

    DrugSearchRow:
      type: object
      properties:
        rxnorm:
          type: string
          example: "860975"
        canonical_name:
          type: string
          example: Metformin 500 MG Oral Tablet
        canonical_name_count:
          type: integer
          example: 12
        sim:
          type: number
          format: float
          example: 0.82

    PlanDrugBatchRequest:
      type: object
      required: [p_plan_id, p_rxnorms]
      properties:
        p_plan_id:
          type: string
          example: "38246LA0010001"
        p_rxnorms:
          type: array
          items:
            type: string
          example: ["310004", "310005"]

    CoverageCoreRequest:
      type: object
      required: [p_plan_id, p_rxnorm]
      properties:
        p_plan_id:
          type: string
          example: "38246LA0010001"
        p_rxnorm:
          type: string
          example: "310004"

    PlansByRxnormStateRequest:
      type: object
      required: [p_rxnorm, p_state]
      properties:
        p_rxnorm:
          type: string
          example: "310004"
        p_state:
          type: string
          description: Two-letter state code
          example: "LA"
        p_limit:
          type: integer
          example: 50

    DrugSearchInPlanRequest:
      type: object
      required: [p_plan_id, p_query]
      properties:
        p_plan_id:
          type: string
          example: "38246LA0010001"
        p_query:
          type: string
          example: "metf"
        p_limit:
          type: integer
          example: 20

    CoverageRowCore:
      type: object
      properties:
        plan_year:
          type: integer
          example: 2026
        issuer_id:
          type: string
          example: "38246"
        state:
          type: string
          example: "LA"
        plan_id:
          type: string
          example: "38246LA0010001"
        marketing_name:
          type: string
          example: "Example Silver Plan"
        rxnorm:
          type: string
          example: "310004"
        drug_tier:
          type: string
          nullable: true
          example: "Tier 1"
        prior_authorization:
          type: boolean
          nullable: true
          example: false
        step_therapy:
          type: boolean
          nullable: true
          example: false
        quantity_limit:
          type: boolean
          nullable: true
          example: true
        variant_count:
          type: integer
          nullable: true
          example: 1
        coverage_varies:
          type: boolean
          nullable: true
          example: false

    CoverageRowCost:
      allOf:
        - $ref: '#/components/schemas/CoverageRowCore'
        - type: object
          properties:
            drug_tier_norm:
              type: string
              nullable: true
              example: "1"
            copay_status:
              type: string
              nullable: true
              example: "copay"
            pharmacy_type:
              type: string
              nullable: true
              example: "retail"
            copay_amount:
              type: number
              nullable: true
              example: 10
            copay_opt:
              type: string
              nullable: true
              example: "$"
            coinsurance_rate:
              type: number
              nullable: true
              example: 0.2
            coinsurance_opt:
              type: string
              nullable: true
              example: "%"
            has_cost_row:
              type: boolean
              nullable: true
              example: true
            cost_resolution:
              type: string
              nullable: true
              example: "matched_single_cost_row"

    CoverageVariantsRow:
      type: object
      properties:
        plan_year:
          type: integer
          example: 2026
        plan_id:
          type: string
          example: "38246LA0010001"
        rxnorm:
          type: string
          example: "310004"
        drug_tier:
          type: string
          nullable: true
          example: "Tier 1"
        prior_authorization:
          type: boolean
          nullable: true
          example: false
        step_therapy:
          type: boolean
          nullable: true
          example: false
        quantity_limit:
          type: boolean
          nullable: true
          example: true
        n:
          type: integer
          example: 2
        issuer_states:
          type: string
          example: "38246:LA, 38246:MS"

security:
  - ApiKeyAuth: []
    BearerAuth: []

paths:
  /rest/v1/rpc/rpc_drug_search_2026:
    post:
      tags: [RPC]
      summary: Drug search (fuzzy)
      description: Search drugs by name and return RxNorm + canonical name.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DrugSearchRequest'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/DrugSearchRow'

  /rest/v1/rpc/rpc_plan_drug_batch_2026:
    post:
      tags: [RPC]
      summary: Coverage + cost for multiple drugs in a plan
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlanDrugBatchRequest'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/CoverageRowCost'

  /rest/v1/rpc/rpc_coverage_lookup_core_2026:
    post:
      tags: [RPC]
      summary: Coverage lookup (core)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CoverageCoreRequest'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/CoverageRowCore'

  /rest/v1/rpc/rpc_coverage_lookup_cost_2026:
    post:
      tags: [RPC]
      summary: Coverage lookup (cost)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CoverageCoreRequest'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/CoverageRowCost'

  /rest/v1/rpc/rpc_coverage_variants_2026:
    post:
      tags: [RPC]
      summary: Coverage variants detector
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CoverageCoreRequest'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/CoverageVariantsRow'

  /rest/v1/rpc/rpc_plans_by_rxnorm_state_2026:
    post:
      tags: [RPC]
      summary: Plans that cover a drug in a state
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlansByRxnormStateRequest'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/CoverageRowCore'

  /rest/v1/rpc/rpc_drug_search_in_plan:
    post:
      tags: [RPC]
      summary: Drug search within a plan
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DrugSearchInPlanRequest'
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    rxnorm: { type: string, example: "310004" }
                    canonical_name: { type: string, example: "Metformin 500 MG Oral Tablet" }
                    sim: { type: number, format: float, example: 0.81 }
                    drug_tier: { type: string, nullable: true, example: "Tier 1" }
                    prior_authorization: { type: boolean, nullable: true, example: false }
                    step_therapy: { type: boolean, nullable: true, example: false }
                    quantity_limit: { type: boolean, nullable: true, example: true }
