Quickstart: Tier 3 (roster-aware)

Tier 3 adds bringing a new athlete onto your roster: redeeming a join code the athlete already generated, or creating the record yourself when no code exists. Conformance requires trying the join code first (ID-02); create is the fallback, never the default. This assumes the Tier 2 quickstart's token is already in hand, minted at Tier 3.

1. Try a join code

The athlete generates their own join code from the ownership surface. Exercise this step with a code your athlete has shared some other way. The code travels in the request body, never the URL (bearer-material handling, RFC 6750 §2.3):

curl -X POST https://xcathletes.org/v1/join-codes/redeem \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "code": "ATHLETE-JOIN-CODE-1234" }'
HTTP/1.1 201 Created

{
  "id": "99887766554433221100ffeeddccbbaa",
  "person_id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
  "state": "pending",
  "training_access": "read",
  "checkins_access": "read"
}

state: "pending" and read-only access are the default for every grant that isn't the person's creating one (GR-07); the athlete accepts it, and may make you the writer, from their own ownership surface.

2. Fall back to create, only if no code exists

Creating requires the age-floor attestation (FLOOR-01) and hands the birth date over for hashing only; the platform discards the date itself immediately and never returns it (ENT-04):

curl -X POST https://xcathletes.org/v1/people \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "given_name": "Riley",
    "family_name": "Sundahl",
    "claim_email": "family@example.com",
    "birth_date": "2009-04-12",
    "attestation": { "floor": "high_school" }
  }'
HTTP/1.1 201 Created

{
  "id": "1a2b3c4d5e6f70819203a4b5c6d7e8f9",
  "given_name": "Riley",
  "family_name": "Sundahl",
  "grant": {
    "id": "99887766554433221100ffeeddccbbaa",
    "state": "provisional",
    "training_access": "write",
    "checkins_access": "write"
  }
}

The creating team's own grant defaults to write, unlike a redeemed one, and starts provisional until the athlete claims the record.

Verified outcome

Either path ends in a 201 carrying a grant. GET /v1/token afterward shows person_count one higher, confirming the roster now reaches this athlete.