Skip to content
AFAIFitnessAPI
AI Features

Personalizing Your App With a User's Own Wearable Data

Updated July 27, 2026

The pattern that actually ships is narrate my structured data: your backend computes the averages, personal baselines and deltas, and the language model only writes prose about numbers it was handed. Do that rather than pasting raw time-series into the prompt and asking the model to find the trend, which is where the arithmetic quietly goes wrong and where the token bill grows. The hard constraint is not technical: sending a user's health profile to a third-party LLM API is exactly what Apple's App Store Review Guideline 5.1.2(i) covers, so you must clearly disclose it and get explicit permission before the first call. Whatever the model writes is your app's output, not the vendor's, and it is not medical advice.

The reliable way to personalize an app with a user's wearable data and a language model is the one already visible in shipped products: your code computes the numbers, and the model only writes prose about them. Averages, baselines, deltas and trend labels are calculated deterministically in your backend; the model receives those finished values and turns them into a sentence a human wants to read. Do that instead of pasting raw time-series into the prompt and asking the model to find the trend — that is where the arithmetic quietly goes wrong, and it is where your token bill grows fastest.

There is one constraint that is not a technical choice at all. Sending a user's health profile to a third-party LLM API is precisely the thing Apple's App Store Review Guideline 5.1.2(i) addresses, and it requires disclosure plus explicit permission before the first call. Get that wrong and the feature does not ship, no matter how good the prose is.

The pattern that ships: narrate, don't compute

Look at what companies have publicly announced and a shape repeats. Strava's Athlete Intelligence produces generative summaries of a completed activity — the metrics Strava already computed, turned into short prose. Oura Advisor is generative chat over the user's own ring data. WHOOP Coach, publicly announced as built with OpenAI models, is conversational coaching over the member's own biometrics. In each case, as described by the companies, the interesting numbers already existed before the model was involved.

That is not a coincidence, and it is not timidity. It is the property that makes the feature shippable: there is very little the model can get factually wrong that the user cannot see. The user's own dashboard shows the same numbers on the same screen. If the prose says something the chart contradicts, that is a bug you can detect, not a subtle inaccuracy that lives in production for months.

Flip it around and the failure mode is obvious. If you hand a model 10,080 minute-level heart-rate samples and ask it what this week's average was, you get a number the user cannot check and you cannot regression-test. Models are unreliable at arithmetic and aggregation, and they are fluent about it — a wrong weekly average arrives with the same confident tone as a right one. The two-stage split is the whole trick: the model does language, your code does maths.

Four ways to put wearable data in front of a model

ApproachHow it worksWhere it breaks
Narrate a precomputed summaryYour backend computes window averages, a personal baseline, deltas and a categorical label. The prompt contains only those finished values. The model writes prose.Interpretation, not arithmetic — the model can still editorialize past what the data supports ("your HRV is down, you might be getting sick"). Constrain tone and scope in the system prompt, and check the output.
Model reasons over raw time-seriesYou serialize days of samples into the prompt and ask for the trend, the average, or the outlier.Aggregation and arithmetic errors the user has no way to spot; results that vary between runs on identical input; token cost that scales with sampling frequency. Avoid.
Model calls tools to query your dataYou expose functions like get_hrv_summary(user_id, window_days). The model requests a call, your code executes it against your own aggregation logic, and returns computed values.Extra round trips, and the model still chooses the window — it can ask for the wrong one. Validate every tool argument server-side. Good for open-ended chat, overkill for a fixed weekly recap.
No data grounding at allThe model answers fitness questions from its priors, with a thin user profile.Confident generic output that reads personalized but is not. Worst accuracy and worst safety profile of the four.

For a first ship, the top row is almost always right. Tool use earns its place once the surface becomes genuinely conversational and you cannot predict in advance which window the user will ask about.

What to actually put in the prompt

Pre-aggregated summaries and deltas. Not raw samples. This is a cost argument and an accuracy argument at the same time, which is unusual and worth taking seriously.

A useful payload for a weekly recap looks roughly like this:

{
  "window": { "start": "2026-07-20", "end": "2026-07-26" },
  "coverage": { "nights_with_sleep_data": 6, "days_in_window": 7, "source": "apple_health" },
  "hrv_rmssd_ms": {
    "window_avg": 62,
    "baseline_60d_avg": 68,
    "delta": -6,
    "label": "below_typical"
  },
  "resting_hr_bpm": { "window_avg": 54, "baseline_60d_avg": 53, "delta": 1, "label": "typical" },
  "training_load": { "sessions": 4, "total_minutes": 213, "prev_window_minutes": 145, "label": "higher_than_usual" }
}

Four things make that payload work:

  • Every number is final. Already rounded, already in the unit you want displayed, already the value shown in the UI. The model never reformats 62.4 into "about 60".
  • Every metric carries its own baseline. "Below typical" is meaningless without saying typical for this person. Population norms are a different and much weaker claim.
  • The label is yours. below_typical was assigned by a threshold you wrote, tested and can change. A threshold living inside the model's head is neither auditable nor stable across releases.
  • Provenance travels with the data. If you pull through an aggregator, a week can mix a phone pedometer with a chest strap and a ring. Carry the source field — and see wearable data APIs for why source mixing is the normal condition, not the edge case.

Then say so explicitly in the system prompt: you may only reference numbers that appear in the data payload; do not compute new values; if a value is absent, do not estimate it. Provider guidance on reducing hallucination points the same direction — restrict the model to the supplied material rather than its general knowledge.

Compute the trend in code, hand the model the conclusion

The labelling step is a few lines and it is the most valuable code in the feature:

function label(value: number, baseline: number, sd: number) {
  const z = (value - baseline) / sd;
  if (z <= -1) return "below_typical";
  if (z >= 1) return "above_typical";
  return "typical";
}

Whatever thresholds you choose, choose them deliberately. Two consequences follow.

First, set a minimum effect size before you let the feature speak at all. Wearable metrics carry real measurement error, and a small night-to-night change is often noise rather than signal — see what HRV is and the practical notes in the HRV API guide. Narrating a tiny wobble as a meaningful trend is false precision regardless of whether a model or a template wrote the sentence. If the delta is inside the noise band, the honest output is "steady", and that costs nothing to generate.

Second, validate the output against the payload. A cheap deterministic check catches invented specifics:

const allowed = new Set(numbersIn(payload));      // every value you actually sent
const emitted = (text.match(/-?\d+(\.\d+)?/g) ?? []).map(Number);
const invented = emitted.filter(n => !allowed.has(n));
if (invented.length > 0) return renderTemplateFallback(payload);

Tune it for dates and ordinals, and prefer falling back to a deterministic template over re-prompting — a bad generation should not be able to loop into cost. This is risk reduction, not a guarantee; it catches fabricated numbers, not bad advice.

Missing data is the normal case

Users take the watch off. Batteries die. A sync fails. Sleep tracking captures four nights out of seven. Any real pipeline handles this constantly, and it is where a narration feature most often embarrasses itself.

Three rules that hold up:

  1. Never silently impute. If you fill a gap with a rolling average and label the result the same way as measured data, you have manufactured a fact and handed it to a model that will state it confidently.
  2. Make coverage an explicit field, and gate on it. If coverage falls below your threshold, do not call the model at all — render a deterministic "not enough data this week yet" state. It is cheaper, it is faster, and it cannot hallucinate.
  3. Omitting a key is not the same as saying it is unknown. If hrv_rmssd_ms simply is not there, a model may still reach for HRV because the surrounding context is about recovery. Send the key with an explicit unknown marker plus a reason, and instruct the model to mention the gap rather than work around it. "You only wore the ring three nights, so this week's recovery picture is partial" is a genuinely better user experience than confident prose built on three data points pretending to be seven.

The privacy layer you cannot skip

This is the part teams discover late, and it is a shipping blocker rather than a polish item.

Apple's Guideline 5.1.2(i) states: "You must clearly disclose where personal data will be shared with third parties, including with third-party AI, and obtain explicit permission before doing so." A user's HRV, sleep, resting heart rate and body-composition history leaving your servers for an external LLM API is exactly that. Disclosure has to be clear, permission has to be explicit, and it has to come first — not a line in a terms-of-service document the user scrolled past.

There is a second restriction that matters specifically here and that teams miss because they stop reading at 5.1.2(i): Apple places further limits on what health and fitness data may be disclosed to third parties and for what purposes. A wearable payload is exactly the data those limits are about, so "we disclosed it and the user tapped accept" is not automatically the end of the question. Read the current guidelines directly rather than relying on a summary — including this one — and see App Store health data rules, which also covers the Play side and the guideline enumeration.

Beyond the stores, three practical points:

  • The contract term usually comes before the code. Health apps commonly need a no-retention, no-training arrangement with the model vendor before the feature can ship at all — legal and security review will ask for it, and enterprise API tiers are where those terms live. Check what your specific agreement says rather than assuming; the default consumer terms are frequently not what you need.
  • Send the least you can. A pseudonymous user ID and a dozen derived metrics are enough to write a recap. Name, email, date of birth and raw records are not needed for the sentence you want back. Minimization also shrinks the prompt, which is the cheapest cost optimization available.
  • Logging is a third party too. Shipping full prompts containing health data to an external observability vendor is another disclosure. Log guardrail verdicts, model version and identifiers by default; think hard before logging payload contents.

Consent capture, storage and deletion are their own subject — start with health data user consent and make sure turning the AI feature off leaves the rest of the app working.

What you still own

The narration pattern removes most of the factual risk. It does not remove the interpretive risk, and interpretation is where health features get into trouble.

"Your HRV is below your baseline and your resting heart rate is up" is a description. "You may be fighting an infection, take the week off" is a disease-adjacent inference with a recommended action, and it is a different kind of claim entirely. Keep the model on describing what happened and suggesting general training adjustments; keep diagnosis, alerts and clinical language out of the prompt, out of the allowed vocabulary, and out of your marketing copy — intended purpose is set by what you say about the product as much as by what the code does.

Two things follow, and the first is specific to a metrics surface. A gate has to read the metrics, not only the message. In a coaching chat the risk signals arrive as text a user typed; here some of them arrive as fields in your payload, and a summary generator that happily narrates any number it is handed will narrate those too. Screen for cardiac, eating-disorder, self-harm and acute-injury signals deterministically before the generation call, and route declared conditions, pregnancy and recent surgery to a constrained path rather than a refusal. The trigger list, the tiering and the reason it must be a rule rather than a prompt instruction are on LLM safety for fitness advice — and they are engineering judgement, not a standard, so have a clinician review yours.

A rapidly worsening metric is not an alert. Whatever your gate does, resist the temptation to let a falling HRV trend or a rising resting heart rate trigger a warning; that turns a recap into a monitoring claim, which is a statement about intended purpose and a different regulatory conversation entirely.

And say the quiet part: a "not medical advice" line is an app-store expectation and good practice, but it is not a liability shield, and it does not make an unsafe answer safe. The app publisher, not the model vendor, owns what the app tells a user.

Cost, briefly

Because the payload is a summary rather than a time-series, this feature is cheap by construction. Work it out with your own numbers rather than trusting anyone's per-user figure:

[ (input tokens ÷ 1,000,000 × input rate) + (output tokens ÷ 1,000,000 × output rate) ] × calls

Using Claude Opus 5 (claude-opus-5) at $5.00 per million input tokens and $25.00 per million output tokens as of mid-2026 — verify current pricing in the provider's docs — a purely hypothetical weekly recap with a 1,500-token prompt and a 250-token reply costs 1500 ÷ 1e6 × 5.00 = $0.0075 in and 250 ÷ 1e6 × 25.00 = $0.00625 out. Substitute your real prompt size, measured with the token-counting endpoint rather than guessed, and multiply by your own call volume.

Two levers are specific to this feature rather than general cost advice. The payload is the lever. Because you decide what goes in it, aggregating harder is simultaneously a cost cut, an accuracy improvement and a data-minimisation win — the only place in this design where those three point the same way. And weekly recaps are not interactive, so generating your whole user base's overnight through the batch path is available to you in a way it is not for a chat surface. The cheapest token is still the one you never send: answer "what was my HRV on Tuesday?" straight from the database with no model call at all. What AI fitness features cost covers caching, tiering and the rest.

Where to go next

Build the aggregation layer first and the prompt second. If your baselines, deltas and coverage flags are correct, the language part is comparatively easy — and if they are not, no amount of prompt engineering rescues it. Get the consent flow designed before the first API call goes out, not after review rejects you.

Next: wearable data APIs for the ingestion layer, health data user consent for the permission and storage side, and choosing an LLM for fitness apps once you know what you are asking the model to do.

Frequently asked questions

Should I send raw heart-rate or HRV samples to the LLM?
Generally no. Serializing days of samples into a prompt asks the model to do aggregation and arithmetic, which is the part models are least reliable at and the part users cannot verify, and it makes the prompt scale with your sampling frequency. Compute window averages, a personal baseline, the delta and a trend label in code, and send only those finished values. If you need the model to fetch data on demand for an open-ended chat surface, expose a tool that returns your own precomputed summary rather than raw rows, and validate the arguments server-side.
Do I need the user's permission to send their health data to an LLM API?
For iOS, Apple's App Store Review Guideline 5.1.2(i) says you must clearly disclose where personal data will be shared with third parties, including with third-party AI, and obtain explicit permission before doing so. A health profile going to an external model API is that. Note also that Apple has no generative-AI-specific guideline; LLM features are governed by the general user-generated-content, physical-harm and medical rules plus 5.1.2(i). Separately, most health apps find they need a no-retention and no-training contract term with the model vendor before legal will approve the feature at all.
How do I stop the model inventing numbers about the user?
Two layers. In the prompt, state that the model may only reference values present in the supplied payload, must not compute new ones, and must not estimate anything absent. Then check the output in code: extract every number the model emitted and confirm each one appears in what you sent, falling back to a deterministic template on a mismatch rather than re-prompting. This reduces risk but does not eliminate it. It catches fabricated figures; it does not catch bad advice attached to correct figures.
What should the app do when the user's wearable data has gaps?
Handle it explicitly rather than papering over it. Do not silently impute missing days and label the result the same as measured data. Carry a coverage field in the payload (days with data out of days in the window) and gate on it: below your threshold, skip the model call entirely and render a deterministic not-enough-data state, which is cheaper and cannot hallucinate. When you do generate, send absent metrics as an explicit unknown with a reason rather than omitting the key, and tell the model to mention the gap. Users find a partial picture that admits it is partial more trustworthy than confident prose built on three nights of data.
Can the model interpret a user's HRV or recovery trend for them?
It can describe it. Be careful about letting it explain it. Describing a below-baseline week and suggesting a lighter training block is general wellness framing; suggesting the user may be ill, or naming a condition, is a disease-adjacent inference with a recommended action, and that is a different kind of claim about your product's intended purpose. Also set a minimum effect size in code before the feature says anything at all, since wearable metrics carry real measurement error and narrating noise as a trend is false precision no matter who wrote the sentence. None of this is medical advice, and a disclaimer is not a liability shield.

Keep reading

Engineering guidance, last reviewed July 27, 2026. This is not medical, nutritional, or legal advice. Language models produce fluent, confident output that can still be wrong — and when the output is exercise or nutrition guidance, being wrong has consequences. Whatever your app tells a user is your responsibility, not the model vendor’s. Model behaviour, pricing, and platform AI policies change often; verify against current official documentation, and design for a qualified human in the loop wherever the advice could cause harm.

← All ai features · by AIFitnessAPI