Bulk summaries and bookings
Use the bulk read operations for imports, reporting, and periodic reconciliation. They return the same public objects as the corresponding single-resource operations, in pages that can be resumed safely.
All six operations are read-only, require a Club API Key with Read or
ReadWrite access, and use a default count of 100 with a maximum of 500.
Choose an operation
Section titled “Choose an operation”| Need | API reference |
|---|---|
| Engagement for every current Athlete | GET /Athletes/EngagementSummaries |
| Effective membership for every current Athlete | GET /Athletes/MembershipSummaries |
| Attendance for every current Athlete | GET /Athletes/AttendanceSummaries |
| All saved bookings in the club | GET /Classes/Bookings |
| Saved bookings for one Class | GET /Classes/{classId}/Bookings |
| Saved bookings for one User | GET /Athletes/{userId}/Bookings |
Keep the path casing exactly as shown.
Read Athlete engagement in bulk
Section titled “Read Athlete engagement in bulk”GET /Athletes/EngagementSummaries returns one
ClubAthleteEngagementSummaryDTO for every current, non-demo Athlete in the
selected club. Athletes without activity are included with zero totals.
Each item contains:
- the Athlete’s current local year and month;
- current-month and lifetime activity totals;
- individual badge awards earned so far in the current local month;
- current Uptivo and selected-brand levels.
Totals and awards include the Athlete’s activity outside the selected club. Because the month is evaluated in the Athlete’s timezone, two Athletes can be in different local months at the same instant.
CurrentLevel is the effective level selected by BrandLevelSource: None
uses the Uptivo level; Club or All uses the selected brand level (zero when
unassigned). MonthToDatePoints and MonthlyPointsGoal are ready to compare:
source Club uses UPPs earned in any club (ClubPoints); the other sources use
all personal UPPs (Points). The Uptivo goal is 2,000; a brand ranking uses its
configured goal. A zero goal disables goal-based nudges.
MonthlyPointsResetAtUtc is the next month boundary in the Athlete’s timezone,
converted to UTC with the offset at that boundary. For a rank nudge, compare
the goal with MonthToDatePoints and the reset instant with the current time.
Previous level is provided by Athlete.LevelChanged.PreviousLevel; retain
that event value because the summary does not reconstruct historical ranks.
Start a complete scan with fromId=1 and paginate by UserId:
GET /Athletes/EngagementSummaries?fromId=1&count=500GET /Athletes/EngagementSummaries?fromId=1&count=500&cursor=<NextCursor>This is the same representation returned by
GET /Athletes/{userId}/EngagementSummary, so the same mapping can be used
for individual and bulk reads.
Read effective membership states
Section titled “Read effective membership states”Use GET /Athletes/{userId}/MembershipSummary for one Athlete, or
GET /Athletes/MembershipSummaries
for all current, non-demo Athletes in the selected club. Both use
ClubAthleteMembershipSummaryDTO and require Read or ReadWrite access.
The single-resource operation returns 404 for an absent Athlete, an Athlete
outside the selected club, or a demo User. An Athlete without assignments
receives Status: "None" and an empty Memberships array.
The server evaluates all of the Athlete’s assignments in this club at request time, in this order:
- Active: at least one non-disabled assignment within its inclusive
From/Tointerval, with available or unlimited tickets, and no active suspension. - Paused: no active assignment, but at least one non-disabled assignment
with available or unlimited tickets and an active suspension, even if the
original
Tohas passed. The assignment’s end can be extended on reactivation. - Pending: no active or paused assignment, but at least one non-disabled
assignment with a future
Fromand available or unlimited tickets. - Otherwise inspect the most recently started assignment, ordering by
Fromdescending and breaking ties byUserClassSubscriptionIddescending: Canceled if disabled; Expired otherwise, because its end has passed or tickets are exhausted. - None if there are no assignments.
MaxActivities=0 means unlimited. A finite-ticket assignment with
AvailableActivities <= 0 is exhausted regardless of its dates and cannot
contribute to Active, Paused, or Pending. Another usable assignment can still
determine the effective state.
A suspension is active from Memberships[].SuspendedAtUtc until, but excluding,
ReactivationDate; an absent reactivation means an open-ended suspension.
A future suspension is scheduled and does not pause the Athlete yet.
| Response field | Meaning |
|---|---|
CalculatedAtUtc |
UTC evaluation instant, not a state-transition timestamp. |
ActiveFrom |
Earliest From among currently active assignments, or null. |
ActiveUntil |
Latest To among currently active assignments, or null. It does not promise uninterrupted future coverage. |
NextPendingStart |
Earliest From among usable future assignments, even when the Athlete is Active or Paused, or null. |
MostRecentEnd |
To of the most recently started assignment, or null. It is not the ticket exhaustion or cancellation timestamp. |
Memberships contains all assignments in this club, including disabled and
exhausted ones. Items include UserClassSubscriptionId, ClassSubscriptionId,
ClassSubscriptionPlanId, SubscriptionName, From, To, Disabled,
ticket counters, SuspendedAtUtc, and ReactivationDate. They are ordered by
start descending, then assignment ID descending. Use SubscriptionName for
display and the IDs to link to the Subscription and plan. The effective status
uses all assignments, rather than choosing one plan.
The summary does not provide a cancellation timestamp. Neither To nor
CalculatedAtUtc is one. You can retain when your integration first observes
Canceled, but that is an observed transition rather than its exact date.
Start a complete forward scan by UserId:
GET /Athletes/MembershipSummaries?fromId=1&count=100GET /Athletes/MembershipSummaries?fromId=1&count=100&cursor=<NextCursor>The state can change when dates pass without a write or webhook. Combine events with periodic reconciliation, including rescanning existing Athlete IDs. Recheck the summary before acting on a previously stored state.
Read Athlete attendance in bulk
Section titled “Read Athlete attendance in bulk”GET /Athletes/AttendanceSummaries returns one ClubAttendanceSummaryDTO for
every current, non-demo Athlete, including Athletes with no check-ins.
Pagination is by Athlete UserId. Both the paginated list and
GET /Athletes/{userId}/AttendanceSummary calculate the two check-in windows
at request time from days; they accept no from/to Class date filters.
GET /Athletes/AttendanceSummaries?fromId=1&count=500GET /Athletes/AttendanceSummaries?fromId=1&count=500&cursor=<NextCursor>Check-in fields for CRM automation
Section titled “Check-in fields for CRM automation”| Field | Meaning |
|---|---|
CalculatedAtUtc |
UTC evaluation instant used for both windows. |
FirstCheckIn / LastCheckIn |
First/last qualifying Class Begin across all saved history in this club, or null. |
LifetimeCheckInCount |
All qualifying saved bookings in this club. |
WindowDays |
Value of days: elapsed UTC days per configurable window, default 30, range 1..366. |
RollingCheckInCount |
Class Begin in [evaluation minus WindowDays days, evaluation]. |
PriorCheckInCount |
Class Begin in [evaluation minus twice WindowDays days, evaluation minus WindowDays days). |
A qualifying check-in is a confirmed Participant booking with NoShow=false
whose Class has started. Published Club, PT and Services Classes are
included. Ongoing Classes qualify; future and queued bookings do not.
Windows use elapsed UTC days and do not overlap; lifetime history is not
limited to 366 days.
Dates are Class Begin values, because a physical check-in timestamp is not
stored. Clubs that create bookings with NoShow=false will count them once
the Class starts. Deleted bookings and attendance corrections update these
metrics on subsequent reads.
Use LifetimeCheckInCount > 0 with membership Active for onboarding.
Use RollingCheckInCount with your chosen threshold for at-risk campaigns.
Both operations accept days (integer, default 30, minimum 1, maximum 366).
Invalid values return 400. With days=7, RollingCheckInCount covers the last
7 days and PriorCheckInCount covers the 7 days immediately before them.
The response echoes the size in WindowDays. First/last check-in and the
lifetime count are unaffected by days. Omitting it selects two consecutive
30-day windows.
GET /Athletes/300001/AttendanceSummary?days=7GET /Athletes/AttendanceSummaries?days=7&count=100GET /Athletes/AttendanceSummaries?days=7&count=100&cursor=<NextCursor>Retain the original endpoint and query parameters when continuing, replacing
only cursor and optionally changing count. Each page evaluates metrics at
its own CalculatedAtUtc; cursors represent Athlete UserId positions.
The previous interval-summary contract used explicit Class date filters. Its
from/to parameters and interval DTO fields have been removed. Discard cursors obtained
under that contract and restart the Athlete scan.
Read saved bookings
Section titled “Read saved bookings”The three booking views return ClubClassParticipationDTO, the same detailed
representation used by the single-booking operation. It includes booking
state, attendance mode, Class and trainer information, costs, and associated
Subscription data.
All views include confirmed and queued bookings for published Club, PT,
and Services Classes. Demo users are excluded.
| View | Population |
|---|---|
/Classes/Bookings |
Every visible saved booking in the selected club. |
/Classes/{classId}/Bookings |
Bookings for one visible published Class; an unknown or unavailable Class returns 404. |
/Athletes/{userId}/Bookings |
Saved bookings for one User, including bookings retained after that User stops being an Athlete. An unknown User or no matches returns an empty page. |
The optional from and to parameters filter the Class Begin, not booking
creation or correction time. from is inclusive and to is exclusive.
GET /Classes/Bookings?from=2026-09-01T00:00:00Z&to=2026-10-01T00:00:00Z&count=100GET /Classes/Bookings?from=2026-09-01T00:00:00Z&to=2026-10-01T00:00:00Z&count=100&cursor=<NextCursor>
GET /Classes/700001/Bookings?count=100GET /Classes/700001/Bookings?count=100&cursor=<NextCursor>
GET /Athletes/300001/Bookings?from=2026-09-01T00:00:00Z&count=100Bookings are ordered by Class start, then ClassId and UserId. The cursor
preserves the complete position, so all bookings remain reachable even when a
single Class has more participants than the page size.
Resume after an interruption
Section titled “Resume after an interruption”Store the endpoint, original query parameters, direction, and cursor together. Save the checkpoint only after processing the complete page successfully.
Use your HTTP library’s query builder when restoring a request:
const params = new URLSearchParams({ from: '2026-09-01T00:00:00Z', to: '2026-10-01T00:00:00Z', count: '100',});
const checkpoint = await loadCheckpoint('club-bookings-september');if (checkpoint) params.set('cursor', checkpoint);
const response = await fetch( `https://api-gw.uptivo.fit/club/Classes/Bookings?${params}`, { headers },);Do not parse or modify the token. Keep every original filter unchanged; only
cursor and, optionally, count may differ between requests. A cursor belongs
to its club, endpoint, resource identifier, and filters.
Load the latest bookings
Section titled “Load the latest bookings”Supply only an upper time boundary to receive the latest Class starts first:
GET /Classes/Bookings?to=2026-10-01T00:00:00Z&count=100Use the first page’s PrevCursor to continue towards older bookings:
GET /Classes/Bookings?to=2026-10-01T00:00:00Z&count=100&cursor=<first-page-PrevCursor>Keep the first page’s NextCursor as the forward position within that fixed
window:
GET /Classes/Bookings?to=2026-10-01T00:00:00Z&count=100&cursor=<first-page-NextCursor>The fixed to remains part of the selection and does not include Classes that
start after it. Start a new request with a new interval when the selection must
change.
Existing participant list
Section titled “Existing participant list”GET /Classes/{classId}/Participants remains a complete, non-paginated array.
Its items use ClubClassParticipationDTO and retain the existing fields,
including BookActorId and BookActorName when available. Existing clients do
not need to migrate, but the paginated /Classes/{classId}/Bookings operation
is preferable when a Class can contain many bookings.
GET /Classes/Participants/Recent remains a bounded sample ordered by booking
creation time. It is not a replacement for a complete booking scan.
Reconcile mutable data
Section titled “Reconcile mutable data”Cursors enumerate the rows visible during each request; they are not a change feed or a frozen snapshot. Engagement, membership, and attendance summaries can change without creating a new Athlete ID. Booking corrections and deletions do not create a new booking position.
Use cursor scans for imports, consume the related webhooks for prompt changes,
and periodically rescan the relevant dataset or time interval. Store summary
rows by UserId and booking rows by (ClassId, UserId), using idempotent
upserts.
See Pagination and synchronization for the shared continuation, checkpoint, retry, and reconciliation rules.