---
search:
  tags:
    - Workouts
    - POST
seo:
  description: >-
    Parses a freeform description of a workout into a normalized row (activity
    type,… Reference for the POST /api/workouts/describe endpoint in the skcal
    API.
sidebar:
  label: Log a workout from a description
  badge: POST
title: Log a workout from a description
type: openapi-operation
---
Parses a freeform description of a workout into a normalized row (activity type, duration, distance, heart rate, strength exercises) and logs it.

`POST /api/workouts/describe`

**Request body** (`application/json`, required)

The workout description and optional local day.

- `text` (string, required) — The workout in the user's own words.
- `date` (string<date>) — Local day to log against (YYYY-MM-DD); defaults to now.
- `tz` (integer) — Timezone offset in minutes, as from getTimezoneOffset.

Request body example:

```json
{
  "text": "ran 5k easy in 28 minutes, then 3x10 pullups",
  "date": "2026-07-02",
  "tz": 420
}
```

**Responses**

- `200` — The workout was parsed and logged.
- `400` — The request body or parameters failed validation.
- `502` — The upstream model call failed.
- `503` — A required secret (model key or ingest token) is not configured.

Response example, 200:

```json
{
  "id": "9c2d1e4f-7a8b-4c5d-9e0f-1a2b3c4d5e6f",
  "source": "manual",
  "activityType": "run",
  "summary": "5k run + pullups",
  "description": "ran 5k easy, then 3x10 pullups",
  "startedAt": 1783100000000,
  "durationS": 1680,
  "movingDurationS": null,
  "distanceM": 5000,
  "elevationGainM": null,
  "energyKcal": 320,
  "avgHr": 148,
  "maxHr": null,
  "avgPowerW": null,
  "avgCadence": null,
  "exercises": [
    {
      "exercise": "pullups",
      "sets": 3,
      "reps": 10,
      "weightLb": null,
      "durationS": null,
      "distanceM": null,
      "notes": null
    }
  ],
  "createdAt": 1783100001000
}
```
