Health import endpoint with a durable rejection trace #13

Closed
opened 2026-08-26 09:14:42 +00:00 by GKaszewski · 0 comments
Owner

What to build

An endpoint that accepts a day's metrics from an external provider — iOS Shortcuts reading HealthKit, Tasker, a cron script — authenticated by the scoped API token from #3.

Partial payloads are normal and expected. Providers differ in what they can supply, and a payload carrying three of the eight kinds is valid, not an error.

Partial validity is the important behaviour. If one metric in a payload of twenty fails validation, the nineteen valid ones are stored and the invalid one is rejected and recorded in a durable trace the User can see. Rejecting the whole payload would mean one bad field costs every night's data from a client that cannot show the User an error — a trivial units bug silently costing months of otherwise-good data. Clamping would be worse still: a 970% blood oxygen becoming 100% fabricates a measurement and hides the bug permanently.

The rejection trace is the same surface that receives unreadable stored rows from #8. One place the User looks to find data that could not be used.

Re-posting a date upserts — (User, Date, MetricKind) uniqueness and the Manual-never-clobbered rule from #8 already define the semantics, so imports are naturally idempotent.

Backfill needs a bound on how many dates one request may carry.

Acceptance criteria

  • Endpoint accepts a dated payload authenticated by an API token and nothing else
  • Partial payloads are accepted without error
  • Valid metrics are stored while invalid ones are rejected, in the same request
  • Rejections are persisted with enough detail to diagnose, and surfaced in settings
  • The response reports what was accepted and what was rejected
  • Re-posting the same date upserts and never duplicates
  • A Manual value is not overwritten by an import
  • Payloads exceeding the configured date limit are refused with a clear error
  • Unknown metric names in a payload are rejected and recorded, not silently dropped

Blocked by

## What to build An endpoint that accepts a day's metrics from an external provider — iOS Shortcuts reading HealthKit, Tasker, a cron script — authenticated by the scoped API token from #3. Partial payloads are normal and expected. Providers differ in what they can supply, and a payload carrying three of the eight kinds is valid, not an error. **Partial validity is the important behaviour.** If one metric in a payload of twenty fails validation, the nineteen valid ones are stored and the invalid one is rejected and recorded in a durable trace the User can see. Rejecting the whole payload would mean one bad field costs every night's data from a client that cannot show the User an error — a trivial units bug silently costing months of otherwise-good data. Clamping would be worse still: a 970% blood oxygen becoming 100% fabricates a measurement and hides the bug permanently. The rejection trace is the same surface that receives unreadable stored rows from #8. One place the User looks to find data that could not be used. Re-posting a date upserts — `(User, Date, MetricKind)` uniqueness and the Manual-never-clobbered rule from #8 already define the semantics, so imports are naturally idempotent. Backfill needs a bound on how many dates one request may carry. ## Acceptance criteria - [ ] Endpoint accepts a dated payload authenticated by an API token and nothing else - [ ] Partial payloads are accepted without error - [ ] Valid metrics are stored while invalid ones are rejected, in the same request - [ ] Rejections are persisted with enough detail to diagnose, and surfaced in settings - [ ] The response reports what was accepted and what was rejected - [ ] Re-posting the same date upserts and never duplicates - [ ] A `Manual` value is not overwritten by an import - [ ] Payloads exceeding the configured date limit are refused with a clear error - [ ] Unknown metric names in a payload are rejected and recorded, not silently dropped ## Blocked by - #3 - #8
GKaszewski added the ready-for-agent label 2026-08-26 09:14:42 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: GKaszewski/k-mood#13