---
title: "Get Profile"
method: GET
path: "/harness/profile"
tags: ["harness"]
---

# Get Profile

`GET /harness/profile`

## Request

- Base URL: `https://api.getathly.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.getathly.com/harness/profile`
- Auth: HTTP bearer

## Query parameters

- `athlete_slug` string, required

## Response `200`

Successful Response

- HarnessProfileResponse: The pair's profile as the settings editor reads it: the structured profile and, beside it, the raw onboarding answers the editor reads through to for a field the profile has no value for. Nothing in the browser writes ``onboarding_data``. ``field_copy`` is the sport seed's wording for the fields it relabels (ATH-521) — overrides only; a field not in it keeps the editor's default question.
  - `plan_profile` PlanProfile: Structured planning context for the pair — the successor to the planner's ``PROFILE_KEYS``, and since ATH-460 Phase 1 the pair's *only* profile (the public ``user_athlete_profiles`` table is archived). Every field is optional: it fills in over time (the settings editor, ATH-460's intake interview, or opportunistically from chat), and an absent field is simply not yet known. The column is written only through the ``merge_plan_profile`` RPC (server-side ``jsonb ||``), never through ``save_user``, so two writers touching different fields both land. The value shapes are the editor's: every field is a string. ``training_days`` is the joined label string the editor stores (``"Mon, Wed"``), deliberately — the athlete's own words, not a parsed set. Nothing has ever written another shape, so a row that carries one is a bug to surface, not a legacy to absorb. The sport is not a field: it is fixed by the athlete the pair is enrolled with (the harness sport seed), so there is nothing to ask. A ``sports`` key on an older row is ignored on read. The profile is not a field of :class:`UserProfile`: ``Store.save_user`` writes the user record from an in-memory copy, and a profile riding on that copy could clobber a merge. It is read through ``Store.get_plan_profile`` instead.
    - `sport_position` string, nullable
    - `competitive_level` string, nullable
    - `primary_focus` string, nullable
    - `training_days` string, nullable
    - `training_setup` string, nullable
    - `age` string, nullable
    - `notes` string, nullable
  - `onboarding_data` object
  - `field_copy` object

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/getathly/apis/athly-api.md) - [All operations](https://skmtc.dev/getathly/apis/athly-api/llms.txt) - [OpenAPI document](https://skmtc.dev/getathly/apis/athly-api/revisions/6adfdc258d75?raw)
