{
  "openapi": "3.1.0",
  "info": {
    "title": "BaziRoute Public Calculation API",
    "version": "0.1.0",
    "description": "Keyless deterministic BaZi calculation endpoints. The current rule engine is a prototype and is not a certified standard or prediction service.",
    "contact": {
      "name": "BaziRoute",
      "url": "https://baziroute.com/contact",
      "email": "hello@baziroute.com"
    }
  },
  "servers": [
    {
      "url": "https://api.baziroute.com",
      "description": "Production API"
    }
  ],
  "paths": {
    "/v1/locations/search": {
      "get": {
        "operationId": "searchLocations",
        "summary": "Find cities and IANA time zones",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 120
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 8
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Location matches",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LocationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/v1/charts/calculate": {
      "post": {
        "operationId": "calculateChart",
        "summary": "Calculate a Four Pillars chart",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChartRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculated chart",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChartResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "422": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/v1/compatibility/calculate": {
      "post": {
        "operationId": "calculateCompatibility",
        "summary": "Compare structural relationships between two charts",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompatibilityRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Structural comparison without a score",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["ok", "compatibility"],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "compatibility": {
                      "$ref": "#/components/schemas/CompatibilityAnalysis"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "422": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "ErrorResponse": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "ChartRequest": {
        "type": "object",
        "required": ["date", "timeZone"],
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "example": "2026-10-27"
          },
          "time": {
            "type": "string",
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "example": "12:00",
            "description": "Required unless unknownTime is true."
          },
          "timeZone": {
            "type": "string",
            "example": "Asia/Hong_Kong",
            "description": "IANA time-zone identifier."
          },
          "latitude": {
            "type": "number",
            "minimum": -90,
            "maximum": 90
          },
          "longitude": {
            "type": "number",
            "minimum": -180,
            "maximum": 180
          },
          "trueSolarTime": {
            "type": "boolean",
            "default": false
          },
          "unknownTime": {
            "type": "boolean",
            "default": false
          },
          "utcOffsetOverride": {
            "type": "string",
            "pattern": "^[+-]((0[0-9]|1[0-3]):[0-5][0-9](:[0-5][0-9])?|14:00(:00)?)$",
            "example": "+07:36:42",
            "description": "Sourced fixed UTC offset. Minute precision remains supported; HH:MM:SS is accepted for historical offsets that preserve seconds."
          },
          "utcOffsetSource": {
            "type": "string",
            "enum": [
              "birth-record",
              "family-record",
              "historical-source",
              "other"
            ]
          }
        },
        "additionalProperties": false
      },
      "Stem": {
        "type": "object",
        "required": ["hanzi", "pinyin", "element", "polarity"],
        "properties": {
          "hanzi": { "type": "string" },
          "pinyin": { "type": "string" },
          "element": {
            "type": "string",
            "enum": ["Wood", "Fire", "Earth", "Metal", "Water"]
          },
          "polarity": {
            "type": "string",
            "enum": ["Yang", "Yin"]
          }
        }
      },
      "Pillar": {
        "type": "object",
        "required": ["hanzi", "pinyin", "stem", "branch"],
        "properties": {
          "hanzi": { "type": "string" },
          "pinyin": { "type": "string" },
          "stem": {
            "$ref": "#/components/schemas/Stem"
          },
          "branch": {
            "type": "object",
            "required": ["hanzi", "pinyin", "element", "polarity"],
            "properties": {
              "hanzi": { "type": "string" },
              "pinyin": { "type": "string" },
              "element": { "type": "string" },
              "polarity": { "type": "string" }
            }
          }
        }
      },
      "Pillars": {
        "type": "object",
        "required": ["year", "month", "day", "hour"],
        "properties": {
          "year": { "$ref": "#/components/schemas/Pillar" },
          "month": { "$ref": "#/components/schemas/Pillar" },
          "day": { "$ref": "#/components/schemas/Pillar" },
          "hour": {
            "oneOf": [
              { "$ref": "#/components/schemas/Pillar" },
              { "type": "null" }
            ]
          }
        }
      },
      "ChartResponse": {
        "type": "object",
        "required": ["ok", "chart"],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "chart": {
            "type": "object",
            "required": [
              "pillars",
              "dayMaster",
              "normalizedInput",
              "calculationTime",
              "structuralAnalysis",
              "boundaries",
              "flags"
            ],
            "properties": {
              "pillars": {
                "$ref": "#/components/schemas/Pillars"
              },
              "dayMaster": {
                "$ref": "#/components/schemas/Stem"
              },
              "normalizedInput": {
                "type": "object",
                "additionalProperties": true
              },
              "calculationTime": {
                "type": "object",
                "additionalProperties": true
              },
              "solarTime": {
                "oneOf": [
                  { "type": "object", "additionalProperties": true },
                  { "type": "null" }
                ]
              },
              "structuralAnalysis": {
                "type": "object",
                "additionalProperties": true
              },
              "boundaries": {
                "type": "object",
                "additionalProperties": true
              },
              "uncertainty": {
                "oneOf": [
                  { "type": "object", "additionalProperties": true },
                  { "type": "null" }
                ]
              },
              "inputWarnings": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              },
              "inputOverrides": {
                "oneOf": [
                  { "type": "object", "additionalProperties": true },
                  { "type": "null" }
                ]
              },
              "flags": {
                "type": "array",
                "items": { "type": "string" }
              }
            }
          }
        }
      },
      "CompatibilityRequest": {
        "type": "object",
        "required": ["primary", "secondary"],
        "properties": {
          "primary": {
            "$ref": "#/components/schemas/ChartRequest"
          },
          "secondary": {
            "$ref": "#/components/schemas/ChartRequest"
          }
        },
        "additionalProperties": false
      },
      "CompatibilityAnalysis": {
        "type": "object",
        "required": [
          "methodology",
          "dayMasterRelation",
          "mutualTenGods",
          "branchInteractions",
          "note"
        ],
        "properties": {
          "methodology": {
            "type": "string",
            "const": "bazi-relationship-structure-v1"
          },
          "dayMasterRelation": {
            "type": "object",
            "additionalProperties": true
          },
          "mutualTenGods": {
            "type": "object",
            "additionalProperties": true
          },
          "branchInteractions": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "note": {
            "type": "string"
          }
        }
      },
      "LocationResponse": {
        "type": "object",
        "required": ["ok", "matches"],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "matches": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "city",
                "label",
                "country",
                "latitude",
                "longitude",
                "timeZone"
              ],
              "properties": {
                "id": { "type": "string" },
                "city": { "type": "string" },
                "label": { "type": "string" },
                "country": { "type": "string" },
                "province": { "type": "string" },
                "latitude": { "type": "number" },
                "longitude": { "type": "number" },
                "timeZone": { "type": "string" },
                "population": { "type": "integer" }
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["ok", "error"],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "string"
          }
        }
      }
    }
  }
}
