Athlete events
Athlete events describe membership and profile changes, level transitions, and
challenge badges for an athlete visible to the club selected by the webhook
subscription. Membership lifecycle events use an Athletes/{userId} subject
and place the athlete representation in data. Level and badge events use the
specialized payloads documented below.
| Event type | When it is sent | Typical action |
|---|---|---|
Athlete.Created |
A new user is created in the club or an existing user joins it. | Insert or upsert the club athlete. |
Athlete.Updated |
Athlete profile or club-specific state changes. | Update the local athlete if the event is not stale. |
Athlete.LevelChanged |
The athlete’s Uptivo or applicable brand level changes. | Update the corresponding level using the effective period. |
Athlete.Deleted |
The user leaves the club or the club relationship is deleted. | Remove the athlete from this club’s local scope. |
Badge.Awarded |
The athlete earns a badge from a club or brand challenge. | Insert the award idempotently using AchievedBadgeId. |
The user may still exist in Uptivo after Athlete.Deleted; the event concerns
membership in the selected club, not necessarily global account deletion.
Athlete.Created
Section titled “Athlete.Created”The data object uses the same athlete shape documented by the
Athletes guide. This shortened example highlights the
identity and lifecycle fields; consumers must tolerate additional properties.
[ { "id": "example-athlete-created-event", "topic": "club-100001-events", "subject": "Athletes/300001", "data": { "UserId": 300001, "Email": "athlete@example.com", "FirstMidName": "Alex", "LastName": "Morgan", "Nickname": "Alex", "Gender": "Unspecified", "Weight": 72.5, "Height": 178, "IsDemo": false, "JoinMode": "System", "IsGuest": false, "CreatedAt": "2026-09-07T10:25:00+00:00", "ClientStage": "Lead", "BirthDate": "1990-05-12" }, "eventType": "Athlete.Created", "eventTime": "2026-09-07T10:30:00Z", "dataVersion": "1.0" }]Use data.UserId, rather than parsing the subject, as the public API identifier.
Athlete.Updated
Section titled “Athlete.Updated”Athlete.Updated has the same data shape as Athlete.Created; only the
envelope discriminator and the changed values differ:
[ { "id": "example-athlete-updated-event", "topic": "club-100001-events", "subject": "Athletes/300001", "data": { "UserId": 300001, "Email": "athlete@example.com", "FirstMidName": "Alex", "LastName": "Morgan", "Nickname": "AM" }, "eventType": "Athlete.Updated", "eventTime": "2026-09-07T10:45:00Z", "dataVersion": "1.0" }]The event contains a snapshot, not a JSON patch. Upsert the representation or
call GET /Athletes/{userId} when current state is important. Because delivery
order is not guaranteed, do not apply an older event over a newer resource
solely because it arrived later.
Changing ClientStage or IsGuest through their dedicated Club API
operations sends this existing event type when the saved value actually
changes. Assigning a non-Trial UserSubscription may also move the Athlete
to Client and send Athlete.Updated. A repeated value does not send an
additional state-change event. The payload remains a complete
ClubAthleteDTO, not a separate event schema for each property.
Athlete.Deleted
Section titled “Athlete.Deleted”The deletion event also carries the last available athlete representation:
[ { "id": "example-athlete-deleted-event", "topic": "club-100001-events", "subject": "Athletes/300001", "data": { "UserId": 300001, "Email": "athlete@example.com", "FirstMidName": "Alex", "LastName": "Morgan" }, "eventType": "Athlete.Deleted", "eventTime": "2026-09-07T11:00:00Z", "dataVersion": "1.0" }]Treat data as a tombstone snapshot. A following GET /Athletes/{userId} can
return 404, so deletion processing must not depend on successfully fetching
the resource. Make the local delete idempotent because the event can be
delivered more than once.
Athlete.LevelChanged
Section titled “Athlete.LevelChanged”Athlete.LevelChanged uses the subject Athletes/{userId}/Levels. It is sent
only when the numeric level changes and can represent either a level increase
or decrease. Its payload is not an Athlete snapshot:
[ { "id": "level-300001-0-2026-10-01-3-4", "topic": "club-100001-events", "subject": "Athletes/300001/Levels", "data": { "UserId": 300001, "LevelSource": "Uptivo", "BrandId": null, "BrandName": null, "PreviousLevel": 3, "Level": 4, "Change": "LevelUp", "EffectivePeriod": "2026-10-01" }, "eventType": "Athlete.LevelChanged", "eventTime": "2026-10-01T04:05:00Z", "dataVersion": "1.0" }]| Field | Meaning |
|---|---|
UserId |
Athlete identity used by the Club API. |
LevelSource |
Uptivo for the Uptivo level or Brand for the club brand’s level. |
BrandId, BrandName |
Set for a brand level and null for the Uptivo level. |
PreviousLevel, Level |
Previous and new numeric levels. Zero is a valid value. |
Change |
LevelUp or LevelDown. |
EffectivePeriod |
First day of the month in which the new level takes effect, formatted as YYYY-MM-01. It is not the event delivery date. |
A club receives the level source applicable to its configuration. Use
GET /Athletes/{userId}/EngagementSummary when you need the athlete’s current
Uptivo and selected-brand levels rather than the historical transition.
Badge.Awarded
Section titled “Badge.Awarded”Badge.Awarded uses the subject
Athletes/{userId}/Badges/{achievedBadgeId}. It identifies a saved badge award,
not merely a badge definition:
[ { "id": "badge-700001", "topic": "club-100001-events", "subject": "Athletes/300001/Badges/700001", "data": { "AchievedBadgeId": 700001, "UserId": 300001, "BadgeId": 910001, "BadgeName": "First milestone", "ImageNum": 42, "PictureUri": "https://imgstorage.uptivo.fit/badges/0042.png", "AchievedAt": "2026-09-18T17:35:00+00:00", "TrainingChallengeId": 900001, "ChallengeName": "September Challenge", "ClubId": 100001, "BrandId": null, "AwardType": "ImmediateGoal", "GoalStep": 0, "Rank": null, "RankPercent": null, "Score": null, "RankingCriteria": null, "Participants": null }, "eventType": "Badge.Awarded", "eventTime": "2026-09-18T17:36:00Z", "dataVersion": "1.0" }]AchievedBadgeId identifies this award and is the correct idempotency key for
the business record. AchievedAt is the effective award date and can precede
eventTime. ClubId and BrandId identify the challenge owner; for a brand
challenge, ClubId is not the receiving club.
AwardType is ImmediateGoal, FinalGoal, or Ranking. GoalStep is a
zero-based goal index and is populated only for goal awards. Rank,
RankPercent, Score, RankingCriteria, and Participants apply only to
ranking awards. Consumers must accept null for fields that do not apply.
GET /Athletes/{userId}/EngagementSummary returns awards earned in the
athlete’s current local month. Use
Badge awards reads to import or reconcile visible
award history, or GET /BadgeAwards/{achievedBadgeId} to retrieve one award.
These reads return ClubBadgeAwardDTO, not the event payload:
TrainingChallengeId and AwardType are only available in the event. Retain
the event when those fields or its original snapshot are required.
Recommended consumer flow
Section titled “Recommended consumer flow”- Deduplicate by envelope
id. - Read
data.UserIdand verify that the event is routed to the expected club. - For
CreatedandUpdated, upsert the snapshot or fetch the current athlete. - For
LevelChanged, update the indicated level source and effective period. - For
Badge.Awarded, insert the award idempotently usingAchievedBadgeId. - For
Deleted, remove only the selected club’s athlete relationship. - Persist the event before returning a successful status.
See the common event contract for compatibility and retry rules.