openapi: 3.0.3
info:
  title: qm-astro Zodiac Support API
  version: "1"
  description: |
    Integration contract for `@quizzman/qm-zodiacsign`.
    Production: `https://astro.quizzman.com`
    Local: `http://127.0.0.1:4055`
servers:
  - url: https://astro.quizzman.com
  - url: http://127.0.0.1:4055
security:
  - bearerAuth: []
paths:
  /health:
    get:
      security: []
      summary: Health check (public)
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  service: { type: string }
                  version: { type: string }
  /v1/zodiac-support/catalog:
    get:
      summary: Static catalog (12 signs, 24 half-signs, bodies)
      responses:
        "200":
          description: Catalog
        "401":
          description: Unauthorized
  /v1/zodiac-support/snapshot:
    get:
      summary: Snapshot at one datetime
      parameters:
        - in: query
          name: datetime
          required: true
          schema: { type: string, example: "2026-07-27T12:00:00" }
        - in: query
          name: tzOffsetHours
          schema: { type: number, example: 7 }
        - in: query
          name: lat
          schema: { type: number }
        - in: query
          name: lon
          schema: { type: number }
        - in: query
          name: frame
          schema: { type: string, enum: [tropical, sidereal] }
        - in: query
          name: bodies
          schema: { type: string, example: "sun,moon,mercury,venus,mars" }
        - in: query
          name: includeAngles
          schema: { type: boolean }
        - in: query
          name: includeMoonPhase
          schema: { type: boolean }
        - in: query
          name: includeSolarTerm
          schema: { type: boolean }
      responses:
        "200":
          description: Snapshot
    post:
      summary: Snapshot (JSON body)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SnapshotRequest"
      responses:
        "200":
          description: Snapshot
  /v1/zodiac-support/snapshot/range:
    post:
      summary: Batch daily snapshots
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RangeRequest"
      responses:
        "200":
          description: Range points
  /v1/zodiac-support/sun-sign-calendar:
    get:
      summary: Sun sign / half-sign calendar for a year
      parameters:
        - in: query
          name: year
          required: true
          schema: { type: integer, example: 2026 }
        - in: query
          name: frame
          schema: { type: string, enum: [tropical, sidereal] }
        - in: query
          name: granularity
          schema: { type: string, enum: [sign, half] }
      responses:
        "200":
          description: Segments
  /v1/zodiac-support/events:
    post:
      summary: Ingress, stations, moon phases, solar terms in a window
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EventsRequest"
      responses:
        "200":
          description: Events
  /v1/zodiac-support/aspects:
    post:
      summary: Major aspects between two body sets
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AspectsRequest"
      responses:
        "200":
          description: Aspects
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
  schemas:
    SnapshotRequest:
      type: object
      required: [datetime]
      properties:
        datetime: { type: string }
        tzOffsetHours: { type: number }
        lat: { type: number }
        lon: { type: number }
        frame: { type: string }
        bodies:
          type: array
          items: { type: string }
        includeAngles: { type: boolean }
        includeMoonPhase: { type: boolean }
        includeSolarTerm: { type: boolean }
    RangeRequest:
      type: object
      required: [startDate, endDate]
      properties:
        startDate: { type: string }
        endDate: { type: string }
        hourLocal: { type: number }
        tzOffsetHours: { type: number }
        lat: { type: number }
        lon: { type: number }
        frame: { type: string }
        bodies:
          type: array
          items: { type: string }
        includeAngles: { type: boolean }
        stepDays: { type: integer, enum: [1, 2, 3, 7] }
    EventsRequest:
      type: object
      required: [start, end]
      properties:
        start: { type: string }
        end: { type: string }
        types:
          type: array
          items:
            type: string
            enum:
              - sign_ingress
              - half_sign_ingress
              - moon_phase
              - retrograde_station
              - solar_term
        bodies:
          type: array
          items: { type: string }
    AspectsRequest:
      type: object
      required: [datetime]
      properties:
        datetime: { type: string }
        tzOffsetHours: { type: number }
        frame: { type: string }
        bodiesA:
          type: array
          items: { type: string }
        bodiesB:
          type: array
          items: { type: string }
        aspects:
          type: array
          items: { type: string }
        orbs:
          type: object
          additionalProperties: { type: number }
