Skip to content

Sessions API

Create, manage, and track workout sessions.

Base URL

http://localhost:3000/api/sessions

All endpoints require authentication.


Create Session

Start and log a new workout session.

Endpoint: POST /sessions

Headers:

Authorization: Bearer {token}
Content-Type: application/json

Request Body:

json
{
  "sessionName": "Push Day A",
  "routineId": "routine-uuid",
  "startTime": "2026-02-23T09:00:00.000Z",
  "endTime": "2026-02-23T10:15:00.000Z",
  "labels": ["Chest", "Shoulders"],
  "exercises": [
    {
      "exerciseId": "exercise-uuid",
      "isTimeBased": false,
      "sets": [
        { "weight": 100, "reps": 10, "isHardSet": true },
        { "weight": 100, "reps": 9, "isHardSet": true },
        { "weight": 100, "reps": 8, "isHardSet": true }
      ]
    },
    {
      "exerciseId": "plank-uuid",
      "isTimeBased": true,
      "sets": [
        { "durationSec": 60, "isHardSet": true },
        { "durationSec": 45, "isHardSet": true }
      ]
    }
  ]
}

Fields:

  • sessionName (required): Name for the session
  • routineId (optional): Link session to a routine template
  • startTime (optional): ISO timestamp — defaults to now
  • endTime (optional): ISO timestamp
  • labels (optional): Array of session labels (e.g. "Chest", "Back", "Legs")
  • exercises (optional): Array of exercise entries
    • exerciseId: ID of the global exercise
    • isTimeBased (optional): true for duration-based exercises (e.g. Plank, Wall Sit) — defaults to false
    • sets[].weight: Weight used in kg — ignored / stored as 0 for time-based exercises
    • sets[].reps: Reps completed — ignored / stored as 0 for time-based exercises
    • sets[].durationSec: Duration in seconds — required for time-based exercises, null for rep-based
    • sets[].isHardSet (optional): Whether it is a working set — defaults to true

Response: 201 Created — full session object with nested exercises and sets.


List Sessions

Retrieve the current user's workout history.

Endpoint: GET /sessions

Headers:

Authorization: Bearer {token}

Query Parameters:

ParamTypeDefaultDescription
pagenumber1Page number
limitnumber20Results per page
filterstringTime filter: today, week, or month

Response: 200 OK

json
{
  "data": [
    {
      "id": 1,
      "sessionName": "Push Day A",
      "startTime": "2026-02-23T09:00:00.000Z",
      "endTime": "2026-02-23T10:15:00.000Z",
      "labels": ["Chest"],
      "exercises": [
        {
          "exerciseId": "exercise-uuid",
          "isTimeBased": false,
          "sets": [
            {
              "weight": 100,
              "reps": 10,
              "durationSec": null,
              "completed": true,
              "isHardSet": true
            }
          ],
          "exercise": {
            "id": "exercise-uuid",
            "name": "Barbell Bench Press",
            "category": "Chest"
          }
        },
        {
          "exerciseId": "plank-uuid",
          "isTimeBased": true,
          "sets": [
            {
              "weight": 0,
              "reps": 0,
              "durationSec": 60,
              "completed": true,
              "isHardSet": true
            }
          ],
          "exercise": {
            "id": "plank-uuid",
            "name": "Plank",
            "category": "Core"
          }
        }
      ],
      "stats": {
        "totalVolume": 9250,
        "duration": 75,
        "exerciseCount": 4,
        "totalSets": 12
      }
    }
  ],
  "pagination": {
    "total": 42,
    "page": 1,
    "limit": 20,
    "totalPages": 3
  }
}

Note: stats.totalVolume is calculated as weight × reps summed across all sets. Time-based exercises (isTimeBased: true) contribute 0 to volume since they have no meaningful weight × reps value.


Get Session Details

Get a single session with all exercises and sets.

Endpoint: GET /sessions/:id

Headers:

Authorization: Bearer {token}

Response: 200 OK — full session object including nested exercises[].sets and exercises[].exercise (global exercise metadata).

Errors:

  • 400 - Invalid session ID
  • 404 - Session not found

Update Session

Replace the full state of an existing session (used to sync a workout in progress or edit after the fact).

Endpoint: PUT /sessions/:id

Headers:

Authorization: Bearer {token}
Content-Type: application/json

Request Body:

json
{
  "sessionName": "Push Day A",
  "endTime": "2026-02-23T10:15:00.000Z",
  "labels": ["Chest", "Triceps"],
  "exercises": [
    {
      "exerciseId": "exercise-uuid",
      "isTimeBased": false,
      "sets": [{ "weight": 105, "reps": 8, "isHardSet": true }]
    },
    {
      "exerciseId": "plank-uuid",
      "isTimeBased": true,
      "sets": [{ "durationSec": 60, "isHardSet": true }]
    }
  ]
}

All fields are optional. When exercises is provided the existing exercise entries are fully replaced.

Response: 200 OK — updated session object.

Errors:

  • 404 - Session not found or not owned by user

Delete Session

Permanently remove a session and all its exercise entries and sets.

Endpoint: DELETE /sessions/:id

Headers:

Authorization: Bearer {token}

Response: 200 OK

json
{ "message": "Session deleted successfully" }

Errors:

  • 404 - Session not found or not owned by user

Calendar Stats

Get per-day workout counts, total volume, and total duration for use in calendar/heatmap views.

Endpoint: GET /sessions/stats/calendar

Headers:

Authorization: Bearer {token}

Query Parameters:

ParamTypeDefaultDescription
daysnumber30How many days back to include

Response: 200 OK

json
[
  {
    "date": "2026-02-23",
    "workouts": 1,
    "totalVolume": 9250,
    "duration": 75
  }
]

Example Usage

javascript
// Create a session with mixed rep-based and time-based exercises
const session = await fetch("http://localhost:3000/api/sessions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    sessionName: "Leg Day",
    startTime: new Date().toISOString(),
    labels: ["Legs", "Core"],
    exercises: [
      {
        exerciseId: "squat-id",
        isTimeBased: false,
        sets: [
          { weight: 140, reps: 5, isHardSet: true },
          { weight: 140, reps: 5, isHardSet: true },
          { weight: 140, reps: 5, isHardSet: true },
        ],
      },
      {
        exerciseId: "plank-id",
        isTimeBased: true,
        sets: [
          { durationSec: 60, isHardSet: true },
          { durationSec: 45, isHardSet: true },
        ],
      },
    ],
  }),
}).then((r) => r.json());

// List recent sessions (last week, page 1)
const { data, pagination } = await fetch(
  "http://localhost:3000/api/sessions?filter=week&page=1&limit=20",
  { headers: { Authorization: `Bearer ${token}` } },
).then((r) => r.json());

// Get session details
const details = await fetch(
  `http://localhost:3000/api/sessions/${session.id}`,
  {
    headers: { Authorization: `Bearer ${token}` },
  },
).then((r) => r.json());

// Update session (sync full state)
await fetch(`http://localhost:3000/api/sessions/${session.id}`, {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    endTime: new Date().toISOString(),
    exercises: updatedExercises,
  }),
});

// Delete a session
await fetch(`http://localhost:3000/api/sessions/${session.id}`, {
  method: "DELETE",
  headers: { Authorization: `Bearer ${token}` },
});

Built with precision for measurable improvement