{
  "openapi": "3.0.3",
  "info": {
    "title": "AstroResearch Public API",
    "version": "1.0.0",
    "description": "ML event-probability API for AstroResearch (research.innerdata.cloud). Free tier available to humans and AI agents (anonymous daily quota). Higher limits when authenticated with InnerData-shared accounts. Outputs are model probabilities, not advice. Training scrapes not permitted (ai-train=no).",
    "contact": { "url": "https://research.innerdata.cloud/" }
  },
  "servers": [
    { "url": "https://research.innerdata.cloud", "description": "Production" }
  ],
  "paths": {
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Service health and model readiness",
        "parameters": [
          {
            "name": "deep",
            "in": "query",
            "schema": { "type": "string", "enum": ["1"] },
            "description": "Run canary predict when deep=1"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": { "type": "string" },
                    "models": { "type": "integer" },
                    "modelIds": { "type": "array", "items": { "type": "string" } },
                    "featureParity": { "type": "object" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/models": {
      "get": {
        "operationId": "listModels",
        "summary": "List available prediction models",
        "responses": {
          "200": {
            "description": "Model list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "models": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": { "type": "string", "example": "v10_honest" },
                          "name": { "type": "string" },
                          "experimental": { "type": "boolean" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/predict": {
      "post": {
        "operationId": "predictEvents",
        "summary": "Predict category probabilities over a date range",
        "description": "Free tier: anonymous clients share a daily quota (same as human free tier). Send Authorization: Bearer <token> after register/login for registered limits. Requires birth place coordinates (lat/lng).",
        "security": [{ "bearerAuth": [] }, {}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["birthData", "dateRange", "models"],
                "properties": {
                  "birthData": {
                    "type": "object",
                    "required": ["date", "lat", "lng"],
                    "properties": {
                      "date": { "type": "string", "format": "date", "example": "2002-07-17" },
                      "time": { "type": "string", "example": "10:09" },
                      "lat": { "type": "number", "example": 50.45 },
                      "lng": { "type": "number", "example": 30.52 },
                      "place": { "type": "string" },
                      "tz": { "type": "number", "description": "UTC offset hours", "example": 3 }
                    }
                  },
                  "dateRange": {
                    "type": "object",
                    "required": ["from", "to"],
                    "properties": {
                      "from": { "type": "string", "format": "date" },
                      "to": { "type": "string", "format": "date" }
                    }
                  },
                  "timeframe": { "type": "string", "enum": ["month", "day"], "default": "month" },
                  "models": {
                    "type": "array",
                    "items": { "type": "string" },
                    "example": ["v10_honest"]
                  },
                  "residence": {
                    "type": "array",
                    "description": "Optional life-vector city segments",
                    "items": { "type": "object" }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Predictions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "predictions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": { "type": "string" },
                          "probabilities": {
                            "type": "object",
                            "additionalProperties": { "type": "number" }
                          }
                        }
                      }
                    },
                    "durationMs": { "type": "integer" },
                    "engine": { "type": "object" }
                  }
                }
              }
            }
          },
          "400": { "description": "VALIDATION_ERROR | COORDS_REQUIRED | RANGE_TOO_LARGE" },
          "401": { "description": "ANON_LIMIT or auth required" },
          "503": { "description": "MODEL_UNAVAILABLE | FEATURES_UNAVAILABLE" }
        }
      }
    },
    "/auth/register": {
      "post": {
        "operationId": "register",
        "summary": "Create account (shared with InnerData users table)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email", "password"],
                "properties": {
                  "email": { "type": "string", "format": "email" },
                  "password": { "type": "string", "minLength": 6 },
                  "name": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "token + user" },
          "409": { "description": "EMAIL_EXISTS — use login with InnerData password" }
        }
      }
    },
    "/auth/login": {
      "post": {
        "operationId": "login",
        "summary": "Login (InnerData password works)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email", "password"],
                "properties": {
                  "email": { "type": "string" },
                  "password": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "token + user" },
          "401": { "description": "INVALID_CREDENTIALS | NO_PASSWORD" }
        }
      }
    },
    "/auth/me": {
      "get": {
        "operationId": "me",
        "security": [{ "bearerAuth": [] }],
        "summary": "Current user profile",
        "responses": {
          "200": { "description": "user" },
          "401": { "description": "unauthorized" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    }
  }
}
