Skip to content

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.

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.

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=500
GET /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.

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:

  1. Active: at least one non-disabled assignment within its inclusive From/To interval, with available or unlimited tickets, and no active suspension.
  2. Paused: no active assignment, but at least one non-disabled assignment with available or unlimited tickets and an active suspension, even if the original To has passed. The assignment’s end can be extended on reactivation.
  3. Pending: no active or paused assignment, but at least one non-disabled assignment with a future From and available or unlimited tickets.
  4. Otherwise inspect the most recently started assignment, ordering by From descending and breaking ties by UserClassSubscriptionId descending: Canceled if disabled; Expired otherwise, because its end has passed or tickets are exhausted.
  5. 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=100
GET /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.

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=500
GET /Athletes/AttendanceSummaries?fromId=1&count=500&cursor=<NextCursor>
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=7
GET /Athletes/AttendanceSummaries?days=7&count=100
GET /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.

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=100
GET /Classes/Bookings?from=2026-09-01T00:00:00Z&to=2026-10-01T00:00:00Z&count=100&cursor=<NextCursor>
GET /Classes/700001/Bookings?count=100
GET /Classes/700001/Bookings?count=100&cursor=<NextCursor>
GET /Athletes/300001/Bookings?from=2026-09-01T00:00:00Z&count=100

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

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.

Supply only an upper time boundary to receive the latest Class starts first:

GET /Classes/Bookings?to=2026-10-01T00:00:00Z&count=100

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

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.

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.