openapi: 3.0.3
info:
  title: 4ura Hologram Synergy API
  description: |
    Async gateway for human-readable multidimensional profile synthesis.

    The public report promotes verified facts from Western Astrology, Chinese BaZi,
    and Human Design, then adds a long-form `profile_narrative` for readable
    person-level interpretation. Public responses keep raw engine output private by
    default. During development, append `debug=1` to compose/status requests to get
    `_debug` diagnostics; append `raw=1` and provide a valid API key to include raw
    engine payloads for inspection.
  version: 1.3.0
servers:
  - url: https://4ura.lair404.xyz/api
    description: Production API root
  - url: http://100.125.93.90/dev/4ura/dev-ops/api
    description: h0menode dev-ops branch API root
  - url: http://100.125.93.90/dev/4ura/main/api
    description: Shared h0menode main branch API root

tags:
  - name: Compose
    description: Queue and poll multidimensional profile synthesis.
  - name: Match
    description: Compare already calculated 4ura identities for contextual fit.
  - name: Geocode
    description: Verified birth-city search and coordinate resolution.
  - name: Visuals
    description: Route-backed deterministic and generated image assets.
  - name: Observability
    description: Health, degradation, and debug surfaces.
  - name: Persistence
    description: Protected seed-first persistence verification.

paths:
  /compose:
    post:
      tags: [Compose]
      summary: Queue a 4ura profile synthesis
      operationId: composeHologram
      parameters:
        - $ref: '#/components/parameters/DebugQuery'
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ComposeRequest'
            example:
              seed_profile:
                birth_date: "1987-03-18"
                birth_time: "22:22"
                birth_city: "Regensburg, Bayern, Deutschland"
                birth_coords:
                  lat: 49.01343
                  lng: 12.10162
                timezone: Europe/Berlin
              orchestration_options:
                language: en
                priority: standard
                lenses: [personal]
      responses:
        '202':
          description: Accepted. Poll the returned hash until status returns 200.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComposeAcceptedResponse'
        '400':
          description: Invalid seed, timezone, or local birth moment.

  /compose/status/{hash}:
    get:
      tags: [Compose]
      summary: Poll synthesis status by hash
      operationId: getHologramStatus
      parameters:
        - $ref: '#/components/parameters/HashPath'
        - name: lang
          in: query
          required: false
          schema:
            type: string
            enum: [en, de]
          description: Response language for finalized copy.
        - $ref: '#/components/parameters/DebugQuery'
        - $ref: '#/components/parameters/RawQuery'
        - name: apikey
          in: query
          required: false
          schema:
            type: string
          description: Optional API key for raw debug output. Prefer the `X-4ura-API-Key` header.
      responses:
        '200':
          description: Completed public report. `_debug` appears only when `debug=1`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComposeResponse'
        '202':
          description: Job is still processing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComposeAcceptedResponse'

  /status/{hash}:
    get:
      tags: [Compose]
      summary: Compatibility alias for `/compose/status/{hash}`
      operationId: getHologramStatusAlias
      parameters:
        - $ref: '#/components/parameters/HashPath'
        - $ref: '#/components/parameters/DebugQuery'
        - $ref: '#/components/parameters/RawQuery'
      responses:
        '200':
          description: Completed public report.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ComposeResponse'
        '202':
          description: Job is still processing.

  /match/partner-profile:
    post:
      tags: [Match]
      summary: Score two calculated identities for dating or partner fit
      operationId: matchPartnerProfile
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PartnerMatchRequest'
            example:
              left_hash: "aaaaaaaaaaaaaaaaaaaaaaaa"
              right_hash: "bbbbbbbbbbbbbbbbbbbbbbbb"
              purpose: dating_partner
      responses:
        '200':
          description: Deterministic partner match score and explainable breakdown.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerMatchResponse'
        '404':
          description: One or both calculated hashes were not found in database or cache.

  /geocode/search:
    get:
      tags: [Geocode]
      summary: Search verified birth cities
      operationId: searchVerifiedCities
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
          example: Regensburg
        - name: lang
          in: query
          required: false
          schema:
            type: string
            enum: [en, de]
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 10
      responses:
        '200':
          description: Verified city suggestions with coordinates and timezone when known.
          content:
            application/json:
              schema:
                type: object
                properties:
                  suggestions:
                    type: array
                    items:
                      $ref: '#/components/schemas/CitySuggestion'

  /render/{hash}/{type}:
    get:
      tags: [Visuals]
      summary: Fetch deterministic SVG visual
      operationId: getRenderVisual
      parameters:
        - $ref: '#/components/parameters/HashPath'
        - name: type
          in: path
          required: true
          schema:
            type: string
            enum: [radar, bazi, hd, western]
      responses:
        '200':
          description: SVG visual.

  /generated/{hash}/{type}:
    get:
      tags: [Visuals]
      summary: Fetch generated imagery asset
      operationId: getGeneratedImagery
      parameters:
        - $ref: '#/components/parameters/HashPath'
        - name: type
          in: path
          required: true
          schema:
            type: string
            enum: [cover, share-card, report-cover]
        - name: profile
          in: query
          required: false
          schema:
            type: string
            enum: [business, private, dating, personal]
      responses:
        '200':
          description: Generated SVG asset.

  /health:
    get:
      tags: [Observability]
      summary: Read health and degradation counters
      operationId: getHealth
      parameters:
        - name: format
          in: query
          required: false
          schema:
            type: string
            enum: [json]
      responses:
        '200':
          description: Service health.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'

  /internal/persistence/run/{hash}:
    get:
      tags: [Persistence]
      summary: Read one persisted calculation run by hash
      operationId: getInternalPersistenceRun
      security:
        - SmokeKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/HashPath'
      responses:
        '200':
          description: Seed identity, calculation run, factors, assets, and linked handles.
        '403':
          description: Missing or invalid internal key.
        '503':
          description: Runtime database is not configured.

  /internal/persistence/handle:
    get:
      tags: [Persistence]
      summary: Read persisted handle linkage
      operationId: getInternalPersistenceHandle
      security:
        - SmokeKeyAuth: []
      parameters:
        - name: value
          in: query
          required: false
          schema:
            type: string
        - name: handle
          in: query
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Handle snapshot and latest linked run data.
        '403':
          description: Missing or invalid internal key.
        '503':
          description: Runtime database is not configured.

  /internal/persistence/diagnostics:
    get:
      tags: [Persistence]
      summary: Read persistence counts and latest-run diagnostics
      operationId: getInternalPersistenceDiagnostics
      security:
        - SmokeKeyAuth: []
      responses:
        '200':
          description: Seed-first persistence counts and latest run metadata.
        '403':
          description: Missing or invalid internal key.
        '503':
          description: Runtime database is not configured.

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-4ura-API-Key
    SmokeKeyAuth:
      type: apiKey
      in: header
      name: x-4ura-smoke-key

  parameters:
    HashPath:
      name: hash
      in: path
      required: true
      schema:
        type: string
        pattern: '^[a-f0-9]{24,32}$'
    DebugQuery:
      name: debug
      in: query
      required: false
      schema:
        type: string
        enum: ["1", "true"]
      description: Include `_debug` diagnostics and enable matching browser console output.
    RawQuery:
      name: raw
      in: query
      required: false
      schema:
        type: string
        enum: ["1", "true"]
      description: Include full `_debug.raw_engines` only when `debug=1` and a valid API key is present.

  schemas:
    ComposeRequest:
      type: object
      required: [seed_profile]
      properties:
        seed_profile:
          type: object
          required: [birth_date, birth_time, birth_city, birth_coords, timezone]
          properties:
            birth_date:
              type: string
              format: date
            birth_time:
              type: string
              pattern: '^\\d{2}:\\d{2}$'
            birth_city:
              type: string
            birth_coords:
              type: object
              required: [lat, lng]
              properties:
                lat: { type: number }
                lng: { type: number }
            timezone:
              type: string
              example: Europe/Berlin
        orchestration_options:
          type: object
          properties:
            language:
              type: string
              enum: [en, de]
              default: en
            priority:
              type: string
              enum: [standard]
            lenses:
              type: array
              items:
                type: string
                enum: [personal, business, dating]

    ComposeAcceptedResponse:
      type: object
      properties:
        status:
          type: string
          enum: [processing]
        hologram_hash:
          type: string
        _debug:
          $ref: '#/components/schemas/DebugPayload'

    PartnerMatchRequest:
      type: object
      description: Provide two existing calculated report hashes. `hash_a/hash_b` and `hashes` are accepted aliases.
      properties:
        left_hash:
          type: string
          pattern: '^[a-f0-9]{24,32}$'
        right_hash:
          type: string
          pattern: '^[a-f0-9]{24,32}$'
        hash_a:
          type: string
          pattern: '^[a-f0-9]{24,32}$'
        hash_b:
          type: string
          pattern: '^[a-f0-9]{24,32}$'
        hashes:
          type: array
          minItems: 2
          maxItems: 2
          items:
            type: string
            pattern: '^[a-f0-9]{24,32}$'
        purpose:
          type: string
          enum: [dating_partner, dating, partner, relationship]
          default: dating_partner
        language:
          type: string
          enum: [en, de]
          default: en

    PartnerMatchResponse:
      type: object
      properties:
        schema_version: { type: string }
        status: { type: string, enum: [ok] }
        match_type: { type: string, enum: [partner_profile] }
        purpose: { type: string }
        purpose_label: { type: string }
        score:
          type: integer
          minimum: 0
          maximum: 100
        band:
          type: string
          enum: [exceptional, strong, supportive, mixed, low]
        confidence:
          type: number
          minimum: 0
          maximum: 1
        identities:
          type: object
        vector_alignment:
          type: object
        complementarity:
          type: object
        methodology_alignment:
          type: object
        strengths:
          type: array
          items: { type: string }
        cautions:
          type: array
          items: { type: string }
        narrative:
          type: string

    ComposeResponse:
      type: object
      properties:
        hologram_hash: { type: string }
        deterministic_matrix_status:
          type: string
          enum: [ok, mock, fallback, unavailable]
        summary:
          type: object
          properties:
            archetype: { type: string }
            holographic_summary: { type: string }
        general_information:
          $ref: '#/components/schemas/GeneralInformation'
        methodologies:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/MethodologyStatus'
        report_evidence:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/MethodologyEvidence'
        profile_narrative:
          $ref: '#/components/schemas/ProfileNarrative'
        visuals:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/VisualManifest'
        generated_imagery:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/VisualManifest'
        _debug:
          $ref: '#/components/schemas/DebugPayload'

    GeneralInformation:
      type: object
      properties:
        schema_version: { type: string }
        seed: { type: object }
        facts:
          type: array
          items:
            type: object
            properties:
              id: { type: string, example: western.sun_sign }
              methodology: { type: string, enum: [western, chinese, hd] }
              label: { type: string }
              value: { type: string }
              display_value: { type: string }

    MethodologyStatus:
      type: object
      properties:
        key: { type: string }
        label: { type: string }
        requested: { type: boolean }
        status:
          type: string
          enum: [available, mock, unavailable, not_requested]
        display_status: { type: string }
        has_payload: { type: boolean }

    MethodologyEvidence:
      type: object
      properties:
        key: { type: string }
        label: { type: string }
        status: { type: string }
        has_payload: { type: boolean }
        payload_preview: { type: object }
        highlights: { type: object }

    ProfileNarrative:
      type: object
      properties:
        status:
          type: string
          enum: [available, unavailable]
        overview_text:
          type: string
          description: Human-readable profile overview. Current product contract targets at least 500 words when all three methods are available.
        overview_word_count:
          type: integer
          minimum: 500
        sections:
          type: array
          items:
            $ref: '#/components/schemas/ProfileNarrativeSection'

    ProfileNarrativeSection:
      type: object
      properties:
        key:
          type: string
          enum: [western, chinese, hd]
        title: { type: string }
        text:
          type: string
          description: Method-specific human-readable interpretation. Current product contract targets at least 300 words.
        word_count:
          type: integer
          minimum: 300
        facts:
          type: array
          items: { type: string }

    VisualManifest:
      type: object
      properties:
        type: { type: string }
        title: { type: string }
        status: { type: string }
        url: { type: string }

    DebugPayload:
      type: object
      description: Present only for `debug=1`; full `raw_engines` appears only for authorized `debug=1&raw=1` requests.
      properties:
        schema_version: { type: string }
        enabled: { type: boolean }
        gateway: { type: object }
        backend_connection: { type: object }
        methods: { type: object }
        raw_output_summary: { type: object }
        raw_engines: { type: object }

    CitySuggestion:
      type: object
      properties:
        id: { type: string }
        primary_name: { type: string }
        display_name: { type: string }
        country: { type: string }
        region: { type: string }
        lat: { type: number }
        lng: { type: number }
        timezone: { type: string }

    HealthResponse:
      type: object
      properties:
        status: { type: string }
