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.
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
List activities in a date range. Returns { activities, total, limit, offset }.
Query parameters (all optional)
fromISO dateStart of the range (e.g.
2026-03-01).toISO dateEnd of the range.
sportTypestringFilter by type:
run,bike,swim,hike,yoga,nordicski,strength,other.completedbooleantrueorfalse.includePairedbooleantrueto include planned activities that have a paired completed activity (hidden by default).limitnumberDefault 50, max 200.
offsetnumberPagination offset. Default 0.
GET /activities?from=2026-03-01&to=2026-03-31
Get a single activity by ID. Returns { activity }.
Create an activity. Returns { activity } with status 201.
Required fields
dateISO datee.g.
2026-03-28or2026-03-28T08:00:00Z.titlestringShort workout title.
sportTypestringOne of
run,bike,swim,hike,yoga,nordicski,strength,other.
Optional fields
descriptionstringdistancenumberMeters.
durationnumberSeconds.
speednumberMeters per second.
heartRatenumberBeats per minute.
powernumberWatts.
loadnumberTraining stress score.
elevationGainnumberMeters.
completedbooleantargetstringOne of
distance,time,pace,steps.stepsarrayStructured workout steps (see below). Set target to
stepswhen using this.
Update an activity. Send only the fields you want to change — same fields as create, all optional. Returns { activity }.
Delete an activity. Returns { deleted: true }.
Create up to 100 activities in one request. Returns { activities } with status 201.
{ "activities": [{ date, title, sportType, ... }, ...] }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 up to 100 activities by ID. Returns { deleted }.
{ "ids": ["id1", "id2", ...] }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 token404— activity not found429— rate limit exceeded
Tips for LLMs
- Always include a date range when listing activities to avoid fetching everything.
- Use
PATCHto 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
sourcestrava,garmin, orwahoowere imported from external services. - The
completedfield 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 — useincludePaired=trueto 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
targetbased on the workout goal: “5k run” →distance, “20min ride” →time, “5k in 20min” →pace, structured intervals →steps. - Only include
stepsfor 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
stepsfor strength training — use thedescriptionfield instead. loadis 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 = 25 TSS.
Example response
[ { "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.