docs(api): say which account payload fields describe the record

The `AccountPayload` description still claimed that under an active switch the
payload describes the caller "their preferences, their modules, their
identity", which stopped being true when the module map and the cycle flag
started answering for the active record — `ModuleMap` on the same spec said
the opposite. The iOS client reads this file.

Both descriptions now say the same thing: identity and preferences are the
caller's, `modules` and `cycleTrackingEnabled` are the active record's, and
both are masked to the sections the active grant opens. Spec regenerated.
This commit is contained in:
Marc-André Bombeck committed 2026-09-10 01:57:12 +02:00
1 parent 8ff1b865fc
commit 0bae48180a
3 files changed
+16 -10

No files matched your search

+13 -7
View File
@@ -18142,7 +18142,8 @@ paths:
summary: The signed-in account, and what sharing lets it do
description: "Keeps answering while the browser is acting on another record — the switcher, the banner naming whose
record is open, and the way back out all read it, so a refusal here would strand a switched session. It is
therefore an ACTOR surface: it always serves the caller's own rows, and a request that attaches
therefore an ACTOR surface: it serves the caller's own rows — apart from `modules` and `cycleTrackingEnabled`,
which answer for the record the session is inside, masked to what the grant opens — and a request that attaches
`X-HealthLog-Account` is refused with 403 `sharing.not_permitted` rather than quietly answered. A client that
wants a record's data sends the selector on the read that needs it, never on this call."
responses:
@@ -43886,9 +43887,10 @@ components:
description: Fully-resolved module enable/disable map. Every toggleable module key is present. `false` means the surface
should disappear end-to-end (nav, dashboard, insights, …). `cycle` mirrors `cycleTrackingEnabled`; `coach`
mirrors the resolved per-user opt-out + operator master flag. On `GET /api/auth/me` it answers for the RECORD
the session is inside rather than for the caller, because every surface it gates shows the record's data; with
no switch — which is every native request, since the Bearer transport carries none — the two are the same
account and the payload is unchanged.
the session is inside rather than for the caller, because every surface it gates shows the record's data, and it
is masked to the sections the active grant opens — a module outside them reads `false` rather than the record's
true state. With no switch — which is every native request, since the Bearer transport carries none — the two
are the same account, nothing is masked and the payload is unchanged.
PatchModulesResponse:
type: object
properties:
@@ -65765,9 +65767,13 @@ components:
- recordSession
additionalProperties: {}
description: "The signed-in account: its identity, its preferences, and (since v1.36.0) what account sharing lets it do.
Additional properties are the preference fields this spec does not yet enumerate. Under an active switch this
payload still describes the CALLER — their preferences, their modules, their identity — because display
preferences belong to the person at the keyboard rather than to the record they are reading."
Additional properties are the preference fields this spec does not yet enumerate. Under an active switch the
identity and preference fields still describe the CALLER, because display preferences belong to the person at
the keyboard rather than to the record they are reading. Two fields are the exception (v1.38.14): `modules` and
`cycleTrackingEnabled` describe the ACTIVE RECORD, since every surface they gate shows the record's data — and
they are masked to the sections the active grant opens, so a scoped grant reads `false` for a module outside it
rather than the record's true state. With no switch — which is every native request, since the Bearer transport
carries none — the two are the same account and nothing is masked."
AccountAccess:
type: object
properties:
+2 -2
View File
@@ -453,7 +453,7 @@ const accountPayload = z
.meta({
id: "AccountPayload",
description:
"The signed-in account: its identity, its preferences, and (since v1.36.0) what account sharing lets it do. Additional properties are the preference fields this spec does not yet enumerate. Under an active switch this payload still describes the CALLER — their preferences, their modules, their identity — because display preferences belong to the person at the keyboard rather than to the record they are reading.",
"The signed-in account: its identity, its preferences, and (since v1.36.0) what account sharing lets it do. Additional properties are the preference fields this spec does not yet enumerate. Under an active switch the identity and preference fields still describe the CALLER, because display preferences belong to the person at the keyboard rather than to the record they are reading. Two fields are the exception (v1.38.14): `modules` and `cycleTrackingEnabled` describe the ACTIVE RECORD, since every surface they gate shows the record's data — and they are masked to the sections the active grant opens, so a scoped grant reads `false` for a module outside it rather than the record's true state. With no switch — which is every native request, since the Bearer transport carries none — the two are the same account and nothing is masked.",
});
/**
@@ -475,7 +475,7 @@ export const accountSharingPaths: NonNullable<ZodOpenApiObject["paths"]> = {
tags: ["Auth"],
summary: "The signed-in account, and what sharing lets it do",
description:
"Keeps answering while the browser is acting on another record — the switcher, the banner naming whose record is open, and the way back out all read it, so a refusal here would strand a switched session. It is therefore an ACTOR surface: it always serves the caller's own rows, and a request that attaches `X-HealthLog-Account` is refused with 403 `sharing.not_permitted` rather than quietly answered. A client that wants a record's data sends the selector on the read that needs it, never on this call.",
"Keeps answering while the browser is acting on another record — the switcher, the banner naming whose record is open, and the way back out all read it, so a refusal here would strand a switched session. It is therefore an ACTOR surface: it serves the caller's own rows — apart from `modules` and `cycleTrackingEnabled`, which answer for the record the session is inside, masked to what the grant opens — and a request that attaches `X-HealthLog-Account` is refused with 403 `sharing.not_permitted` rather than quietly answered. A client that wants a record's data sends the selector on the read that needs it, never on this call.",
responses: {
...stdResponses,
"200": {
+1 -1
View File
@@ -84,7 +84,7 @@ const moduleMapResolved = z
.meta({
id: "ModuleMap",
description:
"Fully-resolved module enable/disable map. Every toggleable module key is present. `false` means the surface should disappear end-to-end (nav, dashboard, insights, …). `cycle` mirrors `cycleTrackingEnabled`; `coach` mirrors the resolved per-user opt-out + operator master flag. On `GET /api/auth/me` it answers for the RECORD the session is inside rather than for the caller, because every surface it gates shows the record's data; with no switch — which is every native request, since the Bearer transport carries none — the two are the same account and the payload is unchanged.",
"Fully-resolved module enable/disable map. Every toggleable module key is present. `false` means the surface should disappear end-to-end (nav, dashboard, insights, …). `cycle` mirrors `cycleTrackingEnabled`; `coach` mirrors the resolved per-user opt-out + operator master flag. On `GET /api/auth/me` it answers for the RECORD the session is inside rather than for the caller, because every surface it gates shows the record's data, and it is masked to the sections the active grant opens — a module outside them reads `false` rather than the record's true state. With no switch — which is every native request, since the Bearer transport carries none — the two are the same account, nothing is masked and the payload is unchanged.",
});
const moduleMapEnvelopeInner = z