Skip to main content

Progress

Completed workouts

The workouts your clients finished, with every exercise's sets and the personal records they set.

A workout a client finished and logged, with each exercise's sets and any personal records it set. To compare clients' workouts over a period, use GET /client-summaries with include=workouts.

A completed workout with every logged set.
In the HubFit appA completed workout with every logged set.

Attributes

  • idstring

    Its unique ID.

  • clientobject
    Show 2 child attributes
    • idstring

      Its unique ID.

    • namestring
  • namestring

    The workout's name.

  • completedAttimestamp
  • durationSecondsintegernullable

    How long the client's workout timer ran. null if it wasn't timed.

  • ratingintegernullable

    How the client rated the workout, from 1 (okay) to 5 (amazing). null if they didn't rate it.

  • notestring

    The client's note on the workout. Empty if none.

  • How many personal records the workout set.

  • totalsobject
    Show 3 child attributes
  • The unit of every weight here, which is the client's unit setting. Weights aren't converted, the way amounts come with their currency. HubFit doesn't record the unit each workout was logged in, so a client who changed their setting may have older workouts logged in the other unit.

    Known valueskglb
  • sectionsarray of objects

    The sections the coach grouped exercises into, such as a warm-up or a circuit. Exercises outside a section aren't in one.

    Show 7 child attributes
    • idstring

      What each of the section's exercises has as sectionId.

    • namestring
    • typeenum

      New section types can appear.

      Known valuesregularcircuitamrapinterval
    • targetRoundsintegernullable

      The rounds the coach set for a circuit or interval section. null for other sections.

    • completedRoundsnumbernullable

      The rounds the client finished in a circuit, interval or AMRAP section. null for regular sections.

    • timeCapSecondsintegernullable

      The time the coach set for an AMRAP section. null for other sections.

    • durationSecondsintegernullable

      How long the client took over a circuit, interval or AMRAP section. null for regular sections or when it wasn't timed.

  • exercisesarray of objects

    Every exercise in the order of the workout, each pointing at its section, if any.

    Show 8 child attributes
    • exerciseIdstringnullable

      The same for every time the client logged this exercise, so it tells you which entries across workouts are the same exercise. null when HubFit has no shared id for it.

    • namestring
    • sectionIdstringnullable

      The id of the section the exercise is in, or null if it isn't in one.

    • supersetIdstringnullable

      Shared by the exercises done together as a superset, or null when the exercise wasn't part of one.

    • Whether the client swapped in this exercise as an alternative to the planned one.

    • notestring

      The client's note on the exercise. Empty if none.

    • setsarray of objects

      One set, with a value for each thing the exercise tracks, or null when the client left it blank. weight is in weightUnit and durationSeconds in seconds; other values are as the client entered them.

    • newPersonalRecordsarray of objects

      The personal records the client set on this exercise in this workout. The first time a client logs an exercise sets none; records start from the next time.

      Show 3 child attributes
      • typeenum

        max_weight for the heaviest set, max_volume for the most weight times reps added up over the exercise's sets in one workout, estimated_one_rep_max for the one-rep max HubFit estimates from a set's weight and reps (the Epley formula). New record types can appear.

        Known valuesmax_weightmax_volumeestimated_one_rep_max
      • valuenumber

        The new record, in weightUnit.

      • How much it beat the previous record by, as a percentage. 100 when the previous record was 0, such as when earlier sessions had no weight entered (HubFit doesn't keep the previous value, so this can't be told apart from doubling it).

Completed workout object
{
  "id": "6ad6af97b154fb96b1536899",
  "client": {
    "id": "6ad6af41d3199442d31b2743",
    "name": "Jordan Miller"
  },
  "name": "Upper body A",
  "completedAt": "2026-10-26T14:32:05.000Z",
  "durationSeconds": 3120,
  "rating": 5,
  "note": "Felt strong. Added 5 lb on bench.",
  "newPersonalRecordCount": 1,
  "totals": {
    "setCount": 18,
    "reps": 142,
    "volume": 18450
  },
  "weightUnit": "lb",
  "sections": [
    {
      "id": "6ad6af11f9669110f964fe0f",
      "name": "Main lifts",
      "type": "regular",
      "targetRounds": 3,
      "completedRounds": 3,
      "timeCapSeconds": null,
      "durationSeconds": 3120
    }
  ],
  "exercises": [
    {
      "exerciseId": "6ad6afb311a359b211a1c6b1",
      "name": "Barbell bench press",
      "sectionId": null,
      "supersetId": null,
      "isAlternative": false,
      "note": "",
      "sets": [
        {
          "reps": 8,
          "weight": 185,
          "rpe": 8
        }
      ],
      "newPersonalRecords": [
        {
          "type": "max_weight",
          "value": 182.4,
          "improvementPercent": 4.2
        }
      ]
    }
  ]
}
GET/v1/completed-workouts

Workouts your clients logged, most recently completed first, without their exercises. Across every client you can reach (team members with the Standard role only see their assigned clients), or one client's with clientId. To pick up new workouts, pass the day before the newest one you've seen as startDate and skip ones you've seen. Workouts are ordered by when the client's app says they finished, so one can arrive after a workout that finished later. Requires clients:read.

Query parameters

  • clientIdstring

    Only this client's workouts.

  • Only workouts from this day, included, as YYYY-MM-DD, in your own calendar (the timezone in your HubFit settings), as the app shows workouts to you.

  • Only workouts up to this day, included, as YYYY-MM-DD, in your own calendar.

  • limitinteger

    How many results to return, from 1 to 100.

    Default 25
  • cursorstring

    The nextCursor from the previous page.

Returns

A page of completed workouts.

GET /v1/completed-workouts
curl -G https://api.hubfit.com/v1/completed-workouts \
  -H "Authorization: Bearer $HUBFIT_API_KEY" \
  -d clientId=6ad6af41d3199442d31b2743 \
  -d startDate=2026-10-20
Response200 OK
{
  "data": [
    {
      "id": "6ad6af97b154fb96b1536899",
      "client": {
        "id": "6ad6af41d3199442d31b2743",
        "name": "Jordan Miller"
      },
      "name": "Upper body A",
      "completedAt": "2026-10-26T14:32:05.000Z",
      "durationSeconds": 3120,
      "rating": 5,
      "note": "Felt strong. Added 5 lb on bench.",
      "newPersonalRecordCount": 1,
      "totals": {
        "setCount": 18,
        "reps": 142,
        "volume": 18450
      },
      "weightUnit": "lb"
    }
  ],
  "nextCursor": "eyJpZCI6IjZhZDZhZjNmMjljNTk3ZjE1In0",
  "totalCount": 48
}
GET/v1/completed-workouts/{workoutId}

One completed workout with every exercise in order, its sets as the client logged them and any personal records, plus the sections the coach grouped exercises into. Requires clients:read.

Path parameters

Returns

The completed workout.

GET /v1/completed-workouts/{workoutId}
curl https://api.hubfit.com/v1/completed-workouts/6ad6af97b154fb96b1536899 \
  -H "Authorization: Bearer $HUBFIT_API_KEY"
Response200 OK
{
  "data": {
    "id": "6ad6af97b154fb96b1536899",
    "client": {
      "id": "6ad6af41d3199442d31b2743",
      "name": "Jordan Miller"
    },
    "name": "Upper body A",
    "completedAt": "2026-10-26T14:32:05.000Z",
    "durationSeconds": 3120,
    "rating": 5,
    "note": "Felt strong. Added 5 lb on bench.",
    "newPersonalRecordCount": 1,
    "totals": {
      "setCount": 18,
      "reps": 142,
      "volume": 18450
    },
    "weightUnit": "lb",
    "sections": [
      {
        "id": "6ad6af11f9669110f964fe0f",
        "name": "Main lifts",
        "type": "regular",
        "targetRounds": 3,
        "completedRounds": 3,
        "timeCapSeconds": null,
        "durationSeconds": 3120
      }
    ],
    "exercises": [
      {
        "exerciseId": "6ad6afb311a359b211a1c6b1",
        "name": "Barbell bench press",
        "sectionId": null,
        "supersetId": null,
        "isAlternative": false,
        "note": "",
        "sets": [
          {
            "reps": 8,
            "weight": 185,
            "rpe": 8
          }
        ],
        "newPersonalRecords": [
          {
            "type": "max_weight",
            "value": 182.4,
            "improvementPercent": 4.2
          }
        ]
      }
    ]
  }
}

Subscribe a webhook endpoint to hear about these as they happen, instead of polling. An event carries the record's id and its clientId, not the record: fetch it with your key. See Webhooks.