Turtle Fit API

JSON API for Turtle Fit accounts, exercises, routines, and workouts.

OpenAPI JSON

GET /api/account

Get the current account

Bearer authentication required

Responses

200 Successful response

application/json

Response example

{
  "id": "0aef8e0b-3782-41b9-96dd-fd792f3f45e2",
  "name": "Alex",
  "email": "alex@example.com",
  "keepAlive": true,
  "restSeconds": 120,
  "setSeconds": 120,
  "healthkitAtMs": null
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

PATCH /api/account

Update the current account

Bearer authentication required

Request example

{
  "name": "Alex",
  "restSeconds": 90
}

Responses

200 Successful response

application/json

Response example

{
  "id": "0aef8e0b-3782-41b9-96dd-fd792f3f45e2",
  "name": "Alex",
  "email": "alex@example.com",
  "keepAlive": true,
  "restSeconds": 120,
  "setSeconds": 120,
  "healthkitAtMs": null
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "email": [
        "Invalid email address"
      ]
    }
  }
}

GET /api/areas

List exercise areas

Bearer authentication required

Responses

200 Successful response

application/json

Response example

[
  {
    "name": "core",
    "title": "core",
    "workouts": 3,
    "lastAtMs": 1705314600000
  }
]

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

GET /api/areas/{name}

List workouts for an exercise area

Bearer authentication required

Responses

200 Successful response

application/json

Response example

[
  {
    "id": "5d3862d1-6a1d-42db-a975-423307276a18",
    "createdAtMs": 1705314600000,
    "lastAtMs": 1705314600000,
    "summary": "12 Suitcase Carries"
  }
]

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

POST /api/auth/login

Log in

Public endpoint

Request example

{
  "email": "alex@example.com",
  "password": "correct-horse-battery-staple"
}

Responses

200 Successful response

application/json

Response example

{
  "token": "session-token",
  "user": {
    "id": "0aef8e0b-3782-41b9-96dd-fd792f3f45e2",
    "name": "Alex",
    "email": "alex@example.com",
    "keepAlive": true,
    "restSeconds": 120,
    "setSeconds": 120,
    "healthkitAtMs": null
  }
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "email": [
        "Invalid email address"
      ]
    }
  }
}

POST /api/auth/signup

Sign up

Public endpoint

Request example

{
  "email": "alex@example.com",
  "password": "correct-horse-battery-staple"
}

Responses

200 Successful response

application/json

Response example

{
  "token": "session-token",
  "user": {
    "id": "0aef8e0b-3782-41b9-96dd-fd792f3f45e2",
    "name": "Alex",
    "email": "alex@example.com",
    "keepAlive": true,
    "restSeconds": 120,
    "setSeconds": 120,
    "healthkitAtMs": null
  }
}

201 Created

application/json

Response example

{
  "token": "session-token",
  "user": {
    "id": "0aef8e0b-3782-41b9-96dd-fd792f3f45e2",
    "name": "Alex",
    "email": "alex@example.com",
    "keepAlive": true,
    "restSeconds": 120,
    "setSeconds": 120,
    "healthkitAtMs": null
  }
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "email": [
        "Invalid email address"
      ]
    }
  }
}

GET /api/exercises

List exercises

Bearer authentication required

Query parameters

Responses

200 Successful response

application/json

Response example

[
  {
    "id": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
    "name": "Suitcase Carry",
    "count": "rep",
    "area": "full body",
    "split": "other",
    "videoUrl": null,
    "favorite": true,
    "equipment": [
      {
        "id": "7ab8fca7-03e4-43ec-a011-66154bfa5348",
        "name": "Dumbbell"
      }
    ]
  }
]

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

POST /api/exercises

Create a custom exercise

Bearer authentication required

Request example

{
  "name": "Suitcase Carry",
  "count": "rep",
  "area": "full body",
  "split": "other",
  "videoUrl": null
}

Responses

201 Created

application/json

Response example

{
  "id": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
  "name": "Suitcase Carry",
  "count": "rep",
  "area": "full body",
  "split": "other",
  "videoUrl": null,
  "favorite": true,
  "equipment": [
    {
      "id": "7ab8fca7-03e4-43ec-a011-66154bfa5348",
      "name": "Dumbbell"
    }
  ]
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "email": [
        "Invalid email address"
      ]
    }
  }
}

GET /api/equipment

List equipment

Bearer authentication required

Responses

200 Successful response

application/json

Response example

[
  {
    "id": "7ab8fca7-03e4-43ec-a011-66154bfa5348",
    "name": "Dumbbell"
  }
]

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

POST /api/exercises/import

Import custom exercises in bulk

Bearer authentication required

Request example

{
  "exercises": [
    {
      "name": "Suitcase Carry",
      "count": "rep",
      "area": "full body",
      "split": "other",
      "videoUrl": null
    },
    {
      "name": "Farmer Carry",
      "count": "rep",
      "area": "full body",
      "split": "other",
      "videoUrl": null
    }
  ]
}

Responses

200 Successful response

application/json

Response example

{
  "exercises": [
    {
      "status": "existing",
      "exercise": {
        "id": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
        "name": "Suitcase Carry",
        "count": "rep",
        "area": "full body",
        "split": "other",
        "videoUrl": null,
        "favorite": true,
        "equipment": [
          {
            "id": "7ab8fca7-03e4-43ec-a011-66154bfa5348",
            "name": "Dumbbell"
          }
        ]
      }
    }
  ]
}

201 Created

application/json

Response example

{
  "exercises": [
    {
      "status": "created",
      "exercise": {
        "id": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
        "name": "Suitcase Carry",
        "count": "rep",
        "area": "full body",
        "split": "other",
        "videoUrl": null,
        "favorite": true,
        "equipment": [
          {
            "id": "7ab8fca7-03e4-43ec-a011-66154bfa5348",
            "name": "Dumbbell"
          }
        ]
      }
    }
  ]
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "workouts": [
        "Too small"
      ]
    },
    "issues": [
      {
        "path": [
          "workouts",
          0,
          "activities",
          0,
          "number"
        ],
        "message": "Too small: expected number to be >=0"
      }
    ]
  }
}

PUT /api/exercises/{id}/favorite

Favorite an exercise

Bearer authentication required

Responses

200 Successful response

application/json

Response example

{
  "favorite": true
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

404 Exercise not found

application/json

Response example

{
  "error": "exercise not found"
}

DELETE /api/exercises/{id}/favorite

Unfavorite an exercise

Bearer authentication required

Responses

200 Successful response

application/json

Response example

{
  "favorite": false
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

404 Exercise not found

application/json

Response example

{
  "error": "exercise not found"
}

GET /api/health

Check service health

Public endpoint

Responses

200 Successful response

application/json

Response example

{
  "ok": true,
  "service": "fit-server"
}

GET /api/history/exercises/{id}

Get exercise history

Bearer authentication required

Responses

200 Successful response

application/json

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

404 Exercise not found

application/json

Response example

{
  "error": "exercise not found"
}

GET /api/gyms

List the current user's gyms

Bearer authentication required

Responses

200 Successful response

application/json

Response example

[
  {
    "id": "4f75bb3e-4626-47de-866f-8904b76ad789",
    "name": "Downtown YMCA"
  }
]

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

POST /api/gyms

Create a gym

Bearer authentication required

Request example

{
  "name": "Downtown YMCA"
}

Responses

201 Created

application/json

Response example

{
  "id": "4f75bb3e-4626-47de-866f-8904b76ad789",
  "name": "Downtown YMCA"
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "email": [
        "Invalid email address"
      ]
    }
  }
}

DELETE /api/gyms/{id}

Delete a gym

Bearer authentication required

Responses

204 Deleted

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

404 Gym not found

application/json

Response example

{
  "error": "gym not found"
}

GET /api/leaders

List leaderboard entries

Bearer authentication required

Responses

200 Successful response

application/json

Response example

[
  {
    "user": {
      "name": "Alex",
      "isViewer": true
    },
    "score": 10,
    "workouts": 1
  }
]

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

GET /api/routines

List routines

Bearer authentication required

Query parameters

Responses

200 Successful response

application/json

Response example

[
  {
    "id": "9d285ce4-ce45-4e09-b549-76e78772f23d",
    "createdAtMs": 1705314600000,
    "name": "Strength",
    "sets": [
      {
        "id": "81e6586a-4d48-44e6-9d6d-61e882b3c4c3",
        "items": [
          {
            "id": "052dc29a-3849-48db-8935-f0880c75b4e7",
            "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
            "quantities": [
              {
                "number": 12,
                "weight": 20,
                "units": "lbs"
              }
            ]
          }
        ]
      }
    ]
  }
]

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

POST /api/routines

Create a routine

Bearer authentication required

Request example

{
  "name": "Strength",
  "sets": [
    {
      "items": [
        {
          "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
          "quantities": [
            {
              "number": 12,
              "weight": 20,
              "units": "lbs"
            }
          ]
        }
      ]
    }
  ]
}

Responses

201 Created

application/json

Response example

{
  "id": "9d285ce4-ce45-4e09-b549-76e78772f23d",
  "createdAtMs": 1705314600000,
  "name": "Strength",
  "sets": [
    {
      "id": "81e6586a-4d48-44e6-9d6d-61e882b3c4c3",
      "items": [
        {
          "id": "052dc29a-3849-48db-8935-f0880c75b4e7",
          "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
          "quantities": [
            {
              "number": 12,
              "weight": 20,
              "units": "lbs"
            }
          ]
        }
      ]
    }
  ]
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "email": [
        "Invalid email address"
      ]
    }
  }
}

PUT /api/routines/{id}

Replace a routine

Bearer authentication required

Request example

{
  "name": "Strength",
  "sets": []
}

Responses

200 Successful response

application/json

Response example

{
  "id": "9d285ce4-ce45-4e09-b549-76e78772f23d",
  "createdAtMs": 1705314600000,
  "name": "Strength",
  "sets": [
    {
      "id": "81e6586a-4d48-44e6-9d6d-61e882b3c4c3",
      "items": [
        {
          "id": "052dc29a-3849-48db-8935-f0880c75b4e7",
          "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
          "quantities": [
            {
              "number": 12,
              "weight": 20,
              "units": "lbs"
            }
          ]
        }
      ]
    }
  ]
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

404 Routine not found

application/json

Response example

{
  "error": "routine not found"
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "email": [
        "Invalid email address"
      ]
    }
  }
}

DELETE /api/routines/{id}

Delete a routine

Bearer authentication required

Responses

204 Deleted

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

404 Routine not found

application/json

Response example

{
  "error": "routine not found"
}

GET /api/workouts

List workouts

Bearer authentication required

Responses

200 Successful response

application/json

Response example

[
  {
    "id": "5d3862d1-6a1d-42db-a975-423307276a18",
    "routineId": null,
    "summary": "12 Suitcase Carries",
    "createdAtMs": 1705314600000,
    "lastAtMs": 1705314600000,
    "healthkitUuid": null,
    "activities": [
      {
        "id": "8feaa928-4a3f-4550-954f-10d53b5080cf",
        "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
        "number": 12,
        "weight": 20,
        "units": "lbs",
        "completedAt": "2024-01-15T10:30:00.000Z"
      }
    ]
  }
]

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

POST /api/workouts

Create a workout

Bearer authentication required

Request example

{
  "createdAt": "2024-01-15T10:30:00.000Z",
  "activities": [
    {
      "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
      "number": 12,
      "weight": 20,
      "units": "lbs",
      "completedAt": "2024-01-15T10:30:00.000Z"
    }
  ]
}

Responses

201 Created

application/json

Response example

{
  "id": "5d3862d1-6a1d-42db-a975-423307276a18",
  "routineId": null,
  "summary": "12 Suitcase Carries",
  "createdAtMs": 1705314600000,
  "lastAtMs": 1705314600000,
  "healthkitUuid": null,
  "activities": [
    {
      "id": "8feaa928-4a3f-4550-954f-10d53b5080cf",
      "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
      "number": 12,
      "weight": 20,
      "units": "lbs",
      "completedAt": "2024-01-15T10:30:00.000Z"
    }
  ]
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "workouts": [
        "Too small"
      ]
    },
    "issues": [
      {
        "path": [
          "workouts",
          0,
          "activities",
          0,
          "number"
        ],
        "message": "Too small: expected number to be >=0"
      }
    ]
  }
}

POST /api/workouts/import

Import historical workouts in bulk

Bearer authentication required

Request example

{
  "workouts": [
    {
      "createdAt": "2024-01-15T10:30:00.000Z",
      "activities": [
        {
          "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
          "number": 12,
          "weight": 20,
          "units": "lbs",
          "completedAt": "2024-01-15T10:30:00.000Z"
        }
      ]
    },
    {
      "createdAt": "2024-01-14T09:00:00.000Z",
      "activities": [
        {
          "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
          "number": 10,
          "weight": 20,
          "units": "lbs",
          "completedAt": "2024-01-14T09:00:00.000Z"
        }
      ]
    }
  ]
}

Responses

201 Created

application/json

Response example

{
  "workouts": [
    {
      "id": "5d3862d1-6a1d-42db-a975-423307276a18",
      "routineId": null,
      "summary": "12 Suitcase Carries",
      "createdAtMs": 1705314600000,
      "lastAtMs": 1705314600000,
      "healthkitUuid": null,
      "activities": [
        {
          "id": "8feaa928-4a3f-4550-954f-10d53b5080cf",
          "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
          "number": 12,
          "weight": 20,
          "units": "lbs",
          "completedAt": "2024-01-15T10:30:00.000Z"
        }
      ]
    }
  ]
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "workouts": [
        "Too small"
      ]
    },
    "issues": [
      {
        "path": [
          "workouts",
          0,
          "activities",
          0,
          "number"
        ],
        "message": "Too small: expected number to be >=0"
      }
    ]
  }
}

GET /api/workouts/{id}

Get a workout

Bearer authentication required

Responses

200 Successful response

application/json

Response example

{
  "id": "5d3862d1-6a1d-42db-a975-423307276a18",
  "routineId": null,
  "summary": "12 Suitcase Carries",
  "createdAtMs": 1705314600000,
  "lastAtMs": 1705314600000,
  "healthkitUuid": null,
  "activities": [
    {
      "id": "8feaa928-4a3f-4550-954f-10d53b5080cf",
      "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
      "number": 12,
      "weight": 20,
      "units": "lbs",
      "completedAt": "2024-01-15T10:30:00.000Z"
    }
  ]
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

404 Workout not found

application/json

Response example

{
  "error": "workout not found"
}

PATCH /api/workouts/{id}

Update a workout

Bearer authentication required

Request example

{
  "healthkitUuid": "B891C1D4-44D0-4D78-A669-0997C92A4C32"
}

Responses

200 Successful response

application/json

Response example

{
  "id": "5d3862d1-6a1d-42db-a975-423307276a18",
  "routineId": null,
  "summary": "12 Suitcase Carries",
  "createdAtMs": 1705314600000,
  "lastAtMs": 1705314600000,
  "healthkitUuid": null,
  "activities": [
    {
      "id": "8feaa928-4a3f-4550-954f-10d53b5080cf",
      "exerciseId": "1e5df6a2-6867-42af-a5a0-9802ffce7d86",
      "number": 12,
      "weight": 20,
      "units": "lbs",
      "completedAt": "2024-01-15T10:30:00.000Z"
    }
  ]
}

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

404 Workout not found

application/json

Response example

{
  "error": "workout not found"
}

422 Validation failed

application/json

Response example

{
  "errors": {
    "formErrors": [],
    "fieldErrors": {
      "email": [
        "Invalid email address"
      ]
    }
  }
}

DELETE /api/workouts/{id}

Delete a workout

Bearer authentication required

Responses

204 Deleted

401 Authentication required

application/json

Response example

{
  "error": "authentication required"
}

404 Workout not found

application/json

Response example

{
  "error": "workout not found"
}

GET /api/openapi.json

Get the OpenAPI document

Public endpoint

Responses

200 OpenAPI 3.1 JSON document

application/json

Response example

{
  "openapi": "3.1.0",
  "info": {
    "title": "Turtle Fit API",
    "version": "1.0.0"
  },
  "paths": {}
}

GET /api/docs

Get the HTML API reference

Public endpoint

Responses

200 HTML API reference

text/html

Response example

"<!doctype html><html lang=\"en\"><head><title>Turtle Fit API</title></head><body>…</body></html>"