Routine and workout documents
Routines and logged workouts can be read and written as small JSON documents: exercises by name, weights in a stated unit, sets as short text. gymgym routine show <name> --json and gymgym workout show <id> --json print them; gymgym routine apply and gymgym workout apply take them back from a file or from stdin (-). A document with an id replaces that routine or workout; without one, a new one is created. The same conversion lives in @gymgym/sdk/documents for other clients.
Exercises can be written as a catalog name (barbell bench press), an everyday name (bench press, RDL), a custom exercise name, or an id; gymgym exercises <words> finds them. Invalid documents are rejected with the path of the first problem, for example exercises[2].reps: a range goes from low to high, and nothing is saved.
Routine document
Section titled “Routine document”| Field | Type | Required | Meaning |
|---|---|---|---|
id |
string | Existing routine to replace; omit to create a new routine | |
name |
string | yes | |
icon |
string | dumbbell, barbell, bench, pullup, squat, trophy … | |
progression |
“linear” | “greyskull” | “double” | “time” | “none” | How targets grow; default linear | |
unit |
“kg” | “lb” | Unit of every weight in the document (default: the user’s unit) | |
deload |
boolean | A planned deload: opens on these targets, never feeds progression | |
exercises |
array | yes |
Each entry of exercises:
| Field | Type | Required | Meaning |
|---|---|---|---|
exercise |
string | yes | Catalog or custom exercise name, everyday name (“bench press”, “RDL”) or id |
sets |
integer | yes | |
reps |
integer or string | Reps, or a range “8-12” | |
time |
integer or string | Hold or cardio duration instead of reps: 45, “45s”, “1:30” | |
weight |
number or null | Starting weight | |
rest |
integer or string | Rest after each set (max 900 s) | |
superset |
string or null | Exercises sharing a letter alternate as a superset | |
progression |
“linear” | “greyskull” | “double” | “time” | “none” | null | Overrides the routine’s rule | |
warmups |
integer | Warm-up sets ramped toward the work weight | |
drops |
integer | Drop sets after the last work set | |
bursts |
integer | Rest-pause bursts after the last work set |
{ "name": "Upper A", "progression": "double", "unit": "kg", "exercises": [ { "exercise": "bench press", "sets": 4, "reps": "6-10", "weight": 80, "rest": "2:30" }, { "exercise": "pull-up", "sets": 3, "reps": "6-10", "superset": "A" }, { "exercise": "dumbbell lateral raise", "sets": 3, "reps": "12-15", "superset": "A" }, { "exercise": "weighted front plank", "sets": 2, "time": "45s" } ]}Workout document
Section titled “Workout document”| Field | Type | Required | Meaning |
|---|---|---|---|
id |
string | Existing workout to replace; omit to log a new one | |
name |
string | ||
date |
string | yes | |
start |
string | Start time, ISO 8601 with offset (default noon UTC on the date) | |
minutes |
integer | Duration | |
bodyweight |
number or null | ||
unit |
“kg” | “lb” | Unit of every weight in the document (default: the user’s unit) | |
effort |
“rir” | “rpe” | Scale of “@” effort ratings in the sets (default: the user’s scale, else RPE) | |
routine |
string or null | Id of the routine followed | |
notes |
string or null | Free-text notes about the session | |
skipProgression |
boolean | Exclude the entire session from progression | |
exercises |
array | yes |
Each entry of exercises:
| Field | Type | Required | Meaning |
|---|---|---|---|
exercise |
string | yes | |
superset |
string or null | ||
note |
string or null | Today’s note on this exercise | |
pinned |
boolean | Show the note again next time | |
skipProgression |
boolean | Leave this exercise out of progression | |
sets |
array | yes |
A set is text: 80x5 (weight × reps), x12 (reps only), bw x 12 (no load), 45s or 1:30 (holds and cardio), each optionally followed by @8 for an effort rating on the scale chosen in Settings. A planned set that was not done is { "set": "80x5", "done": false }.
{ "name": "Evening push", "date": "2026-10-03", "start": "2026-10-03T19:00:00+03:00", "minutes": 50, "unit": "kg", "exercises": [ { "exercise": "bench press", "sets": [ "80x8", "80x7@8", { "set": "80x6", "done": false } ] }, { "exercise": "weighted front plank", "sets": [ "45s", "1:00" ] } ]}JSON Schemas
Section titled “JSON Schemas”For validation in editors and agent frameworks (also printed by gymgym routine schema and gymgym workout schema):
Routine document schema
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "gymgym routine document", "type": "object", "required": [ "name", "exercises" ], "additionalProperties": false, "properties": { "id": { "type": "string", "description": "Existing routine to replace; omit to create a new routine" }, "name": { "type": "string", "minLength": 1, "maxLength": 80 }, "icon": { "type": "string", "description": "dumbbell, barbell, bench, pullup, squat, trophy …" }, "progression": { "enum": [ "linear", "greyskull", "double", "time", "none" ], "description": "How targets grow; default linear" }, "unit": { "enum": [ "kg", "lb" ], "description": "Unit of every weight in the document (default: the user's unit)" }, "deload": { "type": "boolean", "description": "A planned deload: opens on these targets, never feeds progression" }, "exercises": { "type": "array", "maxItems": 100, "items": { "type": "object", "required": [ "exercise", "sets" ], "additionalProperties": false, "properties": { "exercise": { "type": "string", "description": "Catalog or custom exercise name, everyday name (\"bench press\", \"RDL\") or id" }, "sets": { "type": "integer", "minimum": 1, "maximum": 20 }, "reps": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 500 }, { "type": "string", "pattern": "^\\d+\\s*[-–]\\s*\\d+$" } ], "description": "Reps, or a range \"8-12\"" }, "time": { "anyOf": [ { "type": "integer", "minimum": 0 }, { "type": "string", "pattern": "^(\\d+|\\d+:\\d{1,2}|(\\d+m)?\\s*(\\d+s)?)$" } ], "description": "Hold or cardio duration instead of reps: 45, \"45s\", \"1:30\"" }, "weight": { "type": [ "number", "null" ], "minimum": 0, "description": "Starting weight" }, "rest": { "anyOf": [ { "type": "integer", "minimum": 0 }, { "type": "string", "pattern": "^(\\d+|\\d+:\\d{1,2}|(\\d+m)?\\s*(\\d+s)?)$" } ], "description": "Rest after each set (max 900 s)" }, "superset": { "type": [ "string", "null" ], "description": "Exercises sharing a letter alternate as a superset" }, "progression": { "enum": [ "linear", "greyskull", "double", "time", "none", null ], "description": "Overrides the routine's rule" }, "warmups": { "type": "integer", "minimum": 0, "maximum": 5, "description": "Warm-up sets ramped toward the work weight" }, "drops": { "type": "integer", "minimum": 0, "maximum": 5, "description": "Drop sets after the last work set" }, "bursts": { "type": "integer", "minimum": 0, "maximum": 5, "description": "Rest-pause bursts after the last work set" } } } } }}Workout document schema
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "gymgym workout document", "type": "object", "required": [ "date", "exercises" ], "additionalProperties": false, "properties": { "id": { "type": "string", "description": "Existing workout to replace; omit to log a new one" }, "name": { "type": "string", "minLength": 1, "maxLength": 80 }, "date": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" }, "start": { "type": "string", "description": "Start time, ISO 8601 with offset (default noon UTC on the date)" }, "minutes": { "type": "integer", "minimum": 0, "maximum": 1440, "description": "Duration" }, "bodyweight": { "type": [ "number", "null" ], "minimum": 0 }, "unit": { "enum": [ "kg", "lb" ], "description": "Unit of every weight in the document (default: the user's unit)" }, "effort": { "enum": [ "rir", "rpe" ], "description": "Scale of \"@\" effort ratings in the sets (default: the user's scale, else RPE)" }, "routine": { "type": [ "string", "null" ], "description": "Id of the routine followed" }, "notes": { "type": [ "string", "null" ], "maxLength": 2000, "description": "Free-text notes about the session" }, "skipProgression": { "type": "boolean", "description": "Exclude the entire session from progression" }, "exercises": { "type": "array", "items": { "type": "object", "required": [ "exercise", "sets" ], "additionalProperties": false, "properties": { "exercise": { "type": "string" }, "superset": { "type": [ "string", "null" ] }, "note": { "type": [ "string", "null" ], "maxLength": 500, "description": "Today's note on this exercise" }, "pinned": { "type": "boolean", "description": "Show the note again next time" }, "skipProgression": { "type": "boolean", "description": "Leave this exercise out of progression" }, "sets": { "type": "array", "minItems": 1, "items": { "anyOf": [ { "type": "string", "description": "\"80x5\", \"x12\" (reps only), \"bw x 12\", \"45s\", \"1:30\"; append \"@8\" for effort; start with \"w:\" for a warm-up, \"d:\" for a drop set, \"rp:\" for a rest-pause burst" }, { "type": "object", "required": [ "set" ], "additionalProperties": false, "properties": { "set": { "type": "string" }, "done": { "type": "boolean", "description": "false for a planned set that was not done" } } } ] } } } } } }}