Shape API

Shape ships with a REST API so you can plug your training into any tool, AI agent, or automation, or export everything. No lock-in.

The full machine-readable reference is at /llms.txt. Point any LLM or agent at it and it can plan, create, and manage your workouts for you.

Using an MCP-compatible client (Claude, ChatGPT, Raycast)? See the MCP server docs for the one-click OAuth setup.

Authentication

Every request needs your API token in the Authorization header. Tokens start with shape_ and can be created from API access.

Authorization: Bearer shape_...

Base URL

https://shapecalendar.com/api/v1

Endpoints

GET/activities

List activities in a date range. Returns { activities, total, limit, offset }.

Query parameters (all optional)

  • fromISO date

    Start of the range (e.g. 2026-03-01).

  • toISO date

    End of the range.

  • sportTypestring

    Filter by type: run, bike, swim, hike, yoga, nordicski, strength, other.

  • completedboolean

    true or false.

  • includePairedboolean

    true to include planned activities that have a paired completed activity (hidden by default).

  • limitnumber

    Default 50, max 200.

  • offsetnumber

    Pagination offset. Default 0.

GET /activities?from=2026-03-01&to=2026-03-31
GET/activities/:id

Get a single activity by ID. Returns { activity }.

POST/activities

Create an activity. Returns { activity } with status 201.

Required fields

  • dateISO date

    e.g. 2026-03-28 or 2026-03-28T08:00:00Z.

  • titlestring

    Short workout title.

  • sportTypestring

    One of run, bike, swim, hike, yoga, nordicski, strength, other.

Optional fields

  • descriptionstring
  • distancenumber

    Meters.

  • durationnumber

    Seconds.

  • speednumber

    Meters per second.

  • heartRatenumber

    Beats per minute.

  • powernumber

    Watts.

  • loadnumber

    Training stress score.

  • elevationGainnumber

    Meters.

  • completedboolean
  • targetstring

    One of distance, time, pace, steps.

  • stepsarray

    Structured workout steps (see below). Set target to steps when using this.

PATCH/activities/:id

Update an activity. Send only the fields you want to change. Same fields as create, all optional. Returns { activity }.

DELETE/activities/:id

Delete an activity. Returns { deleted: true }.

POST/activities/batch

Create up to 100 activities in one request. Returns { activities } with status 201.

{ "activities": [{ date, title, sportType, ... }, ...] }
PATCH/activities/batch

Update up to 100 activities in one request. Each object must include id plus the fields to change. Returns { updated }.

{ "activities": [{ "id": "id1", "completed": true }, ...] }
DELETE/activities/batch

Delete up to 100 activities by ID. Returns { deleted }.

{ "ids": ["id1", "id2", ...] }
POST/activities/pair

Pair a completed activity with a planned one. The completed row gets plannedActivityId set; the planned row gets completedActivityId set. After pairing, the planned activity is hidden from the default list view. Returns { paired: true, completedActivityId, plannedActivityId }.

{ "completedActivityId": "id1", "plannedActivityId": "id2" }

The completed activity must have completed: true and the planned activity must have completed: false. 400 otherwise. Idempotent when the same pair is sent twice. Fails with 409 if either side is already paired with a different activity. Call /activities/unpair first.

POST/activities/unpair

Remove the pairing between an activity and its partner. Pass either side; both rows are unlinked. If the partner row no longer exists (e.g. it was deleted), only this side is cleared and partnerId is returned as null. Returns { unpaired: true, activityId, partnerId }.

{ "activityId": "id1" }

Activity object

{
  "id": "abc-123",
  "date": "2026-03-28",
  "title": "Easy morning run",
  "description": "Recovery run",
  "sportType": "run",
  "distance": 5000,
  "duration": 1800,
  "speed": 2.78,
  "heartRate": 140,
  "power": null,
  "load": 45,
  "elevationGain": 50,
  "completed": true,
  "target": "distance",
  "source": "strava",
  "externalId": "12345",
  "createdAt": 1711612800000,
  "updatedAt": 1711612800000
}

Structured workout steps

Activities can include structured steps for interval training. Set target to steps and provide a steps array.

Step types

warmup, cooldown, interval, recovery, repeat

End conditions

lap.button, time, distance, iterations, heart.rate, power, open

Target types

no.target, heart.rate, pace, power, speed, cadence

Step object

{
  "stepType": "interval",
  "displayName": "400m fast",
  "description": null,
  "endCondition": "distance",
  "endConditionValue": 400,
  "targetType": "pace",
  "targetValueOne": 210,
  "targetValueTwo": 240,
  "zoneNumber": null,
  "secondaryTargetType": null,
  "secondaryTargetValueOne": null,
  "secondaryTargetValueTwo": null,
  "secondaryZoneNumber": null
}

Repeat group

{
  "stepType": "repeat",
  "numberOfIterations": 5,
  "skipLastRestStep": false,
  "workoutSteps": [
    { "stepType": "interval", "displayName": "400m fast", "endCondition": "distance", "endConditionValue": 400, "targetType": "pace", "targetValueOne": 210, "targetValueTwo": 240 },
    { "stepType": "recovery", "displayName": "90s jog", "endCondition": "time", "endConditionValue": 90, "targetType": "no.target" }
  ]
}

Example: 5×400m interval workout

{
  "date": "2026-03-30",
  "title": "400m Repeats",
  "sportType": "run",
  "distance": 4000,
  "duration": 2400,
  "target": "steps",
  "steps": [
    { "stepType": "warmup", "displayName": "Warmup jog", "endCondition": "time", "endConditionValue": 600, "targetType": "no.target" },
    {
      "stepType": "repeat",
      "numberOfIterations": 5,
      "workoutSteps": [
        { "stepType": "interval", "displayName": "400m fast", "endCondition": "distance", "endConditionValue": 400, "targetType": "pace", "targetValueOne": 210, "targetValueTwo": 240 },
        { "stepType": "recovery", "displayName": "90s jog", "endCondition": "time", "endConditionValue": 90, "targetType": "no.target" }
      ]
    },
    { "stepType": "cooldown", "displayName": "Cooldown jog", "endCondition": "time", "endConditionValue": 600, "targetType": "no.target" }
  ]
}

Sport types

run, bike, swim, hike, yoga, nordicski, strength, other

Errors

Errors always return { "error": "message" }.

  • 400: validation error (bad request body or query params)
  • 401: missing or invalid token
  • 404: activity not found
  • 429: rate limit exceeded

Tips for LLMs

  • Always include a date range when listing activities to avoid fetching everything.
  • Use PATCH to update single fields (e.g. mark completed) rather than sending the full object.
  • Use batch endpoints when creating or deleting multiple activities at once.
  • Distance is always in meters, duration in seconds.
  • Activities with source strava, garmin, or wahoo were imported from external services.
  • The completed field distinguishes planned workouts (false) from completed ones (true).
  • When a planned activity is completed, both exist linked via completedActivityId / plannedActivityId. By default the list endpoint hides the paired planned one. Use includePaired=true to see both.

Creating workouts: conventions

  • Keep titles short. Don’t include distance/duration in the title: “Easy 5k run” → “Easy run”, “bike 100km” → “Bike”.
  • Set target based on the workout goal: “5k run” → distance, “20min ride” → time, “5k in 20min” → pace, structured intervals → steps.
  • Only include steps for workouts with a specific interval structure (e.g. “5×400m at 5k pace with 60s rest”). Don’t create steps for simple descriptions like “5k run” or “20min easy bike ride”.
  • Never include steps for strength training. Use the description field instead.
  • load is Training Stress Score (TSS): (duration_seconds / 3600) × relative_effort² × 100, where relative effort ranges from 0.25 (very easy) to 1.50 (very hard). Example: 30min easy run = (1800/3600) × 0.5² × 100 = 13 TSS.

Example response

GET/api/v1/activities?from=2026-04-15&to=2026-04-21
[
  {
    "id": "act_8hX2",
    "date": "2026-04-18",
    "title": "5×1km at threshold",
    "sportType": "run",
    "distance": 10000,
    "duration": 3600,
    "load": 142,
    "completed": true,
    "steps": []
  }
]

Rate limits

30 requests per minute and 200 per hour. Exceeding either returns a 429. Use batch endpoints to stay under the cap when creating or deleting many activities.