API reference
Health
Your own health data, sync, sharing, Sexual Activity, export and delete; what others share with you; Mirage server leaderboards.
See Health. /v1/me/health is always the caller's own data and /v1/health what others share with them. Health belongs to people, never an organization: personal keys act for their maker only when the person allowed it in Health → Privacy, and privacy settings change only in the apps.
GET /v1/me/health/profile
Your Health settings: the consent you agreed to (and each sensitive category's), time zone, pinned metrics, Caity's and API access, Sexual Activity's sharing, and whether your account is known to be 18 or over (adult, ageUnknown; never the date of birth itself).
Auth: user access token or platform agent key
Response 200
{
profile: {
userId: string
consent: null | {
version: number
at: number
}
sensitiveConsent: {
body?: number
heart?: number
other?: number
activity?: number
cycle?: number
hearing?: number
medications?: number
mental?: number
mindfulness?: number
mobility?: number
nutrition?: number
respiratory?: number
sleep?: number
symptoms?: number
vitals?: number
sexual?: number
}
timeZone: string
pinned: string[]
assistant: {
enabled: boolean
categories: ("body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual")[]
}
sexualShare: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
counts: boolean
protectionRate: boolean
partnerCount: boolean
confirmedAt?: number
}
apiAccess: boolean
adult: boolean
ageUnknown: boolean
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
PATCH /v1/me/health/profile
Changes your time zone (days are your local days) or Summary's pinned metrics (pinned, up to 12, in order).
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
timeZone | string | No | up to 64 characters |
pinned | string[] | No | up to 12 items; each up to 48 characters |
Also checked: Unknown fields are rejected.
Response 200
{
profile: {
userId: string
consent: null | {
version: number
at: number
}
sensitiveConsent: {
body?: number
heart?: number
other?: number
activity?: number
cycle?: number
hearing?: number
medications?: number
mental?: number
mindfulness?: number
mobility?: number
nutrition?: number
respiratory?: number
sleep?: number
symptoms?: number
vitals?: number
sexual?: number
}
timeZone: string
pinned: string[]
assistant: {
enabled: boolean
categories: ("body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual")[]
}
sexualShare: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
counts: boolean
protectionRate: boolean
partnerCount: boolean
confirmedAt?: number
}
apiAccess: boolean
adult: boolean
ageUnknown: boolean
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
POST /v1/me/health/consent
Agrees to Health's terms (version, the current one). Nothing is stored before. In the app only.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
version | integer | Yes |
Also checked: Unknown fields are rejected.
Response 200
{
profile: {
userId: string
consent: null | {
version: number
at: number
}
sensitiveConsent: {
body?: number
heart?: number
other?: number
activity?: number
cycle?: number
hearing?: number
medications?: number
mental?: number
mindfulness?: number
mobility?: number
nutrition?: number
respiratory?: number
sleep?: number
symptoms?: number
vitals?: number
sexual?: number
}
timeZone: string
pinned: string[]
assistant: {
enabled: boolean
categories: ("body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual")[]
}
sexualShare: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
counts: boolean
protectionRate: boolean
partnerCount: boolean
confirmedAt?: number
}
apiAccess: boolean
adult: boolean
ageUnknown: boolean
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
DELETE /v1/me/health/consent
Withdraws consent: nothing new is stored, every share and leaderboard opt-in ends, Caity's and API access turn off. Your data stays until you delete it. In the app only.
Auth: user access token or platform agent key
Response 200
{
profile: {
userId: string
consent: null | {
version: number
at: number
}
sensitiveConsent: {
body?: number
heart?: number
other?: number
activity?: number
cycle?: number
hearing?: number
medications?: number
mental?: number
mindfulness?: number
mobility?: number
nutrition?: number
respiratory?: number
sleep?: number
symptoms?: number
vitals?: number
sexual?: number
}
timeZone: string
pinned: string[]
assistant: {
enabled: boolean
categories: ("body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual")[]
}
sexualShare: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
counts: boolean
protectionRate: boolean
partnerCount: boolean
confirmedAt?: number
}
apiAccess: boolean
adult: boolean
ageUnknown: boolean
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
PUT /v1/me/health/categories/:category/consent
Turns a sensitive category on or off (on): Cycle Tracking, Medications, Mental Wellbeing, Symptoms, and Sexual Activity (18 or over). Off ends its sharing. In the app only.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:category | A Health category: activity, body, cycle, hearing, heart, medications, mental, mindfulness, mobility, nutrition, respiratory, sleep, symptoms, vitals, sexual or other. |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
on | boolean | Yes |
Also checked: Unknown fields are rejected.
Response 200
{
profile: {
userId: string
consent: null | {
version: number
at: number
}
sensitiveConsent: {
body?: number
heart?: number
other?: number
activity?: number
cycle?: number
hearing?: number
medications?: number
mental?: number
mindfulness?: number
mobility?: number
nutrition?: number
respiratory?: number
sleep?: number
symptoms?: number
vitals?: number
sexual?: number
}
timeZone: string
pinned: string[]
assistant: {
enabled: boolean
categories: ("body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual")[]
}
sexualShare: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
counts: boolean
protectionRate: boolean
partnerCount: boolean
confirmedAt?: number
}
apiAccess: boolean
adult: boolean
ageUnknown: boolean
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
PUT /v1/me/health/assistant
Caity's access (enabled, categories): summaries only; standard categories unless you name some, sensitive ones only when named, Sexual Activity never. In the app only.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
enabled | boolean | Yes | ||
categories | ("activity" | "body" | "cycle" | "hearing" | "heart" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual" | "other")[] | No | [] | up to 16 items |
Also checked: Unknown fields are rejected.
Response 200
{
profile: {
userId: string
consent: null | {
version: number
at: number
}
sensitiveConsent: {
body?: number
heart?: number
other?: number
activity?: number
cycle?: number
hearing?: number
medications?: number
mental?: number
mindfulness?: number
mobility?: number
nutrition?: number
respiratory?: number
sleep?: number
symptoms?: number
vitals?: number
sexual?: number
}
timeZone: string
pinned: string[]
assistant: {
enabled: boolean
categories: ("body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual")[]
}
sexualShare: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
counts: boolean
protectionRate: boolean
partnerCount: boolean
confirmedAt?: number
}
apiAccess: boolean
adult: boolean
ageUnknown: boolean
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
PUT /v1/me/health/api-access
Lets personal API keys and connected apps with health:read or health:write act for you (on). Off by default. In the app only.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
on | boolean | Yes |
Also checked: Unknown fields are rejected.
Response 200
{
profile: {
userId: string
consent: null | {
version: number
at: number
}
sensitiveConsent: {
body?: number
heart?: number
other?: number
activity?: number
cycle?: number
hearing?: number
medications?: number
mental?: number
mindfulness?: number
mobility?: number
nutrition?: number
respiratory?: number
sleep?: number
symptoms?: number
vitals?: number
sexual?: number
}
timeZone: string
pinned: string[]
assistant: {
enabled: boolean
categories: ("body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual")[]
}
sexualShare: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
counts: boolean
protectionRate: boolean
partnerCount: boolean
confirmedAt?: number
}
apiAccess: boolean
adult: boolean
ageUnknown: boolean
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
GET /v1/me/health/metrics
The metric registry as you can use it (Sexual Activity only when you're known to be 18 or over), the categories, and your state for each metric you have data in (count, first and last sample, months, unit, sharing).
Auth: user access token or platform agent key
Response 200
{
metrics: {
metricId: string
label: string
category: "body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual"
kind: "duration" | "event" | "category" | "quantity"
unit: string
units: {
id: string
label: string
factor: number
offset?: number
}[]
aggregation: "duration" | "count" | "sum" | "average" | "latest"
decimals: number
sensitivity: "standard" | "sensitive" | "sexual"
values?: {
value: number
label: string
}[]
healthKit?: {
identifier: string
kind: "category" | "quantity" | "workout"
unit?: string
readOnly?: boolean
factor?: number
categoryValues?: number[]
}
healthConnect?: string
leaderboard?: "count" | "sum" | "average"
min?: number
max?: number
pinned?: boolean
description?: string
enabled: boolean
sortOrder: number
}[]
categories: {
id: "body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual"
label: string
glyph: "health-body" | "health-heart" | "health-other" | "health-activity" | "health-cycle" | "health-hearing" | "health-medications" | "health-mental" | "health-mindfulness" | "health-mobility" | "health-nutrition" | "health-respiratory" | "health-sleep" | "health-symptoms" | "health-vitals" | "health-sexual"
sensitivity: "standard" | "sensitive" | "sexual"
adultOnly?: true
}[]
states: {
metricId: string
unit?: string
share?: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
detail: "samples" | "aggregates"
}
count: number
firstAt?: number
lastAt?: number
months: string[]
updatedAt: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
GET /v1/me/health/summary
Summary: your pinned metrics (today, the last seven days, the average and the trend against the week before), highlights, and the categories you have data in.
Auth: user access token or platform agent key
Response 200
{
pinned: {
metricId: string
latest: null | {
value: number
at: number
}
today: null | number
week: (null | number)[]
average: null | number
trend: null | "flat" | "up" | "down"
}[]
highlights: {
metricId: string
text: string
kind: "trend" | "best" | "streak"
}[]
today: string
categories: {
id: "body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual"
metrics: number
lastAt: null | number
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
GET /v1/me/health/metrics/:metricId/chart
A metric's chart: range D (hours of end), W and M (days), 6M (weeks) or Y (months), ending on end (a day; today by default), with the average, range, total and best day.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:metricId | A Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them. |
| Query parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
range | "D" | "W" | "M" | "6M" | "Y" | No | "W" | |
end | string | No | matches ^\d{4}-\d{2}-\d{2}$ |
Response 200
{
metricId: string
range: "M" | "D" | "W" | "6M" | "Y"
buckets: {
key: string
from: string
to: string
value: null | number
min: null | number
max: null | number
days: number
}[]
stats: {
days: number
average: null | number
total: null | number
min: null | number
max: null | number
best: null | {
day: string
value: number
}
}
today: string
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
GET /v1/me/health/metrics/:metricId/samples
A metric's samples, newest first (limit up to 200; from and to in milliseconds; cursor from the last page). Sexual Activity's entries include their partner (name, userId), only ever to you.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:metricId | A Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them. |
| Query parameter | Type | Required | Notes |
|---|---|---|---|
cursor | string | No | up to 2,000 characters |
from | integer | No | coerced from a string |
to | integer | No | coerced from a string |
limit | integer | No | 1–200; coerced from a string |
Response 200
{
samples: {
sampleId: string
metricId: string
start: number
end: number
value: number
stage?: number
protection?: boolean | null
partner?: {
name?: string
userId?: string
}
note?: string
meta?: {
[key: string]: boolean | string | number
}
sourceId: string
sourceName: string
external: boolean
version: number
createdAt: number
updatedAt: number
}[]
cursor: null | string
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
PUT /v1/me/health/metrics/:metricId/unit
The unit you see a metric in (unit, one of its units).
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:metricId | A Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them. |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
unit | string | Yes | up to 20 characters |
Also checked: Unknown fields are rejected.
Response 200
{
metricId: string
unit: string
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
PATCH /v1/me/health/metrics/:metricId/samples/:sampleId
Changes an entry made in the apps (value in unit, times, note, protection, partner). Samples from HealthKit change there and sync here.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:metricId | A Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them. |
:sampleId | A Health sample's id (hsm_…). |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
start | integer | No | ≥ 0 |
end | integer | No | ≥ 0 |
value | number | No | |
unit | string | No | up to 20 characters |
stage | integer | No | |
protection | boolean | No | can be null |
partner | object | No | can be null |
partner.name | string | No | 1–100 characters; trimmed |
partner.userId | string | No | 4–64 characters |
note | string | No | up to 1,000 characters |
meta | object | No | keys up to 40 characters; values: string (up to 200 characters), number or boolean |
Also checked: Unknown fields are rejected.
Response 200
{
sample: {
sampleId: string
metricId: string
start: number
end: number
value: number
stage?: number
protection?: boolean | null
partner?: {
name?: string
userId?: string
}
note?: string
meta?: {
[key: string]: boolean | string | number
}
sourceId: string
sourceName: string
external: boolean
version: number
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
DELETE /v1/me/health/metrics/:metricId/samples/:sampleId
Deletes a sample; the day's values, totals and leaderboard scores follow.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:metricId | A Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them. |
:sampleId | A Health sample's id (hsm_…). |
Response 204 with no body.
Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
404 | Sample not found. |
POST /v1/me/health/samples
Adds samples (entries in the apps, or a batch from elsewhere).
Auth: user access token or platform agent key
Request body (up to 2 MB)
| Field | Type | Required | Notes |
|---|---|---|---|
samples | object[] | Yes | 1–500 items |
samples[].metricId | string | Yes | 1–48 characters |
samples[].start | integer | Yes | ≥ 0 |
samples[].end | integer | No | ≥ 0 |
samples[].value | number | No | |
samples[].unit | string | No | up to 20 characters |
samples[].stage | integer | No | |
samples[].protection | boolean | No | can be null |
samples[].partner | object | No | can be null |
samples[].partner.name | string | No | 1–100 characters; trimmed |
samples[].partner.userId | string | No | 4–64 characters |
samples[].note | string | No | up to 1,000 characters |
samples[].meta | object | No | keys up to 40 characters; values: string (up to 200 characters), number or boolean |
samples[].source | object | No | |
samples[].source.kind | "watch" | "phone" | "app" | "manual" | Yes | |
samples[].source.name | string | Yes | 1–80 characters; trimmed |
samples[].source.bundleId | string | No | up to 200 characters |
samples[].source.device | string | No | up to 120 characters |
samples[].externalId | string | No | 1–100 characters |
samples[].version | integer | No | ≥ 0 |
Also checked: Unknown fields are rejected.
Response 201
{
written: {
sampleId: string
metricId: string
start: number
end: number
value: number
stage?: number
protection?: boolean | null
partner?: {
name?: string
userId?: string
}
note?: string
meta?: {
[key: string]: boolean | string | number
}
sourceId: string
sourceName: string
external: boolean
version: number
createdAt: number
updatedAt: number
}[]
skipped: {
index: number
reason: "older" | "duplicate" | "invalid"
message?: string
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
413 | That's too much at once. |
POST /v1/me/health/sync
The phone's HealthKit sync: new and changed samples, and ones deleted there.
Auth: user access token or platform agent key
Request body (up to 2 MB)
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
samples | any JSON[] | No | [] | up to 500 items |
removals | (object | object)[] | No | [] | up to 500 items |
Also checked: Unknown fields are rejected.
Response 200
{
written: number
skipped: {
index: number
reason: "older" | "duplicate" | "invalid"
message?: string
}[]
removed: number
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
413 | That's too much at once. |
GET /v1/me/health/sync/changes
Entries made in the apps since cursor, for the phone to write to HealthKit.
Auth: user access token or platform agent key
| Query parameter | Type | Required | Notes |
|---|---|---|---|
cursor | string | No | up to 100 characters |
Response 200
{
changes: {
op: "delete"
sampleId: string
metricId: string
} | {
op: "put"
sampleId: string
metricId: string
sample: {
sampleId: string
metricId: string
start: number
end: number
value: number
stage?: number
protection?: boolean | null
partner?: {
name?: string
userId?: string
}
note?: string
meta?: {
[key: string]: boolean | string | number
}
sourceId: string
sourceName: string
external: boolean
version: number
createdAt: number
updatedAt: number
}
}[]
cursor: string
more: boolean
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
GET /v1/me/health/sources
Your data sources (the Watch, the phone, apps, entries made here) in order: where samples overlap, the first wins.
Auth: user access token or platform agent key
Response 200
{
sources: {
sourceId: string
name: string
kind: "watch" | "phone" | "app" | "manual"
bundleId?: string
device?: string
priority: number
lastSeenAt: number
createdAt: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
PUT /v1/me/health/sources/order
Orders your sources (order: source ids, first wins; the rest keep their order after). Days changed from now on use it. In the app only.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
order | string[] | Yes | up to 200 items; each 1–64 characters |
Also checked: Unknown fields are rejected.
Response 200
{
sources: {
sourceId: string
name: string
kind: "watch" | "phone" | "app" | "manual"
bundleId?: string
device?: string
priority: number
lastSeenAt: number
createdAt: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
GET /v1/me/health/sharing
Your sharing: rules per category and metric, the rule in force for every metric, Sexual Activity's own sharing, and how many people follow you and you follow on Mirage.
Auth: user access token or platform agent key
Response 200
{
settings: {
scope: "category" | "metric"
id: string
rule: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
detail: "samples" | "aggregates"
}
updatedAt: number
}[]
effective: {
[key: string]: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
detail: "samples" | "aggregates"
}
}
sexualShare: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
counts: boolean
protectionRate: boolean
partnerCount: boolean
confirmedAt?: number
}
audiences: {
onSocial: boolean
followers: number
following: number
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
PUT /v1/me/health/sharing/:scope/:id
Shares a metric or category (audience: only_me, friends (mutual follows), followers, following or public; detail: aggregates or samples). Sensitive categories share aggregates only; Sexual Activity only through its own settings. In the app only.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:scope | metrics (one metric's rule) or categories (a category's rule, which its metrics follow unless they have their own). |
:id | A record's id in Customers: its object's prefix and a time-sortable id (acc_…, cnt_…, led_…, opp_…, act_…). |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
audience | "only_me" | "friends" | "followers" | "following" | "public" | Yes | ||
detail | "aggregates" | "samples" | No | "aggregates" |
Also checked: Unknown fields are rejected.
Response 200
{
settings: {
scope: "category" | "metric"
id: string
rule: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
detail: "samples" | "aggregates"
}
updatedAt: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
DELETE /v1/me/health/sharing/:scope/:id
Removes a metric's or category's own rule (a metric then follows its category; a category goes back to Only Me). In the app only.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:scope | metrics (one metric's rule) or categories (a category's rule, which its metrics follow unless they have their own). |
:id | A record's id in Customers: its object's prefix and a time-sortable id (acc_…, cnt_…, led_…, opp_…, act_…). |
Response 200
{
settings: {
scope: "category" | "metric"
id: string
rule: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
detail: "samples" | "aggregates"
}
updatedAt: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
PUT /v1/me/health/sexual/sharing
Sexual Activity's own sharing, separate from everything else: an audience and which aggregates (counts, protectionRate, partnerCount), with confirm: true whenever anything is shared. Never names or dates of entries; viewers must be 18 or over. In the app only.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
audience | "only_me" | "friends" | "followers" | "following" | "public" | Yes | |
counts | boolean | Yes | |
protectionRate | boolean | Yes | |
partnerCount | boolean | Yes | |
confirm | true | No |
Also checked: Unknown fields are rejected.
Response 200
{
profile: {
userId: string
consent: null | {
version: number
at: number
}
sensitiveConsent: {
body?: number
heart?: number
other?: number
activity?: number
cycle?: number
hearing?: number
medications?: number
mental?: number
mindfulness?: number
mobility?: number
nutrition?: number
respiratory?: number
sleep?: number
symptoms?: number
vitals?: number
sexual?: number
}
timeZone: string
pinned: string[]
assistant: {
enabled: boolean
categories: ("body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual")[]
}
sexualShare: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
counts: boolean
protectionRate: boolean
partnerCount: boolean
confirmedAt?: number
}
apiAccess: boolean
adult: boolean
ageUnknown: boolean
createdAt: number
updatedAt: number
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
GET /v1/me/health/sharing/preview
How a viewer in an audience sees the person's shared Health (nothing is recorded).
Auth: user access token or platform agent key
| Query parameter | Type | Required | Notes |
|---|---|---|---|
as | "friends" | "followers" | "following" | "public" | "stranger" | Yes |
Response 200
{
userId: string
metrics: {
metricId: string
detail: "samples" | "aggregates"
today: null | number
days: {
day: string
value: null | number
}[]
streak: number
longestStreak: number
best: null | {
day: string
value: number
}
average: null | number
samples?: {
value: number
start: number
stage?: number
end: number
sampleId: string
}[]
}[]
sexual: null | {
counts?: {
week: number
month: number
year: number
all: number
}
protectionRate?: null | number
partnerCount?: number
}
defs: {
metricId: string
label: string
category: "body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual"
kind: "duration" | "event" | "category" | "quantity"
unit: string
units: {
id: string
label: string
factor: number
offset?: number
}[]
aggregation: "duration" | "count" | "sum" | "average" | "latest"
decimals: number
sensitivity: "standard" | "sensitive" | "sexual"
values?: {
value: number
label: string
}[]
healthKit?: {
identifier: string
kind: "category" | "quantity" | "workout"
unit?: string
readOnly?: boolean
factor?: number
categoryValues?: number[]
}
healthConnect?: string
leaderboard?: "count" | "sum" | "average"
min?: number
max?: number
pinned?: boolean
description?: string
enabled: boolean
sortOrder: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
GET /v1/me/health/sexual/summary
Your Sexual Activity counts (this week, month, year and all time), the protection-used rate, and how many different partners your entries name. 404 unless the category is on.
Auth: user access token or platform agent key
Response 200
{
counts: {
week: number
month: number
year: number
all: number
}
protectionRate: null | number
partnerCount: number
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
404 | Sexual Activity isn't on. |
GET /v1/me/health/leaderboards
Your leaderboard opt-ins (server and metric) and the Mirage servers you're in, each with its leaderboards as GET /v1/health/servers/:serverId returns them (health; null when they couldn't be read).
Auth: user access token or platform agent key
Response 200
{
optIns: {
serverId: string
metricId: string
at: number
}[]
servers: {
health: null
serverId: string
name: string
iconUrl: null | string
} | {
health: {
settings: {
metrics: string[]
allowSexual: boolean
enabled: boolean
updatedAt: null | number
updatedBy: null | string
}
canManage: boolean
available: string[]
defs: {
metricId: string
label: string
category: "body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual"
kind: "duration" | "event" | "category" | "quantity"
unit: string
units: object[]
aggregation: "duration" | "count" | "sum" | "average" | "latest"
decimals: number
sensitivity: "standard" | "sensitive" | "sexual"
values?: object[]
healthKit?: {
identifier: string
kind: "category" | "quantity" | "workout"
unit?: string
readOnly?: boolean
factor?: number
categoryValues?: object
}
healthConnect?: string
leaderboard?: "count" | "sum" | "average"
min?: number
max?: number
pinned?: boolean
description?: string
enabled: boolean
sortOrder: number
}[]
adult: boolean
optedIn: string[]
}
serverId: string
name: string
iconUrl: null | string
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
GET /v1/me/health/audit
Your Health audit, newest first: consents, sharing changes, leaderboard opt-ins, Caity's reads, exports, and who viewed what you share (once a day each). In the app only.
Auth: user access token or platform agent key
| Query parameter | Type | Required | Notes |
|---|---|---|---|
cursor | string | No | up to 2,000 characters |
Response 200
{
entries: {
id: string
at: number
action: "export" | "consent.accept" | "consent.withdraw" | "consent.sensitive" | "share.update" | "share.sexual" | "share.view" | "leaderboard.opt_in" | "leaderboard.opt_out" | "assistant.update" | "assistant.read" | "api.update" | "delete_all" | "device.add" | "device.remove"
text: string
actor: null | {
userId: string
name?: string
}
details?: {
[key: string]: unknown
}
}[]
cursor: null | string
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
GET /v1/me/health/export
Your data, a page at a time (cursor from the last page): the first page also has your profile, sources, sharing and metric states; partners included.
Auth: user access token or platform agent key
| Query parameter | Type | Required | Notes |
|---|---|---|---|
cursor | string | No | up to 2,000 characters |
Response 200
{
samples: {
sampleId: string
metricId: string
start: number
end: number
value: number
stage?: number
protection?: boolean | null
partner?: {
name?: string
userId?: string
}
note?: string
meta?: {
[key: string]: boolean | string | number
}
sourceId: string
sourceName: string
external: boolean
version: number
createdAt: number
updatedAt: number
}[]
cursor: null | string
profile?: {
userId: string
consent: null | {
version: number
at: number
}
sensitiveConsent: {
body?: number
heart?: number
other?: number
activity?: number
cycle?: number
hearing?: number
medications?: number
mental?: number
mindfulness?: number
mobility?: number
nutrition?: number
respiratory?: number
sleep?: number
symptoms?: number
vitals?: number
sexual?: number
}
timeZone: string
pinned: string[]
assistant: {
enabled: boolean
categories: ("body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual")[]
}
sexualShare: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
counts: boolean
protectionRate: boolean
partnerCount: boolean
confirmedAt?: number
}
apiAccess: boolean
adult: boolean
ageUnknown: boolean
createdAt: number
updatedAt: number
}
sources?: {
sourceId: string
name: string
kind: "watch" | "phone" | "app" | "manual"
bundleId?: string
device?: string
priority: number
lastSeenAt: number
createdAt: number
}[]
sharing?: {
scope: "category" | "metric"
id: string
rule: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
detail: "samples" | "aggregates"
}
updatedAt: number
}[]
metrics?: {
metricId: string
unit?: string
share?: {
audience: "public" | "following" | "followers" | "only_me" | "friends"
detail: "samples" | "aggregates"
}
count: number
firstAt?: number
lastAt?: number
months: string[]
updatedAt: number
}[]
exportedAt: number
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
POST /v1/me/health/delete-all
Deletes everything; call again while done is false.
Auth: user access token or platform agent key
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
confirm | "delete" | Yes |
Also checked: Unknown fields are rejected.
Response 200
{
done: boolean
remaining: number
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
GET /v1/health/people/:userId
What someone shares with you: each metric's last 30 days, today, streaks and best day (samples only where they chose), and Sexual Activity's aggregates they opted in, when you're both 18 or over. Empty when you're in none of their audiences. Your view is in their audit once a day.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:userId | A member's user id (usr_…). |
Response 200
{
userId: string
metrics: {
metricId: string
detail: "samples" | "aggregates"
today: null | number
days: {
day: string
value: null | number
}[]
streak: number
longestStreak: number
best: null | {
day: string
value: number
}
average: null | number
samples?: {
value: number
start: number
stage?: number
end: number
sampleId: string
}[]
}[]
sexual: null | {
counts?: {
week: number
month: number
year: number
all: number
}
protectionRate?: null | number
partnerCount?: number
}
defs: {
metricId: string
label: string
category: "body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual"
kind: "duration" | "event" | "category" | "quantity"
unit: string
units: {
id: string
label: string
factor: number
offset?: number
}[]
aggregation: "duration" | "count" | "sum" | "average" | "latest"
decimals: number
sensitivity: "standard" | "sensitive" | "sexual"
values?: {
value: number
label: string
}[]
healthKit?: {
identifier: string
kind: "category" | "quantity" | "workout"
unit?: string
readOnly?: boolean
factor?: number
categoryValues?: number[]
}
healthConnect?: string
leaderboard?: "count" | "sum" | "average"
min?: number
max?: number
pinned?: boolean
description?: string
enabled: boolean
sortOrder: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
GET /v1/health/servers/:serverId
A Mirage server's Health leaderboards for a member: the settings, the metrics that can rank, whether you can manage them, whether you're known to be 18 or over (adult; Sexual Activity's leaderboard shows only then), and the metrics you opted in there.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:serverId | Mirage server id (gld_…). |
Response 200
{
settings: {
metrics: string[]
allowSexual: boolean
enabled: boolean
updatedAt: null | number
updatedBy: null | string
}
canManage: boolean
available: string[]
defs: {
metricId: string
label: string
category: "body" | "heart" | "other" | "activity" | "cycle" | "hearing" | "medications" | "mental" | "mindfulness" | "mobility" | "nutrition" | "respiratory" | "sleep" | "symptoms" | "vitals" | "sexual"
kind: "duration" | "event" | "category" | "quantity"
unit: string
units: {
id: string
label: string
factor: number
offset?: number
}[]
aggregation: "duration" | "count" | "sum" | "average" | "latest"
decimals: number
sensitivity: "standard" | "sensitive" | "sexual"
values?: {
value: number
label: string
}[]
healthKit?: {
identifier: string
kind: "category" | "quantity" | "workout"
unit?: string
readOnly?: boolean
factor?: number
categoryValues?: number[]
}
healthConnect?: string
leaderboard?: "count" | "sum" | "average"
min?: number
max?: number
pinned?: boolean
description?: string
enabled: boolean
sortOrder: number
}[]
adult: boolean
optedIn: string[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |
PUT /v1/health/servers/:serverId/settings
Turns a server's Health leaderboards on or off and picks metrics (enabled, metrics). allowSexual is the Sexual Activity leaderboard's own switch (off by default; only a manager known to be 18 or over changes it); turning it off takes everyone off it. Manage Server.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:serverId | Mirage server id (gld_…). |
Request body
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
enabled | boolean | Yes | ||
metrics | string[] | Yes | up to 20 items; each up to 48 characters | |
allowSexual | boolean | No | false |
Also checked: Unknown fields are rejected.
Response 200
{
settings: {
enabled: boolean
metrics: string[]
allowSexual: boolean
updatedAt: null | number
updatedBy: null | string
}
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
PUT /v1/health/servers/:serverId/opt-ins/:metricId
Ranks you with a metric on this server's leaderboards (it must be one the server chose); your scores are written at once.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:serverId | Mirage server id (gld_…). |
:metricId | A Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them. |
Response 200
{
optIns: {
serverId: string
metricId: string
at: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
DELETE /v1/health/servers/:serverId/opt-ins/:metricId
Stops ranking: your opt-in and every score for that server and metric go.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:serverId | Mirage server id (gld_…). |
:metricId | A Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them. |
Response 200
{
optIns: {
serverId: string
metricId: string
at: number
}[]
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:write on a personal key whose owner allowed it. |
403 | Change Health's privacy settings in the app. |
GET /v1/health/servers/:serverId/leaderboards/:metricId
A server's board (period week, month or all; at a past period such as w2026-10-05 or m2026-10): members who opted in, ranked, up to 100. Members only. Sexual Activity's ranks entries by the week only, among adults, and only for viewers known to be 18 or over.
Auth: user access token or platform agent key
| Path parameter | Description |
|---|---|
:serverId | Mirage server id (gld_…). |
:metricId | A Health metric's id from the registry (steps, heart_rate, sleep, sexual_activity, …), as GET /v1/me/health/metrics lists them. |
| Query parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
period | "week" | "month" | "all" | No | "week" | |
at | string | No | up to 16 characters |
Response 200
{
serverId: string
metricId: string
period: "all" | "month" | "week"
periodKey: string
entries: {
rank: number
userId: string
name: string
avatarUrl: null | string
value: number
you: boolean
}[]
optedIn: boolean
}Errors
| Status | Message |
|---|---|
403 | Health is only reachable from the Health, phone and Mirage apps. |
403 | Its owner hasn't let API keys use Health (Health → Privacy). |
403 | Health needs health:read on a personal key whose owner allowed it. |