{
  "openapi": "3.1.0",
  "info": {
    "title": "HealthAPI",
    "version": "1.0.0",
    "summary": "Read your own Apple Health data as JSON.",
    "description": "HealthAPI is a read-only JSON API over **your own Apple Health data**, synced from the HealthAPI iPhone app. A key only ever reads the data of the account it belongs to. Human-readable docs: https://healthapi.app/docs\n\n**Authentication.** Send your personal API key as `Authorization: Bearer <key>`. Get it in the HealthAPI iPhone app under Connect > Other tools.\n\n**Dates and timezones.** `date` parameters are local calendar days (`YYYY-MM-DD`) in the account's timezone, which the app keeps current; the summary endpoints (`/query/*`, `/health/daily-summary`, `/health/weekly-summary`) also take `tz` (an IANA name) to override it. A day runs local midnight to midnight. In responses, `*_utc` timestamps are UTC and `*_local` timestamps carry the local offset.\n\n**Units.** Percentages in raw readings (oxygenSaturation, bodyFatPercentage, ...) are fractions: 0.97 means 97%. The summary endpoints convert SpO2 to a 0-100 percent (`spo2_pct`).\n\n**Daily request limits** (per account; the window resets at 00:00 UTC):\n\n| Plan | Requests per day |\n|---|---|\n| Free preview (before subscribing) | 25 |\n| Pro | 100 |\n| Developer | 1,000 |\n\nEvery health-data read counts: each call to `/health/*` and `/query/*`, including calls rejected with 400 or 422. `/usage`, `/gdpr/*` and `/account/*` are not counted. Counted responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (epoch seconds). Past the limit you get `429` with `Retry-After` (seconds). Check `GET /usage` before a batch of calls. Separately, each endpoint allows about 100 requests per minute per IP address; on uncounted endpoints the `X-RateLimit-*` headers describe that per-minute limit (Reset in seconds), not the daily quota.\n\n**Access.** Health-data reads need an active subscription or free preview, otherwise they return `402` with `code: \"subscription_required\"`. Nothing is deleted; `/usage`, `/gdpr/export` and `/account/*` keep working.\n\n**Errors** are JSON with a `detail` field. Usually `detail` is a string (`{\"detail\": \"Invalid API key\"}`). For `422` it is a list of `{type, loc, msg, input}` objects naming the bad parameter (`loc` is like `[\"query\", \"days\"]`). `402` and `429` add machine-readable fields beside `detail`.\n",
    "contact": {
      "name": "HealthAPI",
      "url": "https://healthapi.app/contact"
    },
    "termsOfService": "https://healthapi.app/terms"
  },
  "externalDocs": {
    "description": "API reference",
    "url": "https://healthapi.app/docs"
  },
  "servers": [
    {
      "url": "https://healthapi.app/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Summaries",
      "description": "Daily, weekly and trend aggregates. Start here."
    },
    {
      "name": "Raw data",
      "description": "Individual readings, workouts and latest values."
    },
    {
      "name": "Account",
      "description": "Quota, key rotation, export and deletion."
    }
  ],
  "paths": {
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "tags": [
          "Account"
        ],
        "summary": "Get plan, daily quota and access status",
        "description": "Use to see how many requests remain today, when the quota resets, and whether the account can read health data (access_status, locked). Free: it never counts toward the daily limit, and it works even when the subscription has lapsed.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                },
                "example": {
                  "plan_tier": "pro",
                  "plan_label": "Pro",
                  "window": "1d",
                  "limit": 100,
                  "used": 7,
                  "remaining": 93,
                  "reset_at": "2026-10-10T00:00:00.000Z",
                  "apple_linked": true,
                  "access_status": "subscribed",
                  "trial_ends_at": null,
                  "deletion_scheduled_at": null,
                  "locked": false
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/query/daily-summary": {
      "get": {
        "operationId": "getDailySummary",
        "tags": [
          "Summaries"
        ],
        "summary": "Steps, average heart rate and workouts for one day",
        "description": "Use for a quick answer about one local day: step total (deduplicated across iPhone and Watch), average heart rate and that day's workouts (durations in minutes). Omit date for today. For energy, distance, sleep, HRV and more, use getFullDailySummary.",
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": false,
            "description": "Local day, YYYY-MM-DD. Defaults to today in the account's timezone (or tz). Other formats return 400.",
            "schema": {
              "type": "string",
              "format": "date",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "example": "2026-10-08"
          },
          {
            "$ref": "#/components/parameters/Tz"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DailySummary"
                },
                "example": {
                  "date": "2026-10-08",
                  "timezone": "America/New_York",
                  "range_local": {
                    "start": "2026-10-08T00:00:00.000-04:00",
                    "end": "2026-10-09T00:00:00.000-04:00"
                  },
                  "range_utc": {
                    "start": "2026-10-08T04:00:00.000Z",
                    "end": "2026-10-09T04:00:00.000Z"
                  },
                  "steps": 11294,
                  "avg_heart_rate": 71.8,
                  "workouts": [
                    {
                      "type": "strength_training",
                      "duration_minutes": 30,
                      "calories": 210,
                      "start_date_utc": "2026-10-08T12:00:00.000Z",
                      "end_date_utc": "2026-10-08T12:30:00.000Z",
                      "start_date_local": "2026-10-08T08:00:00.000-04:00",
                      "end_date_local": "2026-10-08T08:30:00.000-04:00"
                    },
                    {
                      "type": "running",
                      "duration_minutes": 45,
                      "calories": 512.3,
                      "start_date_utc": "2026-10-08T22:00:00.000Z",
                      "end_date_utc": "2026-10-08T22:45:00.000Z",
                      "start_date_local": "2026-10-08T18:00:00.000-04:00",
                      "end_date_local": "2026-10-08T18:45:00.000-04:00"
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/SubscriptionRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/query/trends": {
      "get": {
        "operationId": "getTrends",
        "tags": [
          "Summaries"
        ],
        "summary": "Daily values of one metric over the last N days",
        "description": "Use for trends and comparisons over time. One entry per local day with data, ending today; days without data are left out. aggregation sum: read total (steps, energy, ...). aggregation avg: read avg/min/max (heart rate, weight, ...). Not for sleep: use getFullDailySummary.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": true,
            "description": "Metric to chart, case-sensitive HealthKit name.",
            "schema": {
              "$ref": "#/components/schemas/TrendMetricType"
            },
            "example": "heartRate"
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "How many local days to cover, counting back from and including today (today is partial).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 7
            },
            "example": 30
          },
          {
            "$ref": "#/components/parameters/Tz"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Trend"
                },
                "examples": {
                  "averaged": {
                    "summary": "A reading metric (heartRate)",
                    "value": {
                      "type": "heartRate",
                      "timezone": "America/New_York",
                      "period_days": 3,
                      "aggregation": "avg",
                      "range_local": {
                        "start": "2026-10-07T00:00:00.000-04:00",
                        "end": "2026-10-10T00:00:00.000-04:00"
                      },
                      "range_utc": {
                        "start": "2026-10-07T04:00:00.000Z",
                        "end": "2026-10-10T04:00:00.000Z"
                      },
                      "start_date": "2026-10-07",
                      "end_date": "2026-10-09",
                      "data": [
                        {
                          "date": "2026-10-07",
                          "avg": 68.4,
                          "min": 51,
                          "max": 131,
                          "total": null,
                          "samples": null
                        },
                        {
                          "date": "2026-10-08",
                          "avg": 71.83,
                          "min": 52,
                          "max": 142,
                          "total": null,
                          "samples": null
                        }
                      ]
                    }
                  },
                  "summed": {
                    "summary": "A summed metric (stepCount)",
                    "value": {
                      "type": "stepCount",
                      "timezone": "America/New_York",
                      "period_days": 3,
                      "aggregation": "sum",
                      "range_local": {
                        "start": "2026-10-07T00:00:00.000-04:00",
                        "end": "2026-10-10T00:00:00.000-04:00"
                      },
                      "range_utc": {
                        "start": "2026-10-07T04:00:00.000Z",
                        "end": "2026-10-10T04:00:00.000Z"
                      },
                      "start_date": "2026-10-07",
                      "end_date": "2026-10-09",
                      "data": [
                        {
                          "date": "2026-10-07",
                          "avg": null,
                          "min": null,
                          "max": null,
                          "total": 9120,
                          "samples": null
                        },
                        {
                          "date": "2026-10-08",
                          "avg": null,
                          "min": null,
                          "max": null,
                          "total": 11294,
                          "samples": null
                        },
                        {
                          "date": "2026-10-09",
                          "avg": null,
                          "min": null,
                          "max": null,
                          "total": 2310,
                          "samples": null
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/SubscriptionRequired"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/health/daily-summary": {
      "get": {
        "operationId": "getFullDailySummary",
        "tags": [
          "Summaries"
        ],
        "summary": "Full daily roll-up: activity, energy, heart, sleep, workouts",
        "description": "Use for a complete picture of one local day: steps, distance, active and basal energy, flights, exercise minutes, average/resting heart rate, HRV, SpO2 (percent), respiratory rate, last night's sleep (hours) and workout totals (seconds). date is required.",
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": true,
            "description": "Local day, YYYY-MM-DD. Other formats, or a date more than a day in the future, return 400.",
            "schema": {
              "type": "string",
              "format": "date",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "example": "2026-10-08"
          },
          {
            "$ref": "#/components/parameters/Tz"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FullDailySummary"
                },
                "example": {
                  "date": "2026-10-08",
                  "timezone": "America/New_York",
                  "range_utc": {
                    "start": "2026-10-08T04:00:00.000Z",
                    "end": "2026-10-09T04:00:00.000Z"
                  },
                  "totals": {
                    "steps": 11294,
                    "distance_m": 8120.4,
                    "active_energy_kcal": 540.2,
                    "basal_energy_kcal": 1633.1,
                    "flights_climbed": 9,
                    "exercise_time_min": 41
                  },
                  "averages": {
                    "heart_rate_bpm": 71.83,
                    "resting_heart_rate_bpm": 55,
                    "hrv_ms": 48.2,
                    "spo2_pct": 97,
                    "respiratory_rate_bpm": 14.1
                  },
                  "sleep": {
                    "total_hours": 6.83,
                    "session_count": 13
                  },
                  "workouts": {
                    "count": 2,
                    "total_duration_seconds": 4500,
                    "total_calories_kcal": 722.3
                  },
                  "data_coverage_pct": 100,
                  "sleep_hours": 6.83,
                  "sleep_minutes": 409.8,
                  "sleep_session_count": 13,
                  "workout_count": 2,
                  "workout_duration_seconds": 4500,
                  "workout_duration_minutes": 75,
                  "workout_calories_kcal": 722.3,
                  "workout_calories": 722.3
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/SubscriptionRequired"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/health/weekly-summary": {
      "get": {
        "operationId": "getWeeklySummary",
        "tags": [
          "Summaries"
        ],
        "summary": "Totals and averages for one ISO week, with a per-day breakdown",
        "description": "Use for a whole week (Monday to Sunday, local time): the same fields as getFullDailySummary, totalled and averaged over the week, plus daily_breakdown with one entry per day. week is required, format YYYY-Www, e.g. 2026-W41.",
        "parameters": [
          {
            "name": "week",
            "in": "query",
            "required": true,
            "description": "ISO 8601 week, YYYY-Www (week 01-53). Other formats return 400.",
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-W\\d{2}$"
            },
            "example": "2026-W41"
          },
          {
            "$ref": "#/components/parameters/Tz"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WeeklySummary"
                },
                "example": {
                  "week": "2026-W41",
                  "timezone": "America/New_York",
                  "week_start_date": "2026-10-05",
                  "range_utc": {
                    "start": "2026-10-05T04:00:00.000Z",
                    "end": "2026-10-12T04:00:00.000Z"
                  },
                  "totals": {
                    "steps": 11294,
                    "distance_m": 8120.4,
                    "active_energy_kcal": 540.2,
                    "basal_energy_kcal": 1633.1,
                    "flights_climbed": 9,
                    "exercise_time_min": 41
                  },
                  "averages": {
                    "heart_rate_bpm": 71.83,
                    "resting_heart_rate_bpm": 55,
                    "hrv_ms": 48.2,
                    "spo2_pct": 97,
                    "respiratory_rate_bpm": 14.1
                  },
                  "sleep": {
                    "total_hours": 6.83,
                    "session_count": 13
                  },
                  "workouts": {
                    "count": 2,
                    "total_duration_seconds": 4500,
                    "total_calories_kcal": 722.3
                  },
                  "data_coverage_pct": 100,
                  "sleep_hours": 6.83,
                  "sleep_minutes": 409.8,
                  "sleep_session_count": 13,
                  "workout_count": 2,
                  "workout_duration_seconds": 4500,
                  "workout_duration_minutes": 75,
                  "workout_calories_kcal": 722.3,
                  "workout_calories": 722.3,
                  "daily_breakdown": [
                    {
                      "date": "2026-10-05",
                      "totals": {
                        "steps": 0,
                        "distance_m": 0,
                        "active_energy_kcal": 0,
                        "basal_energy_kcal": 0,
                        "flights_climbed": 0,
                        "exercise_time_min": 0
                      },
                      "averages": {
                        "heart_rate_bpm": null,
                        "resting_heart_rate_bpm": null,
                        "hrv_ms": null,
                        "spo2_pct": null,
                        "respiratory_rate_bpm": null
                      },
                      "sleep": {
                        "total_hours": 0,
                        "session_count": 0
                      },
                      "workouts": {
                        "count": 0,
                        "total_duration_seconds": 0,
                        "total_calories_kcal": 0
                      },
                      "data_coverage_pct": 0,
                      "sleep_hours": 0,
                      "sleep_minutes": 0,
                      "sleep_session_count": 0,
                      "workout_count": 0,
                      "workout_duration_seconds": 0,
                      "workout_duration_minutes": 0,
                      "workout_calories_kcal": 0,
                      "workout_calories": 0
                    },
                    {
                      "date": "2026-10-06",
                      "totals": {
                        "steps": 0,
                        "distance_m": 0,
                        "active_energy_kcal": 0,
                        "basal_energy_kcal": 0,
                        "flights_climbed": 0,
                        "exercise_time_min": 0
                      },
                      "averages": {
                        "heart_rate_bpm": null,
                        "resting_heart_rate_bpm": null,
                        "hrv_ms": null,
                        "spo2_pct": null,
                        "respiratory_rate_bpm": null
                      },
                      "sleep": {
                        "total_hours": 0,
                        "session_count": 0
                      },
                      "workouts": {
                        "count": 0,
                        "total_duration_seconds": 0,
                        "total_calories_kcal": 0
                      },
                      "data_coverage_pct": 0,
                      "sleep_hours": 0,
                      "sleep_minutes": 0,
                      "sleep_session_count": 0,
                      "workout_count": 0,
                      "workout_duration_seconds": 0,
                      "workout_duration_minutes": 0,
                      "workout_calories_kcal": 0,
                      "workout_calories": 0
                    },
                    {
                      "date": "2026-10-07",
                      "totals": {
                        "steps": 0,
                        "distance_m": 0,
                        "active_energy_kcal": 0,
                        "basal_energy_kcal": 0,
                        "flights_climbed": 0,
                        "exercise_time_min": 0
                      },
                      "averages": {
                        "heart_rate_bpm": null,
                        "resting_heart_rate_bpm": null,
                        "hrv_ms": null,
                        "spo2_pct": null,
                        "respiratory_rate_bpm": null
                      },
                      "sleep": {
                        "total_hours": 0,
                        "session_count": 0
                      },
                      "workouts": {
                        "count": 0,
                        "total_duration_seconds": 0,
                        "total_calories_kcal": 0
                      },
                      "data_coverage_pct": 0,
                      "sleep_hours": 0,
                      "sleep_minutes": 0,
                      "sleep_session_count": 0,
                      "workout_count": 0,
                      "workout_duration_seconds": 0,
                      "workout_duration_minutes": 0,
                      "workout_calories_kcal": 0,
                      "workout_calories": 0
                    },
                    {
                      "date": "2026-10-08",
                      "totals": {
                        "steps": 11294,
                        "distance_m": 8120.4,
                        "active_energy_kcal": 540.2,
                        "basal_energy_kcal": 1633.1,
                        "flights_climbed": 9,
                        "exercise_time_min": 41
                      },
                      "averages": {
                        "heart_rate_bpm": 71.83,
                        "resting_heart_rate_bpm": 55,
                        "hrv_ms": 48.2,
                        "spo2_pct": 97,
                        "respiratory_rate_bpm": 14.1
                      },
                      "sleep": {
                        "total_hours": 6.83,
                        "session_count": 13
                      },
                      "workouts": {
                        "count": 2,
                        "total_duration_seconds": 4500,
                        "total_calories_kcal": 722.3
                      },
                      "data_coverage_pct": 100,
                      "sleep_hours": 6.83,
                      "sleep_minutes": 409.8,
                      "sleep_session_count": 13,
                      "workout_count": 2,
                      "workout_duration_seconds": 4500,
                      "workout_duration_minutes": 75,
                      "workout_calories_kcal": 722.3,
                      "workout_calories": 722.3
                    },
                    {
                      "date": "2026-10-09",
                      "totals": {
                        "steps": 0,
                        "distance_m": 0,
                        "active_energy_kcal": 0,
                        "basal_energy_kcal": 0,
                        "flights_climbed": 0,
                        "exercise_time_min": 0
                      },
                      "averages": {
                        "heart_rate_bpm": null,
                        "resting_heart_rate_bpm": null,
                        "hrv_ms": null,
                        "spo2_pct": null,
                        "respiratory_rate_bpm": null
                      },
                      "sleep": {
                        "total_hours": 0,
                        "session_count": 0
                      },
                      "workouts": {
                        "count": 0,
                        "total_duration_seconds": 0,
                        "total_calories_kcal": 0
                      },
                      "data_coverage_pct": 0,
                      "sleep_hours": 0,
                      "sleep_minutes": 0,
                      "sleep_session_count": 0,
                      "workout_count": 0,
                      "workout_duration_seconds": 0,
                      "workout_duration_minutes": 0,
                      "workout_calories_kcal": 0,
                      "workout_calories": 0
                    },
                    {
                      "date": "2026-10-10",
                      "totals": {
                        "steps": 0,
                        "distance_m": 0,
                        "active_energy_kcal": 0,
                        "basal_energy_kcal": 0,
                        "flights_climbed": 0,
                        "exercise_time_min": 0
                      },
                      "averages": {
                        "heart_rate_bpm": null,
                        "resting_heart_rate_bpm": null,
                        "hrv_ms": null,
                        "spo2_pct": null,
                        "respiratory_rate_bpm": null
                      },
                      "sleep": {
                        "total_hours": 0,
                        "session_count": 0
                      },
                      "workouts": {
                        "count": 0,
                        "total_duration_seconds": 0,
                        "total_calories_kcal": 0
                      },
                      "data_coverage_pct": 0,
                      "sleep_hours": 0,
                      "sleep_minutes": 0,
                      "sleep_session_count": 0,
                      "workout_count": 0,
                      "workout_duration_seconds": 0,
                      "workout_duration_minutes": 0,
                      "workout_calories_kcal": 0,
                      "workout_calories": 0
                    },
                    {
                      "date": "2026-10-11",
                      "totals": {
                        "steps": 0,
                        "distance_m": 0,
                        "active_energy_kcal": 0,
                        "basal_energy_kcal": 0,
                        "flights_climbed": 0,
                        "exercise_time_min": 0
                      },
                      "averages": {
                        "heart_rate_bpm": null,
                        "resting_heart_rate_bpm": null,
                        "hrv_ms": null,
                        "spo2_pct": null,
                        "respiratory_rate_bpm": null
                      },
                      "sleep": {
                        "total_hours": 0,
                        "session_count": 0
                      },
                      "workouts": {
                        "count": 0,
                        "total_duration_seconds": 0,
                        "total_calories_kcal": 0
                      },
                      "data_coverage_pct": 0,
                      "sleep_hours": 0,
                      "sleep_minutes": 0,
                      "sleep_session_count": 0,
                      "workout_count": 0,
                      "workout_duration_seconds": 0,
                      "workout_duration_minutes": 0,
                      "workout_calories_kcal": 0,
                      "workout_calories": 0
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/SubscriptionRequired"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/health/samples": {
      "get": {
        "operationId": "getSamples",
        "tags": [
          "Raw data"
        ],
        "summary": "Individual readings, newest first",
        "description": "Use only when individual readings are needed (e.g. every heart-rate reading in a day). Newest first; page with limit/offset. Each row has a kind: reading, total or daily_average. Only kind=total rows are safe to add up; for daily figures prefer getTrends or getFullDailySummary.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Only this metric. Omit for all metrics mixed together.",
            "schema": {
              "$ref": "#/components/schemas/HealthMetricType"
            },
            "example": "heartRate"
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Only rows of this kind. reading: a sample as Apple Health recorded it (iPhone and Watch can overlap). total: a deduplicated sum for its start..end, safe to add up. daily_average: one local day's average of a point-in-time metric.",
            "schema": {
              "$ref": "#/components/schemas/SampleKind"
            }
          },
          {
            "$ref": "#/components/parameters/StartDate"
          },
          {
            "$ref": "#/components/parameters/EndDate"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          },
          {
            "name": "metric",
            "in": "query",
            "required": false,
            "deprecated": true,
            "description": "Deprecated alias of type; ignored when type is given.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "deprecated": true,
            "description": "Deprecated alias of start_date; only its first 10 characters (the date) are used.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "deprecated": true,
            "description": "Deprecated alias of end_date; only its first 10 characters (the date) are used.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Sample"
                  }
                },
                "example": [
                  {
                    "id": 383696,
                    "type": "heartRate",
                    "kind": "reading",
                    "value": 64,
                    "unit": "count/min",
                    "start_date": "2026-10-08T15:05:00.000Z",
                    "end_date": "2026-10-08T15:05:00.000Z",
                    "synced_at": "2026-10-08T15:20:50.090Z",
                    "timezone": "America/New_York",
                    "start_date_utc": "2026-10-08T15:05:00.000Z",
                    "end_date_utc": "2026-10-08T15:05:00.000Z",
                    "synced_at_utc": "2026-10-08T15:20:50.090Z",
                    "start_date_local": "2026-10-08T11:05:00.000-04:00",
                    "end_date_local": "2026-10-08T11:05:00.000-04:00",
                    "synced_at_local": "2026-10-08T11:20:50.090-04:00"
                  }
                ]
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/SubscriptionRequired"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/health/workouts": {
      "get": {
        "operationId": "getWorkouts",
        "tags": [
          "Raw data"
        ],
        "summary": "Workout sessions, newest first",
        "description": "Use to list workouts: type, start and end, duration (seconds), distance (meters) and active energy (kcal), newest first. Filter by activity_type and a local date range; page with limit/offset.",
        "parameters": [
          {
            "name": "activity_type",
            "in": "query",
            "required": false,
            "description": "Only this workout type (exact match).",
            "schema": {
              "$ref": "#/components/schemas/WorkoutActivityType"
            },
            "example": "running"
          },
          {
            "$ref": "#/components/parameters/StartDate"
          },
          {
            "$ref": "#/components/parameters/EndDate"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Workout"
                  }
                },
                "example": [
                  {
                    "id": 188,
                    "activity_type": "running",
                    "start_date": "2026-10-08T22:00:00.000Z",
                    "end_date": "2026-10-08T22:45:00.000Z",
                    "duration_seconds": 2700,
                    "total_distance_meters": 7200.5,
                    "total_energy_kcal": 512.3,
                    "source_app": "Apple Watch",
                    "created_at": "2026-10-08T23:01:12.104Z"
                  },
                  {
                    "id": 189,
                    "activity_type": "strength_training",
                    "start_date": "2026-10-08T12:00:00.000Z",
                    "end_date": "2026-10-08T12:30:00.000Z",
                    "duration_seconds": 1800,
                    "total_distance_meters": null,
                    "total_energy_kcal": 210,
                    "source_app": "Apple Watch",
                    "created_at": "2026-10-08T12:41:03.551Z"
                  }
                ]
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/SubscriptionRequired"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/health/latest": {
      "get": {
        "operationId": "getLatestReadings",
        "tags": [
          "Raw data"
        ],
        "summary": "Most recent reading of every metric",
        "description": "Use for 'what is my latest X' (weight, resting heart rate, ...): the newest single reading per metric, keyed by name. These are single readings, not daily totals: stepCount is one sample's steps. For today's totals use getFullDailySummary.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LatestReadings"
                },
                "example": {
                  "bodyMass": {
                    "value": 72.4,
                    "unit": "kg",
                    "date": "2026-10-08T11:00:00.000Z"
                  },
                  "heartRate": {
                    "value": 64,
                    "unit": "count/min",
                    "date": "2026-10-08T17:05:00.000Z"
                  },
                  "heartRateVariability": {
                    "value": 48,
                    "unit": "ms",
                    "date": "2026-10-08T12:10:00.000Z"
                  },
                  "oxygenSaturation": {
                    "value": 0.97,
                    "unit": "%",
                    "date": "2026-10-08T12:20:00.000Z"
                  },
                  "restingHeartRate": {
                    "value": 55,
                    "unit": "count/min",
                    "date": "2026-10-08T12:00:00.000Z"
                  },
                  "stepCount": {
                    "value": 412,
                    "unit": "count",
                    "date": "2026-10-08T16:00:00.000Z"
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/SubscriptionRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/gdpr/export": {
      "get": {
        "operationId": "exportAllData",
        "tags": [
          "Account"
        ],
        "summary": "Download everything stored for the account (large JSON)",
        "description": "Use only when the user asks for a full export or backup. One JSON document: profile, every health sample, workouts, last 30 days of API requests, subscriptions. Streamed; can be hundreds of MB for years of data. Not counted toward the daily limit.",
        "responses": {
          "200": {
            "description": "The export, streamed as a JSON attachment (healthapi-export-<id>.json).",
            "headers": {
              "Content-Disposition": {
                "description": "attachment; filename=healthapi-export-<user id>.json",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataExport"
                },
                "example": {
                  "user": {
                    "id": 1935,
                    "email": null,
                    "device_id": "3F2A9C1E-7B4D-4E0A-9D6C-2B8F1A5E7C30",
                    "device_name": "iPhone",
                    "platform": "iOS",
                    "provider": "device",
                    "plan_tier": "pro",
                    "created_at": "2026-09-30T18:02:11.000Z",
                    "last_active_at": "2026-10-09T07:20:06.000Z"
                  },
                  "health_samples": [
                    {
                      "id": 383685,
                      "type": "stepCount",
                      "value": 412,
                      "unit": "count",
                      "start_date": "2026-10-08T13:00:00.000Z",
                      "end_date": "2026-10-08T13:30:00.000Z",
                      "source_app": "iPhone",
                      "device": null,
                      "synced_at": "2026-10-08T15:20:50.090Z"
                    }
                  ],
                  "health_samples_count": 1,
                  "workouts": [
                    {
                      "id": 188,
                      "activity_type": "running",
                      "start_date": "2026-10-08T22:00:00.000Z",
                      "end_date": "2026-10-08T22:45:00.000Z",
                      "duration_seconds": 2700,
                      "total_distance_meters": 7200.5,
                      "total_energy_kcal": 512.3,
                      "source_app": "Apple Watch",
                      "synced_at": "2026-10-08T23:01:12.104Z"
                    }
                  ],
                  "workouts_count": 1,
                  "api_requests_last_30_days": [],
                  "api_requests_count": 0,
                  "subscriptions": [
                    {
                      "product_id": "ai.overarch.HealthAPI.pro.monthly",
                      "tier": "pro",
                      "status": "active",
                      "expires_at": "2026-10-30T18:05:00.000Z",
                      "auto_renew": true,
                      "environment": "Production",
                      "created_at": "2026-09-30T18:05:00.000Z",
                      "updated_at": "2026-09-30T18:05:00.000Z"
                    }
                  ],
                  "subscriptions_count": 1,
                  "exported_at": "2026-10-09T11:21:47.831Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your personal API key from the HealthAPI iPhone app (Connect > Other tools)."
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Daily request limit of the plan.",
        "schema": {
          "type": "integer"
        },
        "example": 100
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left today, after this one.",
        "schema": {
          "type": "integer"
        },
        "example": 93
      },
      "X-RateLimit-Reset": {
        "description": "When the daily window resets (next 00:00 UTC), Unix epoch seconds.",
        "schema": {
          "type": "integer"
        },
        "example": 1791590400
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        },
        "example": 45511
      }
    },
    "parameters": {
      "Tz": {
        "name": "tz",
        "in": "query",
        "required": false,
        "description": "IANA timezone name that defines the day boundaries, e.g. America/New_York. Defaults to the account's timezone. An unknown name returns 400.",
        "schema": {
          "type": "string",
          "maxLength": 64
        },
        "example": "America/New_York"
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Maximum number of rows to return (1 to 20000).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 20000,
          "default": 100
        },
        "example": 100
      },
      "Offset": {
        "name": "offset",
        "in": "query",
        "required": false,
        "description": "Rows to skip, for paging through results (newest first). Use with limit.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        },
        "example": 0
      },
      "StartDate": {
        "name": "start_date",
        "in": "query",
        "required": false,
        "description": "First local day to include, YYYY-MM-DD. Date only: a value with a time returns 400. Matched against each record's start time.",
        "schema": {
          "type": "string",
          "format": "date",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
        },
        "example": "2026-10-01"
      },
      "EndDate": {
        "name": "end_date",
        "in": "query",
        "required": false,
        "description": "Last local day to include (inclusive), YYYY-MM-DD. Date only: a value with a time returns 400.",
        "schema": {
          "type": "string",
          "format": "date",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
        },
        "example": "2026-10-08"
      }
    },
    "schemas": {
      "HealthMetricType": {
        "type": "string",
        "description": "A HealthKit metric name as the iPhone app syncs it (case-sensitive). Units: counts `count`, distances `m`, energy `kcal`, durations `min`, heart/breathing rates `count/min`, HRV `ms`, speeds `m/s`, mass `kg`, percentages `%` as fractions (0.97 = 97%).",
        "enum": [
          "stepCount",
          "distanceWalkingRunning",
          "activeEnergyBurned",
          "basalEnergyBurned",
          "flightsClimbed",
          "exerciseTime",
          "appleStandTime",
          "timeInDaylight",
          "distanceCycling",
          "distanceSwimming",
          "swimmingStrokeCount",
          "walkingSpeed",
          "walkingStepLength",
          "walkingAsymmetryPercentage",
          "walkingDoubleSupportPercentage",
          "stairAscentSpeed",
          "stairDescentSpeed",
          "sixMinuteWalkTestDistance",
          "runningSpeed",
          "runningPower",
          "runningStrideLength",
          "runningVerticalOscillation",
          "runningGroundContactTime",
          "heartRate",
          "heartRateVariability",
          "restingHeartRate",
          "walkingHeartRate",
          "heartRateRecoveryOneMinute",
          "vo2Max",
          "oxygenSaturation",
          "respiratoryRate",
          "bloodPressureSystolic",
          "bloodPressureDiastolic",
          "bloodGlucose",
          "bodyTemperature",
          "wristTemperature",
          "atrialFibrillationBurden",
          "environmentalAudioExposure",
          "headphoneAudioExposure",
          "dietaryWater",
          "dietaryEnergyConsumed",
          "bloodAlcoholContent",
          "insulinDelivery",
          "numberOfTimesFallen",
          "bodyMass",
          "bodyMassIndex",
          "bodyFatPercentage",
          "leanBodyMass",
          "height",
          "sleepAnalysis"
        ]
      },
      "TrendMetricType": {
        "type": "string",
        "description": "A metric getTrends can chart: every synced metric except sleepAnalysis (for sleep use getFullDailySummary or getWeeklySummary).",
        "enum": [
          "stepCount",
          "distanceWalkingRunning",
          "activeEnergyBurned",
          "basalEnergyBurned",
          "flightsClimbed",
          "exerciseTime",
          "appleStandTime",
          "timeInDaylight",
          "distanceCycling",
          "distanceSwimming",
          "swimmingStrokeCount",
          "walkingSpeed",
          "walkingStepLength",
          "walkingAsymmetryPercentage",
          "walkingDoubleSupportPercentage",
          "stairAscentSpeed",
          "stairDescentSpeed",
          "sixMinuteWalkTestDistance",
          "runningSpeed",
          "runningPower",
          "runningStrideLength",
          "runningVerticalOscillation",
          "runningGroundContactTime",
          "heartRate",
          "heartRateVariability",
          "restingHeartRate",
          "walkingHeartRate",
          "heartRateRecoveryOneMinute",
          "vo2Max",
          "oxygenSaturation",
          "respiratoryRate",
          "bloodPressureSystolic",
          "bloodPressureDiastolic",
          "bloodGlucose",
          "bodyTemperature",
          "wristTemperature",
          "atrialFibrillationBurden",
          "environmentalAudioExposure",
          "headphoneAudioExposure",
          "dietaryWater",
          "dietaryEnergyConsumed",
          "bloodAlcoholContent",
          "insulinDelivery",
          "numberOfTimesFallen",
          "bodyMass",
          "bodyMassIndex",
          "bodyFatPercentage",
          "leanBodyMass",
          "height"
        ]
      },
      "WorkoutActivityType": {
        "type": "string",
        "description": "Workout type as the iPhone app records it. Types without their own name are recorded as `other`.",
        "enum": [
          "running",
          "cycling",
          "walking",
          "swimming",
          "hiking",
          "yoga",
          "strength_training",
          "functional_strength_training",
          "hiit",
          "elliptical",
          "rowing",
          "stair_climbing",
          "core_training",
          "cooldown",
          "other"
        ]
      },
      "PlanTier": {
        "type": "string",
        "description": "`free` means not subscribed (free preview, or lapsed). `enterprise` is only used for custom accounts.",
        "enum": [
          "free",
          "pro",
          "developer",
          "enterprise"
        ]
      },
      "Error": {
        "type": "object",
        "description": "Error body. `detail` is a human-readable message.",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string"
          }
        }
      },
      "ValidationErrorItem": {
        "type": "object",
        "required": [
          "type",
          "loc",
          "msg"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "Machine-readable reason.",
            "examples": [
              "missing",
              "int_parsing",
              "greater_than_equal",
              "less_than_equal",
              "value_error",
              "json_invalid"
            ]
          },
          "loc": {
            "type": "array",
            "description": "Where the problem is: [\"query\", <param>] or [\"body\", <field>].",
            "items": {
              "type": [
                "string",
                "integer"
              ]
            }
          },
          "msg": {
            "type": "string"
          },
          "input": {
            "description": "The rejected value, when there was one."
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "description": "422 body: a list of problems, FastAPI style.",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationErrorItem"
            }
          }
        }
      },
      "SubscriptionRequiredError": {
        "type": "object",
        "required": [
          "detail",
          "code"
        ],
        "properties": {
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "const": "subscription_required"
          },
          "access_status": {
            "type": "string",
            "const": "expired"
          },
          "plan": {
            "$ref": "#/components/schemas/PlanTier"
          }
        }
      },
      "RateLimitError": {
        "type": "object",
        "description": "429 body. The daily-limit error adds limit, remaining, reset and plan; the per-IP and repeated-invalid-key errors carry only detail.",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string"
          },
          "limit": {
            "type": "integer",
            "description": "Daily request limit of the plan."
          },
          "remaining": {
            "type": "integer",
            "const": 0
          },
          "reset": {
            "type": "integer",
            "description": "When the daily window resets, Unix epoch seconds (00:00 UTC)."
          },
          "plan": {
            "$ref": "#/components/schemas/PlanTier"
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "plan_tier",
          "plan_label",
          "window",
          "limit",
          "used",
          "remaining",
          "reset_at",
          "apple_linked",
          "access_status",
          "trial_ends_at",
          "deletion_scheduled_at",
          "locked"
        ],
        "properties": {
          "plan_tier": {
            "$ref": "#/components/schemas/PlanTier"
          },
          "plan_label": {
            "type": "string",
            "description": "Display name: Trial, Pro, Developer or Enterprise.",
            "examples": [
              "Pro"
            ]
          },
          "window": {
            "type": "string",
            "const": "1d",
            "description": "The quota window: one UTC day."
          },
          "limit": {
            "type": "integer",
            "description": "Requests allowed per day."
          },
          "used": {
            "type": "integer",
            "description": "Requests counted so far today."
          },
          "remaining": {
            "type": "integer",
            "minimum": 0
          },
          "reset_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the count resets (next 00:00 UTC)."
          },
          "apple_linked": {
            "type": "boolean",
            "description": "Whether a Sign in with Apple identity is linked for account recovery."
          },
          "access_status": {
            "type": "string",
            "enum": [
              "subscribed",
              "trialing",
              "expired"
            ],
            "description": "subscribed: paid plan. trialing: free preview, ends at trial_ends_at. expired: no access to health data (reads return 402)."
          },
          "trial_ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "End of the free preview; set only while trialing."
          },
          "deletion_scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Set only when expired: when the lapsed account's data is scheduled for deletion."
          },
          "locked": {
            "type": "boolean",
            "description": "True when health-data reads are refused with 402."
          }
        }
      },
      "UtcRange": {
        "type": "object",
        "description": "Half-open UTC interval [start, end) the query covered.",
        "required": [
          "start",
          "end"
        ],
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LocalRange": {
        "type": "object",
        "description": "The same interval in the local timezone.",
        "required": [
          "start",
          "end"
        ],
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DailySummaryWorkout": {
        "type": "object",
        "required": [
          "type",
          "duration_minutes",
          "calories"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "Workout activity type, e.g. running (see WorkoutActivityType)."
          },
          "duration_minutes": {
            "type": "number",
            "description": "Duration in minutes, one decimal."
          },
          "calories": {
            "type": [
              "number",
              "null"
            ],
            "description": "Active energy in kcal."
          },
          "start_date_utc": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "end_date_utc": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "start_date_local": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "end_date_local": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "DailySummary": {
        "type": "object",
        "required": [
          "date",
          "timezone",
          "steps",
          "avg_heart_rate",
          "workouts"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "description": "The local day summarized."
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone used for the day boundaries."
          },
          "range_local": {
            "$ref": "#/components/schemas/LocalRange"
          },
          "range_utc": {
            "$ref": "#/components/schemas/UtcRange"
          },
          "steps": {
            "type": "integer",
            "description": "Step total, deduplicated across iPhone and Apple Watch (matches Apple Health)."
          },
          "avg_heart_rate": {
            "type": [
              "number",
              "null"
            ],
            "description": "Average heart rate in bpm, one decimal; null when there were no readings."
          },
          "workouts": {
            "type": "array",
            "description": "Workouts that started that day, oldest first.",
            "items": {
              "$ref": "#/components/schemas/DailySummaryWorkout"
            }
          }
        }
      },
      "TrendPoint": {
        "type": "object",
        "required": [
          "date",
          "avg",
          "min",
          "max",
          "total",
          "samples"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "description": "Local day."
          },
          "avg": {
            "type": [
              "number",
              "null"
            ],
            "description": "Daily average (aggregation avg); null for summed metrics."
          },
          "min": {
            "type": [
              "number",
              "null"
            ],
            "description": "Lowest reading of the day (aggregation avg)."
          },
          "max": {
            "type": [
              "number",
              "null"
            ],
            "description": "Highest reading of the day (aggregation avg)."
          },
          "total": {
            "type": [
              "number",
              "null"
            ],
            "description": "Daily total (aggregation sum); null for averaged metrics."
          },
          "samples": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Number of readings averaged; null when the day came from a pre-computed rollup or is a sum."
          }
        }
      },
      "Trend": {
        "type": "object",
        "required": [
          "type",
          "timezone",
          "period_days",
          "aggregation",
          "start_date",
          "end_date",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "The metric requested."
          },
          "timezone": {
            "type": "string"
          },
          "period_days": {
            "type": "integer"
          },
          "aggregation": {
            "type": "string",
            "enum": [
              "sum",
              "avg"
            ],
            "description": "sum: read `total` (steps, energy, distance, ...). avg: read `avg`/`min`/`max` (heart rate, HRV, weight, ...)."
          },
          "range_local": {
            "$ref": "#/components/schemas/LocalRange"
          },
          "range_utc": {
            "$ref": "#/components/schemas/UtcRange"
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "description": "First local day covered."
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "description": "Last local day covered (today)."
          },
          "data": {
            "type": "array",
            "description": "One entry per day that has data, oldest first. Days with no data are left out.",
            "items": {
              "$ref": "#/components/schemas/TrendPoint"
            }
          }
        }
      },
      "AggregateTotals": {
        "type": "object",
        "description": "Summed activity, deduplicated across iPhone and Apple Watch.",
        "required": [
          "steps",
          "distance_m",
          "active_energy_kcal",
          "basal_energy_kcal",
          "flights_climbed",
          "exercise_time_min"
        ],
        "properties": {
          "steps": {
            "type": "integer"
          },
          "distance_m": {
            "type": "number",
            "description": "Walking + running distance, meters."
          },
          "active_energy_kcal": {
            "type": "number"
          },
          "basal_energy_kcal": {
            "type": "number"
          },
          "flights_climbed": {
            "type": "integer"
          },
          "exercise_time_min": {
            "type": "number",
            "description": "Apple exercise minutes."
          }
        }
      },
      "AggregateAverages": {
        "type": "object",
        "description": "Means of the individual readings in the period; null when there were none. Not rounded.",
        "required": [
          "heart_rate_bpm",
          "resting_heart_rate_bpm",
          "hrv_ms",
          "spo2_pct",
          "respiratory_rate_bpm"
        ],
        "properties": {
          "heart_rate_bpm": {
            "type": [
              "number",
              "null"
            ]
          },
          "resting_heart_rate_bpm": {
            "type": [
              "number",
              "null"
            ]
          },
          "hrv_ms": {
            "type": [
              "number",
              "null"
            ],
            "description": "Heart rate variability (SDNN), ms."
          },
          "spo2_pct": {
            "type": [
              "number",
              "null"
            ],
            "description": "Blood oxygen as a percent, 0-100."
          },
          "respiratory_rate_bpm": {
            "type": [
              "number",
              "null"
            ],
            "description": "Breaths per minute."
          }
        }
      },
      "SleepTotals": {
        "type": "object",
        "description": "Sleep for the nights that ended in the period. A night belongs to the morning you woke up and holds the sleep that ended from 6 pm the day before up to 6 pm that day. Asleep stages only; in bed and awake are excluded.",
        "required": [
          "total_hours",
          "session_count"
        ],
        "properties": {
          "total_hours": {
            "type": "number",
            "description": "Time asleep, hours (2 decimals)."
          },
          "session_count": {
            "type": "integer",
            "description": "Asleep segments recorded, not nights: Apple Watch writes several stage segments per night."
          }
        }
      },
      "WorkoutTotals": {
        "type": "object",
        "description": "Workouts that started in the period.",
        "required": [
          "count",
          "total_duration_seconds",
          "total_calories_kcal"
        ],
        "properties": {
          "count": {
            "type": "integer"
          },
          "total_duration_seconds": {
            "type": "number"
          },
          "total_calories_kcal": {
            "type": "number"
          }
        }
      },
      "DailyBreakdownEntry": {
        "type": "object",
        "description": "One local day of a weekly summary.",
        "required": [
          "date",
          "totals",
          "averages",
          "sleep",
          "workouts",
          "data_coverage_pct",
          "sleep_hours",
          "sleep_minutes",
          "sleep_session_count",
          "workout_count",
          "workout_duration_seconds",
          "workout_duration_minutes",
          "workout_calories_kcal",
          "workout_calories"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date"
          },
          "totals": {
            "$ref": "#/components/schemas/AggregateTotals"
          },
          "averages": {
            "$ref": "#/components/schemas/AggregateAverages"
          },
          "sleep": {
            "$ref": "#/components/schemas/SleepTotals"
          },
          "workouts": {
            "$ref": "#/components/schemas/WorkoutTotals"
          },
          "data_coverage_pct": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Share of 7 expected sources with data (steps, active energy, basal energy, heart rate, distance, exercise time, sleep). 85 means one is missing."
          },
          "sleep_hours": {
            "type": "number",
            "description": "Alias of sleep.total_hours."
          },
          "sleep_minutes": {
            "type": "number",
            "description": "sleep.total_hours in minutes."
          },
          "sleep_session_count": {
            "type": "integer",
            "description": "Alias of sleep.session_count."
          },
          "workout_count": {
            "type": "integer",
            "description": "Alias of workouts.count."
          },
          "workout_duration_seconds": {
            "type": "number",
            "description": "Alias of workouts.total_duration_seconds."
          },
          "workout_duration_minutes": {
            "type": "number",
            "description": "workouts.total_duration_seconds in minutes."
          },
          "workout_calories_kcal": {
            "type": "number",
            "description": "Alias of workouts.total_calories_kcal."
          },
          "workout_calories": {
            "type": "number",
            "description": "Alias of workouts.total_calories_kcal (kcal)."
          }
        }
      },
      "FullDailySummary": {
        "type": "object",
        "required": [
          "date",
          "timezone",
          "range_utc",
          "totals",
          "averages",
          "sleep",
          "workouts",
          "data_coverage_pct",
          "sleep_hours",
          "sleep_minutes",
          "sleep_session_count",
          "workout_count",
          "workout_duration_seconds",
          "workout_duration_minutes",
          "workout_calories_kcal",
          "workout_calories"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "description": "The local day summarized."
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone used for the day boundaries."
          },
          "range_utc": {
            "$ref": "#/components/schemas/UtcRange"
          },
          "totals": {
            "$ref": "#/components/schemas/AggregateTotals"
          },
          "averages": {
            "$ref": "#/components/schemas/AggregateAverages"
          },
          "sleep": {
            "$ref": "#/components/schemas/SleepTotals"
          },
          "workouts": {
            "$ref": "#/components/schemas/WorkoutTotals"
          },
          "data_coverage_pct": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Share of 7 expected sources with data (steps, active energy, basal energy, heart rate, distance, exercise time, sleep). 85 means one is missing."
          },
          "sleep_hours": {
            "type": "number",
            "description": "Alias of sleep.total_hours."
          },
          "sleep_minutes": {
            "type": "number",
            "description": "sleep.total_hours in minutes."
          },
          "sleep_session_count": {
            "type": "integer",
            "description": "Alias of sleep.session_count."
          },
          "workout_count": {
            "type": "integer",
            "description": "Alias of workouts.count."
          },
          "workout_duration_seconds": {
            "type": "number",
            "description": "Alias of workouts.total_duration_seconds."
          },
          "workout_duration_minutes": {
            "type": "number",
            "description": "workouts.total_duration_seconds in minutes."
          },
          "workout_calories_kcal": {
            "type": "number",
            "description": "Alias of workouts.total_calories_kcal."
          },
          "workout_calories": {
            "type": "number",
            "description": "Alias of workouts.total_calories_kcal (kcal)."
          }
        }
      },
      "WeeklySummary": {
        "type": "object",
        "required": [
          "week",
          "timezone",
          "week_start_date",
          "range_utc",
          "totals",
          "averages",
          "sleep",
          "workouts",
          "data_coverage_pct",
          "sleep_hours",
          "sleep_minutes",
          "sleep_session_count",
          "workout_count",
          "workout_duration_seconds",
          "workout_duration_minutes",
          "workout_calories_kcal",
          "workout_calories",
          "daily_breakdown"
        ],
        "properties": {
          "week": {
            "type": "string",
            "description": "ISO week, YYYY-Www."
          },
          "timezone": {
            "type": "string"
          },
          "week_start_date": {
            "type": "string",
            "format": "date",
            "description": "The Monday the week starts on."
          },
          "range_utc": {
            "$ref": "#/components/schemas/UtcRange"
          },
          "totals": {
            "$ref": "#/components/schemas/AggregateTotals"
          },
          "averages": {
            "$ref": "#/components/schemas/AggregateAverages"
          },
          "sleep": {
            "$ref": "#/components/schemas/SleepTotals"
          },
          "workouts": {
            "$ref": "#/components/schemas/WorkoutTotals"
          },
          "data_coverage_pct": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Share of 7 expected sources with data (steps, active energy, basal energy, heart rate, distance, exercise time, sleep). 85 means one is missing."
          },
          "sleep_hours": {
            "type": "number",
            "description": "Alias of sleep.total_hours."
          },
          "sleep_minutes": {
            "type": "number",
            "description": "sleep.total_hours in minutes."
          },
          "sleep_session_count": {
            "type": "integer",
            "description": "Alias of sleep.session_count."
          },
          "workout_count": {
            "type": "integer",
            "description": "Alias of workouts.count."
          },
          "workout_duration_seconds": {
            "type": "number",
            "description": "Alias of workouts.total_duration_seconds."
          },
          "workout_duration_minutes": {
            "type": "number",
            "description": "workouts.total_duration_seconds in minutes."
          },
          "workout_calories_kcal": {
            "type": "number",
            "description": "Alias of workouts.total_calories_kcal."
          },
          "workout_calories": {
            "type": "number",
            "description": "Alias of workouts.total_calories_kcal (kcal)."
          },
          "daily_breakdown": {
            "type": "array",
            "description": "Monday to Sunday, always 7 entries.",
            "items": {
              "$ref": "#/components/schemas/DailyBreakdownEntry"
            }
          }
        }
      },
      "Sample": {
        "type": "object",
        "required": [
          "id",
          "type",
          "kind",
          "value",
          "unit",
          "start_date",
          "end_date",
          "timezone"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "description": "Metric name (see HealthMetricType)."
          },
          "kind": {
            "$ref": "#/components/schemas/SampleKind"
          },
          "value": {
            "type": "number",
            "description": "In `unit`. Percentages are fractions; sleepAnalysis is the segment's length in minutes."
          },
          "unit": {
            "type": "string",
            "examples": [
              "count/min",
              "count",
              "%",
              "kg",
              "min"
            ]
          },
          "start_date": {
            "type": "string",
            "format": "date-time",
            "description": "UTC."
          },
          "end_date": {
            "type": "string",
            "format": "date-time",
            "description": "UTC."
          },
          "synced_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the app uploaded it, UTC."
          },
          "timezone": {
            "type": "string",
            "description": "The account timezone used for the *_local fields."
          },
          "start_date_utc": {
            "type": "string",
            "format": "date-time"
          },
          "end_date_utc": {
            "type": "string",
            "format": "date-time"
          },
          "synced_at_utc": {
            "type": "string",
            "format": "date-time"
          },
          "start_date_local": {
            "type": "string",
            "format": "date-time"
          },
          "end_date_local": {
            "type": "string",
            "format": "date-time"
          },
          "synced_at_local": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Workout": {
        "type": "object",
        "required": [
          "id",
          "activity_type",
          "start_date",
          "end_date",
          "duration_seconds"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "activity_type": {
            "type": "string",
            "description": "See WorkoutActivityType."
          },
          "start_date": {
            "type": "string",
            "format": "date-time",
            "description": "UTC."
          },
          "end_date": {
            "type": "string",
            "format": "date-time",
            "description": "UTC."
          },
          "duration_seconds": {
            "type": "number"
          },
          "total_distance_meters": {
            "type": [
              "number",
              "null"
            ]
          },
          "total_energy_kcal": {
            "type": [
              "number",
              "null"
            ],
            "description": "Active energy burned."
          },
          "source_app": {
            "type": [
              "string",
              "null"
            ],
            "description": "App or device that recorded it."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it was synced, UTC."
          }
        }
      },
      "LatestReading": {
        "type": "object",
        "required": [
          "value",
          "unit",
          "date"
        ],
        "properties": {
          "value": {
            "type": "number"
          },
          "unit": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "Start time of the reading, UTC."
          }
        }
      },
      "LatestReadings": {
        "type": "object",
        "description": "Keyed by metric name (see HealthMetricType). Only metrics that have individual readings appear.",
        "additionalProperties": {
          "$ref": "#/components/schemas/LatestReading"
        }
      },
      "DataExport": {
        "type": "object",
        "required": [
          "user",
          "health_samples",
          "health_samples_count",
          "workouts",
          "workouts_count",
          "api_requests_last_30_days",
          "api_requests_count",
          "subscriptions",
          "subscriptions_count",
          "exported_at"
        ],
        "properties": {
          "user": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "email": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "device_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "device_name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "platform": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "provider": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "plan_tier": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "created_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "last_active_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          },
          "health_samples": {
            "type": "array",
            "description": "Every stored sample, oldest id first, including the app's rollup rows (source_app healthapi:agg / healthapi:dstat).",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "type": {
                  "type": "string"
                },
                "value": {
                  "type": "number"
                },
                "unit": {
                  "type": "string"
                },
                "start_date": {
                  "type": "string",
                  "format": "date-time"
                },
                "end_date": {
                  "type": "string",
                  "format": "date-time"
                },
                "source_app": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "device": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "synced_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "health_samples_count": {
            "type": "integer"
          },
          "workouts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "activity_type": {
                  "type": "string"
                },
                "start_date": {
                  "type": "string",
                  "format": "date-time"
                },
                "end_date": {
                  "type": "string",
                  "format": "date-time"
                },
                "duration_seconds": {
                  "type": "number"
                },
                "total_distance_meters": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "total_energy_kcal": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "source_app": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "synced_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "workouts_count": {
            "type": "integer"
          },
          "api_requests_last_30_days": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "endpoint": {
                  "type": "string"
                },
                "method": {
                  "type": "string"
                },
                "requested_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "response_status": {
                  "type": [
                    "integer",
                    "null"
                  ]
                }
              }
            }
          },
          "api_requests_count": {
            "type": "integer"
          },
          "subscriptions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "product_id": {
                  "type": "string"
                },
                "tier": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "examples": [
                    "active",
                    "grace",
                    "expired",
                    "revoked"
                  ]
                },
                "expires_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "auto_renew": {
                  "type": [
                    "boolean",
                    "null"
                  ]
                },
                "environment": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "created_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "updated_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "subscriptions_count": {
            "type": "integer"
          },
          "exported_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SampleKind": {
        "type": "string",
        "enum": [
          "reading",
          "total",
          "daily_average"
        ],
        "description": "reading: as Apple Health recorded it. total: the app's deduplicated sum for start..end (iPhone and Watch combined). daily_average: the app's average for one local day."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "A date or week value is malformed, a date is too far in the future, or tz is not a known timezone.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "badDate": {
                "value": {
                  "detail": "Invalid date format. Use YYYY-MM-DD"
                }
              },
              "badWeek": {
                "value": {
                  "detail": "Invalid week format. Use YYYY-Www (e.g. 2026-W19)"
                }
              },
              "future": {
                "value": {
                  "detail": "Date is too far in the future"
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string"
            },
            "example": "Bearer"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "invalid": {
                "value": {
                  "detail": "Invalid API key"
                }
              },
              "missing": {
                "value": {
                  "detail": "Missing Authorization header"
                }
              },
              "format": {
                "value": {
                  "detail": "Invalid Authorization header format. Expected: Bearer YOUR_API_KEY"
                }
              }
            }
          }
        }
      },
      "SubscriptionRequired": {
        "description": "No active subscription or free preview, so health data can't be read. Nothing is deleted; subscribing in the iPhone app restores access. Tell the user rather than retrying.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SubscriptionRequiredError"
            },
            "example": {
              "detail": "An active subscription is required to use HealthAPI. Subscribe in the app to continue — your data is still here.",
              "code": "subscription_required",
              "access_status": "expired",
              "plan": "free"
            }
          }
        }
      },
      "ValidationError": {
        "description": "A required parameter is missing, or a parameter or body field is out of range or the wrong type.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "examples": {
              "missing": {
                "value": {
                  "detail": [
                    {
                      "type": "missing",
                      "loc": [
                        "query",
                        "type"
                      ],
                      "msg": "Field required"
                    }
                  ]
                }
              },
              "outOfRange": {
                "value": {
                  "detail": [
                    {
                      "type": "less_than_equal",
                      "loc": [
                        "query",
                        "days"
                      ],
                      "msg": "Input should be less than or equal to 365",
                      "input": 400
                    }
                  ]
                }
              },
              "notAnInteger": {
                "value": {
                  "detail": [
                    {
                      "type": "int_parsing",
                      "loc": [
                        "query",
                        "limit"
                      ],
                      "msg": "Input should be a valid integer",
                      "input": "1.5"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Daily request limit reached (wait for Retry-After or the reset at 00:00 UTC), or too many requests per minute from this IP address (wait 60 seconds).",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            },
            "examples": {
              "dailyLimit": {
                "summary": "Daily plan limit reached",
                "value": {
                  "detail": "Rate limit exceeded. Your Pro plan allows 100 requests/day. Upgrade for a higher daily limit.",
                  "limit": 100,
                  "remaining": 0,
                  "reset": 1791590400,
                  "plan": "pro"
                }
              },
              "perMinute": {
                "summary": "Per-IP limit (about 100 a minute per endpoint)",
                "value": {
                  "detail": "ThrottlerException: Too Many Requests"
                }
              },
              "invalidKeys": {
                "summary": "Too many wrong keys from this address",
                "value": {
                  "detail": "Too many invalid API keys from this address. Wait a minute and try again."
                }
              }
            }
          }
        }
      }
    }
  }
}
