# Completed workouts

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

Source: https://hubfit.com/developers/api/completed-workouts


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

Endpoints:

- `GET /v1/completed-workouts`: [List completed workouts](https://hubfit.com/developers/api/completed-workouts#list-completed-workouts)
- `GET /v1/completed-workouts/{workoutId}`: [Get a completed workout](https://hubfit.com/developers/api/completed-workouts#get-completed-workout)

## Completed workout object

### Attributes

- `id` (string): Its unique ID.
- `client` (object)
  - `id` (string): Its unique ID.
  - `name` (string)
- `name` (string): The workout's name.
- `completedAt` (timestamp)
- `durationSeconds` (integer, nullable): How long the client's workout timer ran. `null` if it wasn't timed.
- `rating` (integer, nullable): How the client rated the workout, from 1 (okay) to 5 (amazing). `null` if they didn't rate it.
- `note` (string): The client's note on the workout. Empty if none.
- `newPersonalRecordCount` (integer): How many personal records the workout set.
- `totals` (object)
  - `setCount` (integer)
  - `reps` (number)
  - `volume` (number): Weight times reps over every set, in `weightUnit`.
- `weightUnit` (enum): 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 values `kg`, `lb`.
- `sections` (array 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.
  - `id` (string): What each of the section's exercises has as `sectionId`.
  - `name` (string)
  - `type` (enum): New section types can appear. Known values `regular`, `circuit`, `amrap`, `interval`.
  - `targetRounds` (integer, nullable): The rounds the coach set for a circuit or interval section. `null` for other sections.
  - `completedRounds` (number, nullable): The rounds the client finished in a circuit, interval or AMRAP section. `null` for regular sections.
  - `timeCapSeconds` (integer, nullable): The time the coach set for an AMRAP section. `null` for other sections.
  - `durationSeconds` (integer, nullable): How long the client took over a circuit, interval or AMRAP section. `null` for regular sections or when it wasn't timed.
- `exercises` (array of objects): Every exercise in the order of the workout, each pointing at its section, if any.
  - `exerciseId` (string, nullable): 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.
  - `name` (string)
  - `sectionId` (string, nullable): The `id` of the section the exercise is in, or `null` if it isn't in one.
  - `supersetId` (string, nullable): Shared by the exercises done together as a superset, or `null` when the exercise wasn't part of one.
  - `isAlternative` (boolean): Whether the client swapped in this exercise as an alternative to the planned one.
  - `note` (string): The client's note on the exercise. Empty if none.
  - `sets` (array 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.
  - `newPersonalRecords` (array 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.
    - `type` (enum): `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 values `max_weight`, `max_volume`, `estimated_one_rep_max`.
    - `value` (number): The new record, in `weightUnit`.
    - `improvementPercent` (number): 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).

### Example

```json
{
  "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
        }
      ]
    }
  ]
}
```

## List completed workouts

`GET https://api.hubfit.com/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

- `clientId` (string): Only this client's workouts.
- `startDate` (date): 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.
- `endDate` (date): Only workouts up to this day, included, as `YYYY-MM-DD`, in your own calendar.
- `limit` (integer): How many results to return, from 1 to 100. Default `25`.
- `cursor` (string): The `nextCursor` from the previous page.

### Returns

A page of completed workouts.

### Example request

cURL:

```bash
curl -G https://api.hubfit.com/v1/completed-workouts \
  -H "Authorization: Bearer $HUBFIT_API_KEY" \
  -d clientId=6ad6af41d3199442d31b2743 \
  -d startDate=2026-10-20
```

Node.js:

```javascript
const response = await fetch("https://api.hubfit.com/v1/completed-workouts?clientId=6ad6af41d3199442d31b2743&startDate=2026-10-20", {
  headers: {
    Authorization: `Bearer ${process.env.HUBFIT_API_KEY}`,
  },
});

const { data, nextCursor } = await response.json();
```

Python:

```python
import os
import requests

response = requests.get(
    "https://api.hubfit.com/v1/completed-workouts",
    headers={"Authorization": f"Bearer {os.environ['HUBFIT_API_KEY']}"},
    params={
        "clientId": "6ad6af41d3199442d31b2743",
        "startDate": "2026-10-20",
    },
)

page = response.json()
data, next_cursor = page["data"], page["nextCursor"]
```

### Example response (200 OK)

```json
{
  "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 a completed workout

`GET https://api.hubfit.com/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

- `workoutId` (string, required): The workout's `id`.

### Returns

The completed workout.

### Example request

cURL:

```bash
curl https://api.hubfit.com/v1/completed-workouts/6ad6af97b154fb96b1536899 \
  -H "Authorization: Bearer $HUBFIT_API_KEY"
```

Node.js:

```javascript
const response = await fetch("https://api.hubfit.com/v1/completed-workouts/6ad6af97b154fb96b1536899", {
  headers: {
    Authorization: `Bearer ${process.env.HUBFIT_API_KEY}`,
  },
});

const { data } = await response.json();
```

Python:

```python
import os
import requests

response = requests.get(
    "https://api.hubfit.com/v1/completed-workouts/6ad6af97b154fb96b1536899",
    headers={"Authorization": f"Bearer {os.environ['HUBFIT_API_KEY']}"},
)

data = response.json()["data"]
```

### Example response (200 OK)

```json
{
  "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
          }
        ]
      }
    ]
  }
}
```

## Webhook events

- `completed_workout.created`: A workout was completed. Sent when a completed workout is saved, whether the client logged it or a coach logged it for them. Edits to it aren't sent. Fetch it with `GET /completed-workouts/{id}`.

An event carries the record's `id` and its `clientId`, not the record. See [Webhooks](https://hubfit.com/developers/webhooks).
