docs(api): describe the legacy integration-status fields as still served

The published contract said the nine health-shaped fields that predate
`health` were removed in v1.44, but every status read still sends them:
older native builds decode them, the current one falls back to them against
a server without `health`, and the web card's copied diagnostics read the
failure streak. They stay deprecated, without a removal release.
This commit is contained in:
Marc-André Bombeck committed 2026-10-25 12:12:14 +01:00
1 parent 856eb036f8
commit d6bb7bc905
4 files changed
+48 -46

No files matched your search

+34 -35
View File
@@ -82136,7 +82136,7 @@ components:
- type: "null"
syncHealth:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Present on the GET read; omitted from the PATCH echo."
description: "Deprecated, still served: read `health`. Present on the GET read; omitted from the PATCH echo."
$ref: "#/components/schemas/SyncHealth"
metricFreshness:
description: Per-metric-type freshness for the `APPLE_HEALTH` source, with the per-type arrival facts. Present on the
@@ -82814,7 +82814,7 @@ components:
type: boolean
state:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`."
description: "Deprecated, still served: read `health`."
type: string
enum:
- connected
@@ -82837,14 +82837,14 @@ components:
- type: "null"
lastError:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Last recorded failure message, decrypted from the ledger.
Null when the last attempt succeeded or none has run."
description: "Deprecated, still served: read `health`. Last recorded failure message, decrypted from the ledger. Null
when the last attempt succeeded or none has run."
type:
- string
- "null"
syncHealth:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`."
description: "Deprecated, still served: read `health`."
$ref: "#/components/schemas/SyncHealth"
health:
description: The unified connection state. Present whenever the connection exists; null in the rare case it resolved to
@@ -83033,9 +83033,9 @@ components:
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
tokenExpired:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. The stored access token's expiry is in the past. Read
straight off the row — unlike the Withings status read, this endpoint does not refresh; the sync path
refreshes lazily instead."
description: "Deprecated, still served: read `health`. The stored access token's expiry is in the past. Read straight
off the row — unlike the Withings status read, this endpoint does not refresh; the sync path refreshes
lazily instead."
type: boolean
tokenExpiresAt:
type: string
@@ -83124,8 +83124,7 @@ components:
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
tokenExpired:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. True after a refresh attempt that did not produce a live
token."
description: "Deprecated, still served: read `health`. True after a refresh attempt that did not produce a live token."
type: boolean
tokenRefreshFailed:
description: The read tried to refresh an expired token and could not — either the per-user credentials are gone or
@@ -83145,8 +83144,8 @@ components:
- "null"
hasActivityScope:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Pre-computed convenience flag the reconnect banner reads. A
null `scope` counts as missing activity."
description: "Deprecated, still served: read `health`. Pre-computed convenience flag the reconnect banner reads. A null
`scope` counts as missing activity."
type: boolean
health:
description: The unified connection state. Present whenever the connection exists; null in the rare case it resolved to
@@ -83455,8 +83454,8 @@ components:
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
tokenExpired:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Read straight off the row — this endpoint does not refresh;
the sync path refreshes lazily."
description: "Deprecated, still served: read `health`. Read straight off the row — this endpoint does not refresh; the
sync path refreshes lazily."
type: boolean
tokenExpiresAt:
type: string
@@ -83626,8 +83625,8 @@ components:
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
tokenExpired:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Read straight off the row — this endpoint does not refresh;
the sync path refreshes lazily."
description: "Deprecated, still served: read `health`. Read straight off the row — this endpoint does not refresh; the
sync path refreshes lazily."
type: boolean
tokenExpiresAt:
type: string
@@ -83677,7 +83676,7 @@ components:
when every type was read to its start. Kept after the backfill completes; cleared on reconnect.
needsReauth:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. The stored refresh token was rejected with `invalid_grant` —
description: "Deprecated, still served: read `health`. The stored refresh token was rejected with `invalid_grant` —
Google expires it after 7 days while the OAuth client is in Testing publishing mode, and a user-revoked
grant lands here too. Distinct from `parked` and from `disconnected`: the connection still exists, it just
cannot mint a token until the user reconnects. Only Google Health has this field."
@@ -83881,7 +83880,7 @@ components:
description: The user has stored their own BYO client id and secret.
state:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`."
description: "Deprecated, still served: read `health`."
type: string
enum:
- connected
@@ -83904,13 +83903,13 @@ components:
- type: "null"
lastError:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`."
description: "Deprecated, still served: read `health`."
type:
- string
- "null"
syncHealth:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`."
description: "Deprecated, still served: read `health`."
$ref: "#/components/schemas/SyncHealth"
health:
description: The unified connection state. Present whenever the connection exists; null in the rare case it resolved to
@@ -84305,7 +84304,7 @@ components:
- disconnected
- parked
- unknown
description: "Deprecated: removed in v1.44, read `health`."
description: "Deprecated, still served: read `health`."
deprecated: true
lastSuccessAt:
anyOf:
@@ -84321,7 +84320,7 @@ components:
- type: "null"
lastError:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`."
description: "Deprecated, still served: read `health`."
type:
- string
- "null"
@@ -84348,11 +84347,11 @@ components:
additionalProperties: false
- type: "null"
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Per-kind consecutive-failure counters, or null when the row
has never written a bucket payload. Compare against the top-level `threshold` to know how close the provider
is to the operator alert."
description: "Deprecated, still served: read `health`. Per-kind consecutive-failure counters, or null when the row has
never written a bucket payload. Compare against the top-level `threshold` to know how close the provider is
to the operator alert."
syncHealth:
description: "Deprecated: removed in v1.44, read `health`."
description: "Deprecated, still served: read `health`."
deprecated: true
$ref: "#/components/schemas/SyncHealth"
metricFreshness:
@@ -84389,7 +84388,7 @@ components:
- type: "null"
tokenExpired:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Null — not false — when there is no connection to judge."
description: "Deprecated, still served: read `health`. Null — not false — when there is no connection to judge."
type:
- boolean
- "null"
@@ -84432,14 +84431,14 @@ components:
- "null"
hasActivityScope:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Withings only."
description: "Deprecated, still served: read `health`. Withings only."
type: boolean
planUnavailable:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Withings only. The readings this connection's Withings API
plan withholds (status 277 since the 2026-10-12 plan split), deduplicated and in display order. Empty when
the plan refuses nothing. The sync skips these and keeps importing everything else; the list refreshes when
the weekly re-probe asks again."
description: "Deprecated, still served: read `health`. Withings only. The readings this connection's Withings API plan
withholds (status 277 since the 2026-10-12 plan split), deduplicated and in display order. Empty when the
plan refuses nothing. The sync skips these and keeps importing everything else; the list refreshes when the
weekly re-probe asks again."
type: array
items:
type: string
@@ -84468,7 +84467,7 @@ components:
- weight
needsReauth:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Google Health only."
description: "Deprecated, still served: read `health`. Google Health only."
type: boolean
available:
description: Oura / Polar / Strava only.
@@ -84478,7 +84477,7 @@ components:
type: boolean
scopeReconnect:
deprecated: true
description: "Deprecated: removed in v1.44, read `health`. Oura only. Collections the stored grant does not cover: Oura
description: "Deprecated, still served: read `health`. Oura only. Collections the stored grant does not cover: Oura
refused them for a missing scope (named in its 401, or inferred when sibling collections succeeded with the
same token), or the scopes Oura reported at connect leave theirs out. Not a failure and not counted toward
the reconnect threshold; one reconnect asks for the missing scopes. Empty when the grant covers everything
@@ -84534,7 +84533,7 @@ components:
description: The unified connection state, null when the provider is not connected (and for Fitbit once
`retirement.ended`). It supersedes `state`, `lastError`, `consecutiveFailuresByKind`, `syncHealth`,
`tokenExpired`, `needsReauth`, `hasActivityScope`, `planUnavailable` and `scopeReconnect` on this entry;
those nine are deprecated and removed in v1.44.
those nine are deprecated and still served for clients that predate `health`.
required:
- integration
- state
@@ -1,7 +1,7 @@
{
"$comment": "Comment lines that open with a release version, per file under src/. Maintained by src/__tests__/version-stamp-ratchet.test.ts.",
"count": 2897,
"total": 9247,
"total": 9246,
"entries": {
"__tests__/account-payload-consumer-guard.test.ts": 2,
"__tests__/acting-account-boundary-guard.test.ts": 7,
@@ -360,7 +360,7 @@
"app/api/integrations/healthkit/__tests__/route.test.ts": 4,
"app/api/integrations/healthkit/route.ts": 3,
"app/api/integrations/status/__tests__/route.test.ts": 2,
"app/api/integrations/status/route.ts": 7,
"app/api/integrations/status/route.ts": 6,
"app/api/integrations/withings/resume/__tests__/route.test.ts": 1,
"app/api/integrations/withings/resume/route.ts": 1,
"app/api/internal/deploy-webhook/__tests__/route.test.ts": 2,
+5 -4
View File
@@ -502,8 +502,8 @@ export const GET = apiHandler(async () => {
} satisfies IntegrationViewModel & NightscoutExtras,
{
...publish(dawarichStatus),
// v1.43 — Dawarich, read by the card through `health`. The ledger core
// rides along like on every entry, and goes with the rest in v1.44.
// Dawarich, read by the card through `health`. The ledger core rides
// along like on every entry.
connected: dawarichConfigured,
configured: dawarichConfigured,
health: health["dawarich"],
@@ -524,8 +524,9 @@ interface IntegrationViewModel {
* health-shaped field on this entry (`state`, `lastError`,
* `consecutiveFailuresByKind`, `syncHealth`, `tokenExpired`, `needsReauth`,
* `hasActivityScope`, `planUnavailable`, `scopeReconnect`) is folded into
* it and goes once the settings cards render `health`; no other client
* reads this envelope.
* it. Those stay on the wire, deprecated in the published document; the
* cards render `health`, and only their copied diagnostics still read
* `consecutiveFailuresByKind`. No native client reads this envelope.
*/
health: ConnectionHealth | null;
state: string;
+7 -5
View File
@@ -75,10 +75,12 @@ const healthKitEntry = z
"One resolved HealthKit metric mapping (defaults merged with the user's stored overrides).",
});
// The health-shaped fields that predate `health` (v1.42.4). They stay on the
// wire through v1.43 so App 1.2.0 keeps decoding, are marked deprecated in the
// document, and go in v1.44.
const LEGACY_HEALTH_FIELD = "Deprecated: removed in v1.44, read `health`.";
// The health-shaped fields that predate `health`. They are marked deprecated
// in the document and stay on the wire: native builds from before `health`
// still decode them, the current one falls back to them only against a server
// that sends no `health`, and the web card's copied diagnostics read the
// failure streak. No removal release is set.
const LEGACY_HEALTH_FIELD = "Deprecated, still served: read `health`.";
function legacy(text?: string): { deprecated: true; description: string } {
return {
@@ -1184,7 +1186,7 @@ const integrationEnvelopeEntry = z
health: connectionHealth
.nullable()
.describe(
"The unified connection state, null when the provider is not connected (and for Fitbit once `retirement.ended`). It supersedes `state`, `lastError`, `consecutiveFailuresByKind`, `syncHealth`, `tokenExpired`, `needsReauth`, `hasActivityScope`, `planUnavailable` and `scopeReconnect` on this entry; those nine are deprecated and removed in v1.44.",
"The unified connection state, null when the provider is not connected (and for Fitbit once `retirement.ended`). It supersedes `state`, `lastError`, `consecutiveFailuresByKind`, `syncHealth`, `tokenExpired`, `needsReauth`, `hasActivityScope`, `planUnavailable` and `scopeReconnect` on this entry; those nine are deprecated and still served for clients that predate `health`.",
),
})
.meta({