openapi: 3.1.0
info:
  title: APEX Velocity -- Revenue Cycle Management API
  version: 1.0.0
  summary: Unified RCM dashboard, claims lifecycle, revenue analytics, and denial management
  description: |
    OpenAPI contract for APEX Velocity v1 endpoints.
    Velocity consolidates billing, insurance-claim, and payer operations into a
    single branded module for revenue cycle management.

    **Stedi clearinghouse partner** (formal integration 2026-06-18): real-time
    270/271 eligibility, 276/277 claim status, and 837P submission via
    `/v1/clearinghouse/*` routes. Sandbox stub at `/insurance/sandbox/stedi/*`.
servers:
  - url: https://api.parkerapex.com/velocity/v1
    description: Production
  - url: https://sandbox.api.parkerapex.com/velocity/v1
    description: Sandbox
tags:
  - name: Dashboard
  - name: Claims
  - name: Analytics
  - name: Denials
  - name: Clearinghouse
security:
  - OAuth2: []
paths:
  /dashboard:
    get:
      tags: [Dashboard]
      operationId: getVelocityDashboard
      summary: Unified RCM dashboard metrics
      description: |
        Returns high-level revenue cycle KPIs including claims pipeline counts,
        net revenue, AR aging buckets, denial summary, and payer mix.
      security:
        - OAuth2:
            - velocity.dashboard.read
      parameters:
        - $ref: '#/components/parameters/XCorrelationId'
      responses:
        '200':
          description: Dashboard KPIs
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DashboardResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalError'
  /claims:
    get:
      tags: [Claims]
      operationId: listVelocityClaims
      summary: Claims lifecycle with filtering
      description: |
        Returns a filtered, paginated list of claims across the revenue cycle.
      security:
        - OAuth2:
            - velocity.claims.read
      parameters:
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum: [pending, submitted, in_review, approved, denied, paid, appealed]
        - name: payer
          in: query
          required: false
          schema:
            type: string
          description: Payer name substring filter
        - name: from
          in: query
          required: false
          schema:
            type: string
            format: date
          description: Start date (ISO-8601)
        - name: to
          in: query
          required: false
          schema:
            type: string
            format: date
          description: End date (ISO-8601)
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/XCorrelationId'
      responses:
        '200':
          description: Paginated claims list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClaimsListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalError'
  /analytics:
    get:
      tags: [Analytics]
      operationId: getVelocityAnalytics
      summary: Revenue analytics
      description: |
        Returns revenue analytics for a specified period including gross/net
        revenue, collection rates, monthly trend, payer performance, and AR summary.
      security:
        - OAuth2:
            - velocity.analytics.read
      parameters:
        - name: period
          in: query
          required: false
          schema:
            type: string
            enum: [mtd, qtd, ytd, trailing_12]
            default: mtd
          description: Reporting period
        - $ref: '#/components/parameters/XCorrelationId'
      responses:
        '200':
          description: Revenue analytics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnalyticsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalError'
  /denials:
    get:
      tags: [Denials]
      operationId: listVelocityDenials
      summary: Denial management
      description: |
        Returns denial details with actionable triage information, appeal
        deadlines, and recommended actions.
      security:
        - OAuth2:
            - velocity.denials.read
      parameters:
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum: [open, in_appeal, resolved, written_off]
        - name: payer
          in: query
          required: false
          schema:
            type: string
          description: Payer name substring filter
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/XCorrelationId'
      responses:
        '200':
          description: Paginated denials list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DenialsListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalError'
  /v1/clearinghouse/stedi/health:
    get:
      tags: [Clearinghouse]
      operationId: getStediClearinghouseHealth
      summary: Stedi partnership health
      description: |
        Public health report for the Parker Apex / Stedi clearinghouse partnership.
        Used by the developer portal status page and go-live runbooks.
      security: []
      responses:
        '200':
          description: Stedi partnership health (operational or degraded)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StediHealthReport'
        '503':
          description: Stedi not configured or required checks failing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StediHealthReport'
  /v1/clearinghouse/eligibility:
    post:
      tags: [Clearinghouse]
      operationId: postClearinghouseEligibility
      summary: Real-time 270/271 eligibility (Stedi)
      description: Submit a JSON eligibility inquiry; Stedi handles X12 270/271 translation.
      security:
        - OAuth2:
            - velocity.clearinghouse.eligibility
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EligibilityRequest'
      responses:
        '200':
          description: 271 eligibility response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClearinghouseResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '502':
          description: Stedi upstream error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClearinghouseResult'
        '503':
          description: Stedi not configured
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClearinghouseResult'
  /v1/clearinghouse/claims/status:
    post:
      tags: [Clearinghouse]
      operationId: postClearinghouseClaimStatus
      summary: Real-time 276/277 claim status (Stedi)
      security:
        - OAuth2:
            - velocity.clearinghouse.claims.read
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClaimStatusRequest'
      responses:
        '200':
          description: 277 claim status response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClearinghouseResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '502':
          $ref: '#/components/responses/BadRequest'
        '503':
          $ref: '#/components/responses/InternalError'
  /v1/clearinghouse/claims/submit:
    post:
      tags: [Clearinghouse]
      operationId: postClearinghouseClaimSubmit
      summary: Submit 837P professional claim (Stedi)
      security:
        - OAuth2:
            - velocity.clearinghouse.claims.write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClaimSubmitRequest'
      responses:
        '200':
          description: Claim submission acknowledgment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClearinghouseResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '502':
          $ref: '#/components/responses/BadRequest'
        '503':
          $ref: '#/components/responses/InternalError'
components:
  securitySchemes:
    OAuth2:
      type: oauth2
      description: Plexus OAuth2 bearer token.
      flows:
        authorizationCode:
          authorizationUrl: https://nexus.parkerapex.com/oauth/authorize
          tokenUrl: https://nexus.parkerapex.com/oauth/token
          scopes:
            velocity.dashboard.read: Read RCM dashboard metrics
            velocity.claims.read: Read claims lifecycle data
            velocity.analytics.read: Read revenue analytics
            velocity.denials.read: Read denial management data
            velocity.clearinghouse.eligibility: Stedi 270/271 eligibility checks
            velocity.clearinghouse.claims.read: Stedi 276/277 claim status
            velocity.clearinghouse.claims.write: Stedi 837P claim submission
  parameters:
    Page:
      in: query
      name: page
      required: false
      schema:
        type: integer
        minimum: 1
        default: 1
    Limit:
      in: query
      name: limit
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25
    XCorrelationId:
      in: header
      name: X-Correlation-Id
      required: false
      schema:
        type: string
        format: uuid
  responses:
    BadRequest:
      description: Bad request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Caller does not have required role/scope
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  schemas:
    DashboardResponse:
      type: object
      required: [generated_at, period, kpis, ar_aging, denial_summary, payer_mix]
      properties:
        generated_at:
          type: string
          format: date-time
        period:
          type: string
        kpis:
          $ref: '#/components/schemas/DashboardKPIs'
        ar_aging:
          $ref: '#/components/schemas/ARAgingBuckets'
        denial_summary:
          $ref: '#/components/schemas/DenialSummary'
        payer_mix:
          type: array
          items:
            $ref: '#/components/schemas/PayerMixEntry'
    DashboardKPIs:
      type: object
      properties:
        claims_pending:
          type: integer
        claims_approved:
          type: integer
        claims_denied:
          type: integer
        claims_in_review:
          type: integer
        total_claims:
          type: integer
        clean_claim_rate_pct:
          type: number
        net_revenue:
          type: number
        gross_charges:
          type: number
        adjustments:
          type: number
        average_reimbursement_rate_pct:
          type: number
    ARAgingBuckets:
      type: object
      properties:
        current_0_30:
          type: number
        days_31_60:
          type: number
        days_61_90:
          type: number
        days_91_120:
          type: number
        over_120:
          type: number
    DenialSummary:
      type: object
      properties:
        total_denials:
          type: integer
        denial_rate_pct:
          type: number
        top_reasons:
          type: array
          items:
            $ref: '#/components/schemas/DenialReason'
    DenialReason:
      type: object
      properties:
        code:
          type: string
        reason:
          type: string
        count:
          type: integer
    PayerMixEntry:
      type: object
      properties:
        payer:
          type: string
        pct:
          type: number
    ClaimsListResponse:
      type: object
      required: [page, limit, total, claims]
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        claims:
          type: array
          items:
            $ref: '#/components/schemas/Claim'
    Claim:
      type: object
      properties:
        claim_id:
          type: string
        patient_gpid:
          type: string
        service_date:
          type: string
          format: date
        payer:
          type: string
        status:
          type: string
          enum: [pending, submitted, in_review, approved, denied, paid, appealed]
        total_charges:
          type: number
        allowed_amount:
          type: number
          nullable: true
        patient_responsibility:
          type: number
          nullable: true
        procedure_codes:
          type: array
          items:
            type: string
        diagnosis_codes:
          type: array
          items:
            type: string
        submitted_at:
          type: string
          format: date-time
        adjudicated_at:
          type: string
          format: date-time
          nullable: true
        denial_reason:
          type: string
          nullable: true
    AnalyticsResponse:
      type: object
      required: [period, generated_at, revenue, monthly_trend, payer_performance, ar_summary]
      properties:
        period:
          type: string
          enum: [mtd, qtd, ytd, trailing_12]
        generated_at:
          type: string
          format: date-time
        revenue:
          $ref: '#/components/schemas/RevenueMetrics'
        monthly_trend:
          type: array
          items:
            $ref: '#/components/schemas/MonthlyTrendEntry'
        payer_performance:
          type: array
          items:
            $ref: '#/components/schemas/PayerPerformance'
        ar_summary:
          $ref: '#/components/schemas/ARSummary'
    RevenueMetrics:
      type: object
      properties:
        gross_charges:
          type: number
        contractual_adjustments:
          type: number
        net_revenue:
          type: number
        collections:
          type: number
        collection_rate_pct:
          type: number
    MonthlyTrendEntry:
      type: object
      properties:
        month:
          type: string
        gross:
          type: number
        net:
          type: number
        collections:
          type: number
    PayerPerformance:
      type: object
      properties:
        payer:
          type: string
        avg_days_to_pay:
          type: integer
        denial_rate_pct:
          type: number
        collection_rate_pct:
          type: number
    ARSummary:
      type: object
      properties:
        total_outstanding:
          type: number
        avg_days_in_ar:
          type: integer
        over_90_pct:
          type: number
    DenialsListResponse:
      type: object
      required: [page, limit, total, summary, denials]
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        summary:
          $ref: '#/components/schemas/DenialStatusSummary'
        denials:
          type: array
          items:
            $ref: '#/components/schemas/Denial'
    DenialStatusSummary:
      type: object
      properties:
        open:
          type: integer
        in_appeal:
          type: integer
        resolved:
          type: integer
        written_off:
          type: integer
        total_at_risk:
          type: number
    Denial:
      type: object
      properties:
        denial_id:
          type: string
        claim_id:
          type: string
        patient_gpid:
          type: string
        payer:
          type: string
        service_date:
          type: string
          format: date
        charged_amount:
          type: number
        denial_code:
          type: string
        denial_reason:
          type: string
        category:
          type: string
          enum: [authorization, coding, documentation, eligibility, medical_necessity, timely_filing, other]
        status:
          type: string
          enum: [open, in_appeal, resolved, written_off]
        recommended_action:
          type: string
        appeal_deadline:
          type: string
          format: date
        days_to_deadline:
          type: integer
        resolution:
          type: string
          nullable: true
    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: string
            message:
              type: string
            correlation_id:
              type: string
              format: uuid
    StediHealthReport:
      type: object
      properties:
        partner:
          type: string
        status:
          type: string
          enum: [operational, degraded, down]
        integrationDate:
          type: string
          format: date
        integrationStatus:
          type: string
        checks:
          type: array
          items:
            type: object
            properties:
              id: { type: string }
              name: { type: string }
              status: { type: string }
              detail: { type: string, nullable: true }
    ClearinghouseResult:
      type: object
      properties:
        success:
          type: boolean
        status_code:
          type: integer
          nullable: true
        error:
          type: string
          nullable: true
        data:
          type: object
          additionalProperties: true
    EligibilityRequest:
      type: object
      required: [tradingPartnerServiceId, subscriber]
      properties:
        tradingPartnerServiceId:
          type: string
          description: Payer routing ID
        subscriber:
          type: object
          additionalProperties: true
        provider:
          type: object
          additionalProperties: true
        encounter:
          type: object
          additionalProperties: true
        dependent:
          type: object
          additionalProperties: true
    ClaimStatusRequest:
      type: object
      required: [tradingPartnerServiceId, subscriber, encounter]
      properties:
        tradingPartnerServiceId:
          type: string
        subscriber:
          type: object
        encounter:
          type: object
    ClaimSubmitRequest:
      type: object
      required: [tradingPartnerServiceId, billing, subscriber, claimInformation]
      properties:
        tradingPartnerServiceId:
          type: string
        billing:
          type: object
        subscriber:
          type: object
        claimInformation:
          type: object
        usageIndicator:
          type: string
          enum: [T, P]
          description: T=test, P=production
        production:
          type: boolean
          description: Shorthand for usageIndicator P when true
