How to: a shared training log
An athlete often trains with more than one program at once: a school team and an outside training group are the common case. Both want the same log. The platform serves this with one writer and first-class readers by default (GR-07): a second write-holding grant for the same scope exists only when the athlete explicitly moves the writer, never automatically. Doubled rows are then expected, not forbidden (MW-04), but the default case below has exactly one writer.
Writer setup
Say the school team is the writer. It holds a Tier 2 token, and the athlete's grant gives it training_access: write (and checkins_access: write if it logs
check-ins too). Every row it writes carries its own origin_team_id (ENT-08) and,
when it sets one, its own external_ref (WR-02):
curl -X POST https://xcathletes.org/v1/training-sessions \
-H "Authorization: Bearer $SCHOOL_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
"date": "2026-08-20",
"modality": [{ "system": "xca", "code": "skiing", "display": "Skiing" }],
"duration_seconds": 3600,
"external_ref": "school-2026-08-20-riley"
}' Reader setup
The training group holds a Tier 1 token. The athlete accepts its grant read-only, the default
for every grant beyond the creating team's own (GR-07). A /v1/ list read carries
no person filter this pass: it reads every row the token reaches, each one still carrying its
own origin_team_id, so the reader labels the source in its own UI and narrows to
one athlete client-side rather than at the query string:
curl -H "Authorization: Bearer $GROUP_TOKEN" \
https://xcathletes.org/v1/training-sessions Echo suppression: one comparison
If the training group also holds write access, deliberately or because the athlete moved the
writer to it later, both sides poll the same change feed and both see rows the other side
wrote, and rows they wrote themselves coming back around. SYNC-07 makes telling those apart
one comparison: every feed entry carries origin_consumer_id, the team that made
the change. When it equals your own team id, the entry is your own write echoing back through
the feed, something you already have locally, and you skip re-applying it. When it doesn't,
the row is genuinely someone else's, and you apply it as usual. No second lookup, no comparing external_ref values against your own store first; the one field decides it.
What checkin_conflict means here
Training sessions tolerate two write grants (MW-04); check-ins do not (ENT-07). Exactly one
check-in exists per person per local calendar date, platform-wide, regardless of who logs it.
If both the school and the training group's own system independently try to log the athlete's
check-in for the same day, whichever POST lands second gets checkin_conflict:
HTTP/1.1 409 Conflict
{
"type": "https://xcathletes.org/docs/errors/checkin_conflict",
"title": "Check-in conflict",
"status": 409,
"detail": "A check-in already exists for this person on 2026-08-20 (MW-05, ENT-07); PATCH the existing check-in instead of creating another."
} The losing side's next move is to GET the existing check-in, which will show the
other team's origin_team_id, and treat it as the athlete's report already
captured rather than retrying its own write blind.