Skip to content

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.

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 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.

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 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 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.

  1. Deduplicate by envelope id.
  2. Read data.UserId and verify that the event is routed to the expected club.
  3. For Created and Updated, upsert the snapshot or fetch the current athlete.
  4. For LevelChanged, update the indicated level source and effective period.
  5. For Badge.Awarded, insert the award idempotently using AchievedBadgeId.
  6. For Deleted, remove only the selected club’s athlete relationship.
  7. Persist the event before returning a successful status.

See the common event contract for compatibility and retry rules.