{
  "openapi": "3.1.0",
  "info": {
    "title": "Moon Shadows Sky Oracle API",
    "version": "1.0.0",
    "description": "Independent VSOP87D/Meeus calculations validated against JPL DE440. Not the NASA/JPL Horizons API. Token-protected calculations use no-store. Default continuously refilling budgets: 60 credits/minute per token (burst 30), 600 globally (burst 60). Counters reset on restart. Existing v1 fields remain compatible; clients must allow additional fields and enum values."
  },
  "servers": [
    {
      "url": "https://moonshadows.app/horizons/api/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "operationId": "health",
        "summary": "Liveness and build identity; public.",
        "x-work-credits": 0,
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful JSON answer. Protected answers are no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/healthResponse"
                }
              }
            }
          },
          "304": {
            "description": "Matching If-None-Match; no body. Authentication and work credits still apply."
          },
          "400": {
            "description": "Invalid input",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string",
                  "example": "Bearer realm=\"skyoracle\""
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "429": {
            "description": "Work budget exhausted",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "503": {
            "description": "Admission queue full",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/position": {
      "get": {
        "operationId": "position",
        "summary": "Geometric Sun and Moon positions, illumination, and shadow.",
        "x-work-credits": 1,
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "description": "Latitude, decimal degrees.",
            "schema": {
              "type": "number",
              "minimum": -90,
              "maximum": 90
            }
          },
          {
            "name": "lon",
            "in": "query",
            "required": true,
            "description": "Longitude, decimal degrees east of Greenwich.",
            "schema": {
              "type": "number",
              "minimum": -180,
              "maximum": 180
            }
          },
          {
            "name": "at",
            "in": "query",
            "required": false,
            "description": "RFC 3339 instant in 1900\u20132050 UTC; defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "height_m",
            "in": "query",
            "required": false,
            "description": "Object height in metres.",
            "schema": {
              "type": "number",
              "minimum": 0.3,
              "maximum": 6,
              "default": 1.8
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful JSON answer. Protected answers are no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/positionResponse"
                }
              }
            }
          },
          "304": {
            "description": "Matching If-None-Match; no body. Authentication and work credits still apply."
          },
          "400": {
            "description": "Invalid input",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string",
                  "example": "Bearer realm=\"skyoracle\""
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "429": {
            "description": "Work budget exhausted",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "503": {
            "description": "Admission queue full",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          }
        }
      }
    },
    "/events": {
      "get": {
        "operationId": "events",
        "summary": "First light crossings per kind, solar noon, and Moon day for one civil day.",
        "x-work-credits": 10,
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "description": "Latitude, decimal degrees.",
            "schema": {
              "type": "number",
              "minimum": -90,
              "maximum": 90
            }
          },
          {
            "name": "lon",
            "in": "query",
            "required": true,
            "description": "Longitude, decimal degrees east of Greenwich.",
            "schema": {
              "type": "number",
              "minimum": -180,
              "maximum": 180
            }
          },
          {
            "name": "zone",
            "in": "query",
            "required": true,
            "description": "IANA time zone, such as America/Los_Angeles.",
            "schema": {
              "type": "string",
              "example": "America/Los_Angeles"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "description": "Civil date; its start instant must be in 1900\u20132050 UTC. Defaults to today in zone.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful JSON answer. Protected answers are no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/eventsResponse"
                }
              }
            }
          },
          "304": {
            "description": "Matching If-None-Match; no body. Authentication and work credits still apply."
          },
          "400": {
            "description": "Invalid input",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string",
                  "example": "Bearer realm=\"skyoracle\""
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "429": {
            "description": "Work budget exhausted",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "503": {
            "description": "Admission queue full",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          }
        }
      }
    },
    "/day": {
      "get": {
        "operationId": "day",
        "summary": "Complete civil-day report including the Moon and shadow at 21:00 local time.",
        "x-work-credits": 10,
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "description": "Latitude, decimal degrees.",
            "schema": {
              "type": "number",
              "minimum": -90,
              "maximum": 90
            }
          },
          {
            "name": "lon",
            "in": "query",
            "required": true,
            "description": "Longitude, decimal degrees east of Greenwich.",
            "schema": {
              "type": "number",
              "minimum": -180,
              "maximum": 180
            }
          },
          {
            "name": "zone",
            "in": "query",
            "required": true,
            "description": "IANA time zone, such as America/Los_Angeles.",
            "schema": {
              "type": "string",
              "example": "America/Los_Angeles"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "description": "Civil date; its start instant must be in 1900\u20132050 UTC. Defaults to today in zone.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful JSON answer. Protected answers are no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/report_Sky"
                }
              }
            }
          },
          "304": {
            "description": "Matching If-None-Match; no body. Authentication and work credits still apply."
          },
          "400": {
            "description": "Invalid input",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string",
                  "example": "Bearer realm=\"skyoracle\""
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "429": {
            "description": "Work budget exhausted",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "503": {
            "description": "Admission queue full",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          }
        }
      }
    },
    "/ephemeris": {
      "get": {
        "operationId": "ephemeris",
        "summary": "Positions sampled from start to end inclusive when step divides the span. Truncated after 3000 rows.",
        "x-work-credits": 10,
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "description": "Latitude, decimal degrees.",
            "schema": {
              "type": "number",
              "minimum": -90,
              "maximum": 90
            }
          },
          {
            "name": "lon",
            "in": "query",
            "required": true,
            "description": "Longitude, decimal degrees east of Greenwich.",
            "schema": {
              "type": "number",
              "minimum": -180,
              "maximum": 180
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "RFC 3339 start, 1900\u20132050 UTC; defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "RFC 3339 end, 1900\u20132050 UTC; must not precede from. Defaults to from + 24 hours.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "step",
            "in": "query",
            "required": false,
            "description": "Positive Go duration, e.g. 30m or 1h. At most 3000 rows are returned; inspect truncated.",
            "schema": {
              "type": "string",
              "default": "1h"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful JSON answer. Protected answers are no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ephemerisResponse"
                }
              }
            }
          },
          "304": {
            "description": "Matching If-None-Match; no body. Authentication and work credits still apply."
          },
          "400": {
            "description": "Invalid input",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string",
                  "example": "Bearer realm=\"skyoracle\""
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "429": {
            "description": "Work budget exhausted",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "503": {
            "description": "Admission queue full",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          }
        }
      }
    },
    "/eclipse": {
      "get": {
        "operationId": "eclipse",
        "summary": "Eclipse geometry at one instant. Supply both lat and lon for local circumstances.",
        "x-work-credits": 1,
        "parameters": [
          {
            "name": "at",
            "in": "query",
            "required": false,
            "description": "RFC 3339 instant in 1900\u20132050 UTC; defaults to now.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "lat",
            "in": "query",
            "required": false,
            "description": "Latitude, decimal degrees.",
            "schema": {
              "type": "number",
              "minimum": -90,
              "maximum": 90
            }
          },
          {
            "name": "lon",
            "in": "query",
            "required": false,
            "description": "Longitude, decimal degrees east of Greenwich.",
            "schema": {
              "type": "number",
              "minimum": -180,
              "maximum": 180
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful JSON answer. Protected answers are no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/eclipseResponse"
                }
              }
            }
          },
          "304": {
            "description": "Matching If-None-Match; no body. Authentication and work credits still apply."
          },
          "400": {
            "description": "Invalid input",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string",
                  "example": "Bearer realm=\"skyoracle\""
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "429": {
            "description": "Work budget exhausted",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          },
          "503": {
            "description": "Admission queue full",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/errorBody"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Manually issued API token in the Authorization header. Never put it in a URL."
      }
    },
    "schemas": {
      "healthResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "build_time": {
            "type": "string"
          },
          "uptime_seconds": {
            "type": "integer"
          },
          "now_utc": {
            "type": "string"
          }
        },
        "required": [
          "status",
          "version",
          "build_time",
          "uptime_seconds",
          "now_utc"
        ],
        "additionalProperties": true
      },
      "errorBody": {
        "type": "object",
        "properties": {
          "error": {
            "$ref": "#/components/schemas/errorDetail"
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": true
      },
      "errorDetail": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "additionalProperties": true
      },
      "positionResponse": {
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "utc": {
            "type": "string"
          },
          "sun": {
            "$ref": "#/components/schemas/bodyPosition"
          },
          "moon": {
            "$ref": "#/components/schemas/moonPosition"
          },
          "light_kind": {
            "type": "string"
          },
          "shadow": {
            "$ref": "#/components/schemas/shadowBody"
          }
        },
        "required": [
          "latitude",
          "longitude",
          "utc",
          "sun",
          "moon",
          "light_kind",
          "shadow"
        ],
        "additionalProperties": true
      },
      "bodyPosition": {
        "type": "object",
        "properties": {
          "azimuth_deg": {
            "type": "number"
          },
          "elevation_deg": {
            "type": "number"
          }
        },
        "required": [
          "azimuth_deg",
          "elevation_deg"
        ],
        "additionalProperties": true
      },
      "moonPosition": {
        "type": "object",
        "properties": {
          "azimuth_deg": {
            "type": "number"
          },
          "elevation_deg": {
            "type": "number"
          },
          "phase_fraction": {
            "type": "number"
          },
          "phase_name": {
            "type": "string"
          },
          "illumination": {
            "type": "number"
          },
          "effective_illumination": {
            "type": "number"
          },
          "distance_km": {
            "type": "number"
          }
        },
        "required": [
          "azimuth_deg",
          "elevation_deg",
          "phase_fraction",
          "phase_name",
          "illumination",
          "effective_illumination",
          "distance_km"
        ],
        "additionalProperties": true
      },
      "shadowBody": {
        "type": "object",
        "properties": {
          "caster": {
            "type": "string"
          },
          "height_m": {
            "type": "number"
          },
          "length_m": {
            "type": "number"
          },
          "bearing_deg": {
            "type": "number"
          }
        },
        "required": [
          "caster",
          "height_m"
        ],
        "additionalProperties": true
      },
      "eventsResponse": {
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "zone": {
            "type": "string"
          },
          "date": {
            "type": "string"
          },
          "day_start_utc": {
            "type": "string"
          },
          "day_minutes": {
            "type": "integer"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/eventRow"
            }
          },
          "solar_noon": {
            "$ref": "#/components/schemas/transitRow"
          },
          "moon_day": {
            "$ref": "#/components/schemas/moonDayRow"
          }
        },
        "required": [
          "latitude",
          "longitude",
          "zone",
          "date",
          "day_start_utc",
          "day_minutes",
          "events",
          "moon_day"
        ],
        "additionalProperties": true
      },
      "eventRow": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "utc": {
            "type": "string"
          },
          "local": {
            "type": "string"
          },
          "rate_deg_per_hour": {
            "type": "number"
          }
        },
        "required": [
          "kind",
          "utc",
          "local",
          "rate_deg_per_hour"
        ],
        "additionalProperties": true
      },
      "transitRow": {
        "type": "object",
        "properties": {
          "utc": {
            "type": "string"
          },
          "local": {
            "type": "string"
          },
          "sun_elevation_deg": {
            "type": "number"
          }
        },
        "required": [
          "utc",
          "local",
          "sun_elevation_deg"
        ],
        "additionalProperties": true
      },
      "moonDayRow": {
        "type": "object",
        "properties": {
          "rise_utc": {
            "type": "string"
          },
          "set_utc": {
            "type": "string"
          },
          "always_up": {
            "type": "boolean"
          },
          "always_down": {
            "type": "boolean"
          }
        },
        "required": [
          "always_up",
          "always_down"
        ],
        "additionalProperties": true
      },
      "report_Sky": {
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "zone": {
            "type": "string"
          },
          "date": {
            "type": "string"
          },
          "day_start_utc": {
            "type": "string"
          },
          "day_minutes": {
            "type": "integer"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/report_Event"
            }
          },
          "solar_noon": {
            "$ref": "#/components/schemas/report_Transit"
          },
          "moon_day": {
            "$ref": "#/components/schemas/report_MoonDay"
          },
          "at_21_local": {
            "$ref": "#/components/schemas/report_Evening"
          }
        },
        "required": [
          "latitude",
          "longitude",
          "zone",
          "date",
          "day_start_utc",
          "day_minutes",
          "events",
          "moon_day",
          "at_21_local"
        ],
        "additionalProperties": true
      },
      "report_Event": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "local": {
            "type": "string"
          },
          "utc": {
            "type": "string"
          },
          "rate_deg_per_hour": {
            "type": "number"
          }
        },
        "required": [
          "kind",
          "local",
          "utc",
          "rate_deg_per_hour"
        ],
        "additionalProperties": true
      },
      "report_Transit": {
        "type": "object",
        "properties": {
          "local": {
            "type": "string"
          },
          "utc": {
            "type": "string"
          },
          "sun_azimuth_deg": {
            "type": "number"
          },
          "sun_elevation_deg": {
            "type": "number"
          },
          "moon_azimuth_deg": {
            "type": "number"
          },
          "moon_elevation_deg": {
            "type": "number"
          }
        },
        "required": [
          "local",
          "utc",
          "sun_azimuth_deg",
          "sun_elevation_deg",
          "moon_azimuth_deg",
          "moon_elevation_deg"
        ],
        "additionalProperties": true
      },
      "report_MoonDay": {
        "type": "object",
        "properties": {
          "rise_local": {
            "type": "string"
          },
          "rise_utc": {
            "type": "string"
          },
          "set_local": {
            "type": "string"
          },
          "set_utc": {
            "type": "string"
          },
          "always_up": {
            "type": "boolean"
          },
          "always_down": {
            "type": "boolean"
          }
        },
        "required": [
          "always_up",
          "always_down"
        ],
        "additionalProperties": true
      },
      "report_Evening": {
        "type": "object",
        "properties": {
          "local": {
            "type": "string"
          },
          "utc": {
            "type": "string"
          },
          "phase_fraction": {
            "type": "number"
          },
          "phase_name": {
            "type": "string"
          },
          "illumination": {
            "type": "number"
          },
          "effective_illumination": {
            "type": "number"
          },
          "shadow_caster": {
            "type": "string"
          }
        },
        "required": [
          "local",
          "utc",
          "phase_fraction",
          "phase_name",
          "illumination",
          "effective_illumination",
          "shadow_caster"
        ],
        "additionalProperties": true
      },
      "ephemerisResponse": {
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "from_utc": {
            "type": "string"
          },
          "to_utc": {
            "type": "string"
          },
          "step": {
            "type": "string"
          },
          "rows": {
            "type": "integer"
          },
          "max_rows": {
            "type": "integer"
          },
          "truncated": {
            "type": "boolean"
          },
          "ephemeris": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ephemerisRow"
            }
          }
        },
        "required": [
          "latitude",
          "longitude",
          "from_utc",
          "to_utc",
          "step",
          "rows",
          "max_rows",
          "truncated",
          "ephemeris"
        ],
        "additionalProperties": true
      },
      "ephemerisRow": {
        "type": "object",
        "properties": {
          "utc": {
            "type": "string"
          },
          "sun": {
            "$ref": "#/components/schemas/bodyPosition"
          },
          "moon": {
            "$ref": "#/components/schemas/bodyPosition"
          }
        },
        "required": [
          "utc",
          "sun",
          "moon"
        ],
        "additionalProperties": true
      },
      "eclipseResponse": {
        "type": "object",
        "properties": {
          "utc": {
            "type": "string"
          },
          "lunar": {
            "$ref": "#/components/schemas/lunarEclipseBody"
          },
          "solar": {
            "$ref": "#/components/schemas/solarEclipseBody"
          },
          "local": {
            "$ref": "#/components/schemas/localEclipseBody"
          }
        },
        "required": [
          "utc",
          "lunar",
          "solar"
        ],
        "additionalProperties": true
      },
      "lunarEclipseBody": {
        "type": "object",
        "properties": {
          "in_eclipse": {
            "type": "boolean"
          },
          "separation_deg": {
            "type": "number"
          },
          "umbra_radius_deg": {
            "type": "number"
          },
          "penumbra_radius_deg": {
            "type": "number"
          },
          "moon_semidiameter_deg": {
            "type": "number"
          },
          "umbral_magnitude": {
            "type": "number"
          },
          "penumbral_magnitude": {
            "type": "number"
          },
          "effective_illumination": {
            "type": "number"
          }
        },
        "required": [
          "in_eclipse",
          "separation_deg",
          "umbra_radius_deg",
          "penumbra_radius_deg",
          "moon_semidiameter_deg",
          "umbral_magnitude",
          "penumbral_magnitude",
          "effective_illumination"
        ],
        "additionalProperties": true
      },
      "solarEclipseBody": {
        "type": "object",
        "properties": {
          "gamma": {
            "type": "number"
          },
          "central": {
            "type": "boolean"
          },
          "magnitude": {
            "type": "number"
          }
        },
        "required": [
          "gamma",
          "central"
        ],
        "additionalProperties": true
      },
      "localEclipseBody": {
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "sun": {
            "$ref": "#/components/schemas/localBodyPosition"
          },
          "moon": {
            "$ref": "#/components/schemas/localBodyPosition"
          },
          "solar_eclipse": {
            "$ref": "#/components/schemas/localSolarEclipse"
          },
          "lunar_eclipse": {
            "$ref": "#/components/schemas/localLunarEclipse"
          }
        },
        "required": [
          "latitude",
          "longitude",
          "sun",
          "moon",
          "solar_eclipse",
          "lunar_eclipse"
        ],
        "additionalProperties": true
      },
      "localBodyPosition": {
        "type": "object",
        "properties": {
          "azimuth_deg": {
            "type": "number"
          },
          "elevation_deg": {
            "type": "number"
          },
          "above_horizon": {
            "type": "boolean"
          }
        },
        "required": [
          "azimuth_deg",
          "elevation_deg",
          "above_horizon"
        ],
        "additionalProperties": true
      },
      "localSolarEclipse": {
        "type": "object",
        "properties": {
          "separation_deg": {
            "type": "number"
          },
          "sun_semidiameter_deg": {
            "type": "number"
          },
          "moon_semidiameter_deg": {
            "type": "number"
          },
          "obscuration": {
            "type": "number"
          },
          "external_contact_gap_deg": {
            "type": "number"
          },
          "internal_contact_gap_deg": {
            "type": "number"
          },
          "phase": {
            "type": "string"
          },
          "visible": {
            "type": "boolean"
          }
        },
        "required": [
          "separation_deg",
          "sun_semidiameter_deg",
          "moon_semidiameter_deg",
          "obscuration",
          "external_contact_gap_deg",
          "internal_contact_gap_deg",
          "phase",
          "visible"
        ],
        "additionalProperties": true
      },
      "localLunarEclipse": {
        "type": "object",
        "properties": {
          "phase": {
            "type": "string"
          },
          "visible": {
            "type": "boolean"
          }
        },
        "required": [
          "phase",
          "visible"
        ],
        "additionalProperties": true
      }
    }
  }
}
