# Check-in submissions

> Read your clients' check-ins with their answers, and review them with a comment.

Source: https://hubfit.com/developers/api/check-in-submissions


A check-in submission is one completed check-in form, with the client's answers. To compare clients' check-ins over a period, use `GET /client-summaries` with `include=checkIns`.

Endpoints:

- `GET /v1/check-in-submissions`: [List check-in submissions](https://hubfit.com/developers/api/check-in-submissions#list-check-in-submissions)
- `GET /v1/check-in-submissions/{submissionId}`: [Get a check-in submission](https://hubfit.com/developers/api/check-in-submissions#get-check-in-submission)
- `POST /v1/check-in-submissions/{submissionId}/review`: [Review a check-in submission](https://hubfit.com/developers/api/check-in-submissions#review-check-in-submission)

## Check-in submission object

### Attributes

- `id` (string): Its unique ID.
- `name` (string): The check-in's title in the app, such as `Weekly check-in`.
- `status` (enum): Known values `awaiting_review`, `reviewed`.
- `client` (object)
  - `id` (string): Its unique ID.
  - `name` (string)
- `submittedAt` (timestamp, nullable): When the submission arrived. The `submittedAfter` and `submittedBefore` filters use this.
- `checkInDate` (date, nullable): The day this check-in is for, as a calendar date. A client can fill in a missed check-in up to 14 days late and mark it for the day it was due; otherwise it's the day it was submitted, in the client's time zone. It's the date coaches see in the app.
- `reviewedAt` (timestamp, nullable)
- `reviewedBy` (object, nullable): The coach who reviewed it. `null` until it's reviewed.
  - `id` (string): Its unique ID.
  - `name` (string, nullable): The coach's name. `null` if their account no longer exists.
- `reviewComment` (string): The review comment sent to the client. Empty if none.
- `answers` (array of objects): One shape per question type, told apart by `type`. `answer` is the answer as text, empty if there's none, as for uploads and signatures. New question types can appear, and one not listed here has `question`, `type` and `answer`.
  - `question` (string)
  - `type` (enum): The kind of question, which decides the other fields. New types can appear. Known values `text`, `number`, `one_to_ten`, `star_rating`, `metric`, `yes_no`, `date`, `multiple_choice`, `progress_photos`, `media`, `signature`.
  - `answer` (string)
  - `value` (number, nullable): The answer as a number, or `null` if it isn't one. Only when `type` is `number`, `one_to_ten`, `star_rating`, `metric`.
  - `unit` (string, nullable): `null` if the metric has no unit. Only when `type` is `metric`.
  - `metricId` (string, nullable): The metric the answer was saved to, for `GET /metrics/{metricId}/entries`. `null` if it wasn't saved, as with a blank answer. Only when `type` is `metric`.
  - `answeredYes` (boolean, nullable): `null` if unanswered. Only when `type` is `yes_no`.
  - `selectedOptions` (array of strings): Only when `type` is `multiple_choice`.
  - `fileCount` (integer): How many files the client uploaded. Only when `type` is `progress_photos`, `media`.
  - `signed` (boolean): Only when `type` is `signature`.

### Example

```json
{
  "id": "6ad6af25540e6a26540ffd23",
  "name": "Weekly check-in",
  "status": "awaiting_review",
  "client": {
    "id": "6ad6af41d3199442d31b2743",
    "name": "Jordan Miller"
  },
  "submittedAt": "2026-10-26T14:32:05.000Z",
  "checkInDate": "2026-10-26",
  "reviewedAt": null,
  "reviewedBy": null,
  "reviewComment": "",
  "answers": [
    {
      "question": "What went well this week?",
      "type": "text",
      "answer": "Hit every session and slept better."
    },
    {
      "question": "How would you rate your energy, from 1 to 10?",
      "type": "one_to_ten",
      "answer": "8",
      "value": 8
    },
    {
      "question": "Your weight this morning",
      "type": "metric",
      "answer": "182.4",
      "value": 182.4,
      "unit": "lb",
      "metricId": "6ad6af776298037662967079"
    },
    {
      "question": "Did you hit your protein target every day?",
      "type": "yes_no",
      "answer": "Yes",
      "answeredYes": true
    }
  ]
}
```

## Review result object

### Attributes

- `id` (string): Its unique ID.
- `status` (string): Always `reviewed`.
- `reviewedAt` (timestamp)
- `clientNotified` (boolean): Whether the client was emailed your comment. False without a comment, or when the email failed; the comment is saved and shows in the client's app either way.

### Example

```json
{
  "id": "6ad6af9094a2ea9194a47d8e",
  "status": "reviewed",
  "reviewedAt": "2026-10-26T14:32:05.000Z",
  "clientNotified": true
}
```

## List check-in submissions

`GET https://api.hubfit.com/v1/check-in-submissions`

Check-in submissions, most recently submitted first. By default only those awaiting your review. Requires `clients:read`.

### Query parameters

- `status` (enum): One of `awaiting_review`, `reviewed`, `all`. Default `awaiting_review`.
- `clientId` (string): Only this client's submissions.
- `submittedAfter` (timestamp): Only submissions received after this time, e.g. `2026-10-05T00:00:00Z` (encode a `+` offset as `%2B`). To fetch what's new since your last run, pass the time of that run.
- `submittedBefore` (timestamp): Only submissions received before this time.
- `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 submissions, without their answers.

### Example request

cURL:

```bash
curl -G https://api.hubfit.com/v1/check-in-submissions \
  -H "Authorization: Bearer $HUBFIT_API_KEY" \
  -d status=awaiting_review
```

Node.js:

```javascript
const response = await fetch("https://api.hubfit.com/v1/check-in-submissions?status=awaiting_review", {
  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/check-in-submissions",
    headers={"Authorization": f"Bearer {os.environ['HUBFIT_API_KEY']}"},
    params={
        "status": "awaiting_review",
    },
)

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

### Example response (200 OK)

```json
{
  "data": [
    {
      "id": "6ad6af25540e6a26540ffd23",
      "name": "Weekly check-in",
      "status": "awaiting_review",
      "client": {
        "id": "6ad6af41d3199442d31b2743",
        "name": "Jordan Miller"
      },
      "submittedAt": "2026-10-26T14:32:05.000Z",
      "checkInDate": "2026-10-26",
      "reviewedAt": null,
      "reviewedBy": null
    }
  ],
  "nextCursor": "eyJpZCI6IjZhZDZhZjNmMjljNTk3ZjE1In0",
  "totalCount": 48
}
```

## Get a check-in submission

`GET https://api.hubfit.com/v1/check-in-submissions/{submissionId}`

A submission with its questions and answers. Answers are written by the client. Uploads are counted (`fileCount`) and a signature says whether the client signed (`signed`). None are linked. Requires `clients:read`.

### Path parameters

- `submissionId` (string, required): The submission's `id`.

### Returns

The submission.

### Example request

cURL:

```bash
curl https://api.hubfit.com/v1/check-in-submissions/6ad6af25540e6a26540ffd23 \
  -H "Authorization: Bearer $HUBFIT_API_KEY"
```

Node.js:

```javascript
const response = await fetch("https://api.hubfit.com/v1/check-in-submissions/6ad6af25540e6a26540ffd23", {
  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/check-in-submissions/6ad6af25540e6a26540ffd23",
    headers={"Authorization": f"Bearer {os.environ['HUBFIT_API_KEY']}"},
)

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

### Example response (200 OK)

```json
{
  "data": {
    "id": "6ad6af25540e6a26540ffd23",
    "name": "Weekly check-in",
    "status": "awaiting_review",
    "client": {
      "id": "6ad6af41d3199442d31b2743",
      "name": "Jordan Miller"
    },
    "submittedAt": "2026-10-26T14:32:05.000Z",
    "checkInDate": "2026-10-26",
    "reviewedAt": null,
    "reviewedBy": null,
    "reviewComment": "",
    "answers": [
      {
        "question": "What went well this week?",
        "type": "text",
        "answer": "Hit every session and slept better."
      },
      {
        "question": "How would you rate your energy, from 1 to 10?",
        "type": "one_to_ten",
        "answer": "8",
        "value": 8
      },
      {
        "question": "Your weight this morning",
        "type": "metric",
        "answer": "182.4",
        "value": 182.4,
        "unit": "lb",
        "metricId": "6ad6af776298037662967079"
      },
      {
        "question": "Did you hit your protein target every day?",
        "type": "yes_no",
        "answer": "Yes",
        "answeredYes": true
      }
    ]
  }
}
```

## Review a check-in submission

`POST https://api.hubfit.com/v1/check-in-submissions/{submissionId}/review`

Marks a submission as reviewed. If you include a `comment`, the client receives it by email and push notification, exactly as when you review in the app. A submission can be reviewed once; reviewing it again returns `409 already_reviewed`. An archived client's submissions can't be reviewed (`409 client_archived`). Requires `check-ins:review`.

### Headers

- `Idempotency-Key` (string): Any unique string, such as a UUID, up to 255 characters. Retrying with the same key within 24 hours returns the first response instead of repeating the write. See [Idempotency](https://hubfit.com/developers/idempotency).

### Path parameters

- `submissionId` (string, required): The submission's `id`.

### Body parameters

- `comment` (string): Your review comment, sent to the client. Can't be blank.

### Returns

The submission is now reviewed.

### Example request

cURL:

```bash
curl -X POST https://api.hubfit.com/v1/check-in-submissions/6ad6af25540e6a26540ffd23/review \
  -H "Authorization: Bearer $HUBFIT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "Great week, Jordan. Keep the step count up."
  }'
```

Node.js:

```javascript
const response = await fetch("https://api.hubfit.com/v1/check-in-submissions/6ad6af25540e6a26540ffd23/review", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.HUBFIT_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    comment: "Great week, Jordan. Keep the step count up.",
  }),
});

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

Python:

```python
import os
import uuid
import requests

response = requests.post(
    "https://api.hubfit.com/v1/check-in-submissions/6ad6af25540e6a26540ffd23/review",
    headers={"Authorization": f"Bearer {os.environ['HUBFIT_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
    json={
        "comment": "Great week, Jordan. Keep the step count up.",
    },
)

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

### Example response (200 OK)

```json
{
  "data": {
    "id": "6ad6af9094a2ea9194a47d8e",
    "status": "reviewed",
    "reviewedAt": "2026-10-26T14:32:05.000Z",
    "clientNotified": true
  }
}
```

## Webhook events

- `check_in_submission.created`: A client submitted a check-in. Fetch the submission with `GET /check-in-submissions/{id}`.

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