openapi: 3.1.0
info:
  title: Hermes API
  version: 0.1.0
  description: Native betting study prototype API.
servers:
  - url: http://localhost:3000
paths:
  /health:
    get:
      summary: Health check
      responses:
        '200':
          description: Service health
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
  /api/v1/session/start:
    post:
      summary: Start a session
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SessionStartRequest'
      responses:
        '201':
          description: Session started
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponse'
  /api/v1/session/end:
    post:
      summary: End the current session
      responses:
        '200':
          description: Session ended
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponse'
  /api/v1/session/me:
    get:
      summary: Inspect the current session
      responses:
        '200':
          description: Current session snapshot
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponse'
  /api/v1/feed/next:
    get:
      summary: Fetch the next round
      responses:
        '200':
          description: Next event payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Event'
  /api/v1/events/{event_id}:
    get:
      summary: Fetch an event
      parameters:
        - name: event_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Event
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Event'
  /api/v1/events/{event_id}/manifest:
    get:
      summary: Fetch the event manifest
      parameters:
        - name: event_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Manifest
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventManifest'
  /api/v1/events/{event_id}/markets:
    get:
      summary: List markets for an event
      parameters:
        - name: event_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Markets
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Market'
  /api/v1/markets/{market_id}/odds/current:
    get:
      summary: Fetch current odds for a market
      parameters:
        - name: market_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Current odds
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OddsVersion'
  /api/v1/stream:
    get:
      summary: Live odds and state stream
      responses:
        '200':
          description: Server-sent events stream
          content:
            text/event-stream:
              schema:
                type: string
  /api/v1/bets/intent:
    post:
      summary: Submit a bet intent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BetIntentRequest'
      responses:
        '201':
          description: Bet intent result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BetIntentResponse'
  /api/v1/bets/{bet_intent_id}:
    get:
      summary: Fetch a bet intent
      parameters:
        - name: bet_intent_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Bet intent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BetIntentResponse'
  /api/v1/events/{event_id}/result:
    get:
      summary: Fetch event result
      parameters:
        - name: event_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Result payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Settlement'
  /api/v1/analytics/batch:
    post:
      summary: Ingest analytics batch
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AnalyticsBatchRequest'
      responses:
        '202':
          description: Accepted
  /api/v1/experiments/config:
    get:
      summary: Fetch experiment config
      responses:
        '200':
          description: Experiment config
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExperimentConfig'
  /api/v1/localization/{locale_code}:
    get:
      summary: Fetch a localization bundle
      parameters:
        - name: locale_code
          in: path
          required: true
          schema:
            type: string
            enum: [en, sv]
      responses:
        '200':
          description: Localization bundle
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocalizationBundle'
  /api/v1/admin/events:
    post:
      summary: Create an event
      responses:
        '201':
          description: Created
  /api/v1/admin/markets:
    post:
      summary: Create a market
      responses:
        '201':
          description: Created
  /api/v1/admin/odds:
    post:
      summary: Publish odds
      responses:
        '201':
          description: Created
  /api/v1/admin/settlements:
    post:
      summary: Publish a settlement
      responses:
        '201':
          description: Created
components:
  schemas:
    HealthResponse:
      type: object
      required: [status, service_name, environment, version, uptime_ms, server_time, database_ready, redis_ready]
      properties:
        status:
          type: string
        service_name:
          type: string
        environment:
          type: string
        version:
          type: string
        uptime_ms:
          type: integer
        server_time:
          type: string
          format: date-time
        database_ready:
          type: boolean
        redis_ready:
          type: boolean
    SessionStartRequest:
      type: object
      properties:
        external_ref:
          type: string
        locale_code:
          type: string
        device_platform:
          type: string
        device_model:
          type: string
        os_version:
          type: string
        app_version:
          type: string
    SessionResponse:
      type: object
      required: [session_id, user_id, started_at, experiment_variant, app_version, locale_code, device_platform]
      properties:
        session_id:
          type: string
          format: uuid
        user_id:
          type: string
          format: uuid
        started_at:
          type: string
          format: date-time
        ended_at:
          type: string
          format: date-time
          nullable: true
        experiment_variant:
          type: string
        app_version:
          type: string
        device_model:
          type: string
          nullable: true
        os_version:
          type: string
          nullable: true
        locale_code:
          type: string
        device_platform:
          type: string
    Event:
      type: object
      required: [id, sport_type, source_ref, title_en, title_sv, status, lock_at, settle_at]
      properties:
        id:
          type: string
          format: uuid
        sport_type:
          type: string
        source_ref:
          type: string
        title_en:
          type: string
        title_sv:
          type: string
        status:
          type: string
        preview_start_ms:
          type: integer
        preview_end_ms:
          type: integer
        reveal_start_ms:
          type: integer
        reveal_end_ms:
          type: integer
        lock_at:
          type: string
          format: date-time
        settle_at:
          type: string
          format: date-time
    EventManifest:
      type: object
      properties:
        event:
          $ref: '#/components/schemas/Event'
        media:
          type: array
          items:
            $ref: '#/components/schemas/EventMedia'
        markets:
          type: array
          items:
            $ref: '#/components/schemas/Market'
    EventMedia:
      type: object
      required: [id, event_id, media_type, hls_master_url, duration_ms]
      properties:
        id:
          type: string
          format: uuid
        event_id:
          type: string
          format: uuid
        media_type:
          type: string
        hls_master_url:
          type: string
        poster_url:
          type: string
          nullable: true
        duration_ms:
          type: integer
        preview_start_ms:
          type: integer
        preview_end_ms:
          type: integer
        reveal_start_ms:
          type: integer
        reveal_end_ms:
          type: integer
    Market:
      type: object
      required: [id, event_id, question_key, market_type, status, lock_at, settlement_rule_key]
      properties:
        id:
          type: string
          format: uuid
        event_id:
          type: string
          format: uuid
        question_key:
          type: string
        market_type:
          type: string
        status:
          type: string
        lock_at:
          type: string
          format: date-time
        settlement_rule_key:
          type: string
        outcomes:
          type: array
          items:
            $ref: '#/components/schemas/Outcome'
    Outcome:
      type: object
      required: [id, market_id, outcome_code, label_key, sort_order]
      properties:
        id:
          type: string
          format: uuid
        market_id:
          type: string
          format: uuid
        outcome_code:
          type: string
        label_key:
          type: string
        sort_order:
          type: integer
    OddsVersion:
      type: object
      required: [id, market_id, version_no, created_at, is_current]
      properties:
        id:
          type: string
          format: uuid
        market_id:
          type: string
          format: uuid
        version_no:
          type: integer
        created_at:
          type: string
          format: date-time
        is_current:
          type: boolean
        odds:
          type: array
          items:
            $ref: '#/components/schemas/OutcomeOdds'
    OutcomeOdds:
      type: object
      required: [id, odds_version_id, outcome_id, decimal_odds, fractional_num, fractional_den]
      properties:
        id:
          type: string
          format: uuid
        odds_version_id:
          type: string
          format: uuid
        outcome_id:
          type: string
          format: uuid
        decimal_odds:
          type: number
        fractional_num:
          type: integer
        fractional_den:
          type: integer
    BetIntentRequest:
      type: object
      required: [session_id, event_id, market_id, outcome_id, idempotency_key, client_sent_at]
      properties:
        session_id:
          type: string
          format: uuid
        event_id:
          type: string
          format: uuid
        market_id:
          type: string
          format: uuid
        outcome_id:
          type: string
          format: uuid
        idempotency_key:
          type: string
        client_sent_at:
          type: string
          format: date-time
    BetIntentResponse:
      type: object
      required: [id, accepted, acceptance_code, server_received_at]
      properties:
        id:
          type: string
          format: uuid
        accepted:
          type: boolean
        acceptance_code:
          type: string
        accepted_odds_version_id:
          type: string
          format: uuid
          nullable: true
        server_received_at:
          type: string
          format: date-time
    Settlement:
      type: object
      required: [id, market_id, settled_at, winning_outcome_id]
      properties:
        id:
          type: string
          format: uuid
        market_id:
          type: string
          format: uuid
        settled_at:
          type: string
          format: date-time
        winning_outcome_id:
          type: string
          format: uuid
    AnalyticsBatchRequest:
      type: object
      required: [events]
      properties:
        events:
          type: array
          items:
            $ref: '#/components/schemas/AnalyticsEventInput'
    AnalyticsEventInput:
      type: object
      required: [event_name, occurred_at]
      properties:
        event_name:
          type: string
        occurred_at:
          type: string
          format: date-time
        attributes:
          type: array
          items:
            $ref: '#/components/schemas/AttributeInput'
    AttributeInput:
      type: object
      required: [key, value]
      properties:
        key:
          type: string
        value:
          type: string
    ExperimentConfig:
      type: object
      properties:
        variant:
          type: string
        feature_flags:
          type: object
          additionalProperties:
            type: boolean
    LocalizationBundle:
      type: object
      required: [locale_code, values]
      properties:
        locale_code:
          type: string
        values:
          type: object
          additionalProperties:
            type: string
    ErrorResponse:
      type: object
      required: [code, message]
      properties:
        code:
          type: string
        message:
          type: string
