Reference
Import and export programs
TabSet reads and writes plain JSON. A whole multi-week program imports in one tap, and everything you own exports to a file you can keep. This page documents the format exactly, because a typo here produces a file that fails quietly.
Getting a file in and out
Both live in the same menu, and both use the same format, so an export is also a backup.
How to import
- Open the Presets tab on iPhone.
- Tap the + menu in the top right.
- Choose Import from File.
- Pick a
.jsonfile from Files or iCloud Drive.
How to export
- Same + menu, choose Export All.
- You get
tabset-presets.json, holding every preset plus the exercises they use. - That file re-imports through the same route, so it doubles as a backup.
- To share one day only, long-press a preset in the list and choose Share Preset.
Two shapes
The importer tries the program shape first, because its presets
array is the distinguishing key, then falls back to the single preset shape.
Program
Many days over one shared exercise list. This is the shape you want for anything real, and the shape Export All writes.
{
"name": "My 3-Day Split",
"presets": [ /* WorkoutPreset objects */ ],
"exercises": [ /* ExerciseTemplate objects */ ]
}
Single preset
One training day and the exercises it references. This is what Share Preset produces.
{
"preset": { /* one WorkoutPreset */ },
"exercises": [ /* the ExerciseTemplates it references */ ]
}
WorkoutPreset
One training day. Every field except id, name and
exerciseIDs has a sensible default, so a missing field is safe. Being
explicit is clearer.
| Field | Type | Notes |
|---|---|---|
id |
UUID string required |
Uppercase, for example 3F2B1A44-…. Unique within the file. TabSet assigns a fresh id on import, so this only has to be internally consistent. |
name |
string required |
Shown on the watch. Long names scroll. |
symbol |
string optional |
An icon name from the list below. Anything else falls back to the default. |
emoji |
string legacy |
Send "💪". Ignored whenever symbol is set. |
restSeconds |
int optional |
Only used when the preset has no routine. |
sets |
int optional |
Applied to every exercise, unless usesExerciseDefaults is true. |
reps |
int optional |
Same. |
useAutoCount |
bool legacy |
Send false. |
usesExerciseDefaults |
bool optional |
Set this to true for any real program. Each exercise then uses its own sets, reps and rest, so one day can hold 4×5 compounds beside 3×15 isolation. |
exerciseIDs |
array of UUID strings required |
The routine, in order. Every entry must match an id in exercises. |
Valid symbol values
Anything not on this list falls back to
figure.strengthtraining.traditional.
- figure.strengthtraining.traditional
- figure.strengthtraining.functional
- dumbbell.fill
- figure.core.training
- figure.cross.training
- figure.boxing
- figure.highintensity.intervaltraining
- figure.mixed.cardio
- figure.run
- figure.indoor.cycle
- figure.rower
- figure.jumprope
- figure.flexibility
- flame.fill
- bolt.fill
- heart.fill
- target
ExerciseTemplate
A library entry. Omit createdAt: the app fills it in.
| Field | Type | Notes |
|---|---|---|
id |
UUID string required |
Referenced by exerciseIDs. |
name |
string required |
Matching is by name, case-insensitively. See what happens on import. |
muscleGroup |
string optional |
One of the ten values listed below. |
tracksWeight |
bool optional |
false for bodyweight work, so no phantom "0 kg" pollutes your volume stats. |
useAutoCount |
bool legacy |
Send false. |
kind |
string optional |
sets, duration or intervals. |
defaultSets |
int optional |
Sets, or rounds when kind is intervals. |
defaultReps |
int optional |
Ignored by the timed kinds. |
defaultWeight |
number optional |
Starting working weight, in the user's own unit. Never overwrites a weight they have already built up. |
barWeight |
number optional |
The empty bar, for example 20. Only set this for barbell lifts. It unlocks the plate calculator. Omit it for dumbbells, cables and machines, where a plate count would be nonsense. |
defaultRestSeconds |
int optional |
Rest after each set. For intervals this is the recovery leg. |
defaultDurationSeconds |
int optional |
duration only. How long the block runs. |
defaultWorkSeconds |
int optional |
intervals only. The hard leg. |
defaultRounds |
int optional |
intervals only. |
Valid muscleGroup values
- chest
- back
- shoulders
- arms
- legs
- core
- fullBody
- cardio
- mobility
- other
Valid kind values
- sets
- duration
- intervals
What happens on import
Worth understanding before you hand a file to someone else, because the two shapes deliberately behave differently.
Matching
- Exercises are matched by name, case-insensitively, against the library you already have.
- A name you already have is reused, keeping its id, so your history stays attached to it.
- A name you do not have is created. This is how a file adds new exercises to a library.
What a program may change
A program carries a deliberate prescription, so for an exercise you already own it adopts that program's sets, reps, rest and bar weight.
What it never touches is what you have earned: your working
defaultWeight and your history. A program is a plan, not a reset.
A single shared preset is gentler still. It reuses an exercise you already own completely untouched, and only adds the ones you are missing.
- Presets are always appended. Importing the same file twice gives you duplicates. There is no merge-by-name for presets, so delete the old day yourself if you are replacing one.
- An
exerciseIDsentry that matches nothing inexercisesis dropped silently. The preset still imports, just shorter than you meant. This is the single most common mistake, and it is what the validator below is really for.
A complete, valid file
One push day with all three exercise kinds in it. This imports cleanly. Start here and edit.
{
"name": "Sample Program",
"presets": [
{
"id": "11111111-1111-1111-1111-111111111111",
"name": "Day 1 · Push",
"symbol": "figure.strengthtraining.traditional",
"emoji": "💪",
"restSeconds": 90,
"sets": 3,
"reps": 10,
"useAutoCount": false,
"usesExerciseDefaults": true,
"exerciseIDs": [
"22222222-2222-2222-2222-222222222222",
"33333333-3333-3333-3333-333333333333",
"44444444-4444-4444-4444-444444444444"
]
}
],
"exercises": [
{
"id": "22222222-2222-2222-2222-222222222222",
"name": "Bench Press",
"muscleGroup": "chest",
"tracksWeight": true,
"useAutoCount": false,
"kind": "sets",
"defaultSets": 4,
"defaultReps": 5,
"defaultWeight": 60,
"barWeight": 20,
"defaultRestSeconds": 180,
"defaultDurationSeconds": 300,
"defaultWorkSeconds": 30,
"defaultRounds": 8
},
{
"id": "33333333-3333-3333-3333-333333333333",
"name": "HIIT Sprints",
"muscleGroup": "cardio",
"tracksWeight": false,
"useAutoCount": false,
"kind": "intervals",
"defaultSets": 8,
"defaultReps": 10,
"defaultWeight": 0,
"defaultRestSeconds": 60,
"defaultDurationSeconds": 300,
"defaultWorkSeconds": 30,
"defaultRounds": 8
},
{
"id": "44444444-4444-4444-4444-444444444444",
"name": "Treadmill Incline Walk",
"muscleGroup": "cardio",
"tracksWeight": false,
"useAutoCount": false,
"kind": "duration",
"defaultSets": 1,
"defaultReps": 1,
"defaultWeight": 0,
"defaultRestSeconds": 60,
"defaultDurationSeconds": 1200,
"defaultWorkSeconds": 30,
"defaultRounds": 8
}
]
}
Rest-pause, supersets and drop sets
TabSet has no superset object, no drop-set object and no rest-pause object. It has exercises in an order, and a rest value on each one. That turns out to be enough for most of it, and being blunt about where it is not saves you an hour of wondering.
| Technique | How to write it |
|---|---|
| Rest-pause | One exercise with more sets, fewer reps and a short
defaultRestSeconds. Five sets of four at 20 seconds is what a
rest-pause cluster actually feels like on the watch. |
| Superset | The two exercises next to each other in exerciseIDs. Give the
first a rest of 10, just long enough to move, and put the pair's
real rest on the second. |
| Giant set or circuit | Same idea, three or four deep. Every exercise rests 15 except the
last, which carries the rest before the round comes round again. |
| Mechanical drop | Two genuinely different exercises, heavy then light, the first resting
20. Leg Press into Leg Extension, say. |
| Periodisation | One preset per block, all sharing one exercise list, with
usesExerciseDefaults set to false so the preset's own
sets and reps apply to the whole day. |
Three things worth knowing before you try
- A true drop set on a single exercise cannot be written. Listing the same exercise twice gives you two identical prescriptions, because both occurrences share one library entry and pre-fill the same weight. Use two real exercises instead.
- Rest belongs to the exercise, not the
preset. A preset's own
restSecondsis ignored by a routine entirely. That means rest stays the same across every block of a periodised program, which is usually what you want: it is a property of the movement. - Your working weight follows you between presets. After a workout TabSet writes the last weight you logged back onto the exercise, so starting a new block picks up where the previous one finished rather than at whatever the file said.
Ready-made programs
Seven complete files, each one valid and each one showing a different corner of the format. Download one, open it in a text editor, and edit it into your own. They are deliberately ordinary programmes: the point is the file, not the training.
Every exercise name in these files is spelled exactly as
it appears in TabSet's own library. That matters, because import matches
by name, case-insensitively: a file that says Bench Press reuses the
Bench Press you already have, keeping your working weight and your history, instead of
creating a second near-identical entry.
6 days
36 exercises
Push Pull Legs, 6 Day
Six training days over one shared exercise list. The everyday case.
- Six presets in one file
- Per-exercise rest
- Bar weights for the plate calculator
4 days
23 exercises
Upper Lower, 4 Day
Four days, heavier early in the week and higher rep later.
- Rep ranges varying inside one day
- Warm-up as a timed block
3 days
12 exercises
Full Body, 3 Day
Three sessions a week, compound lifts first. A sane starting point.
- A short file
- Long rest on compounds, short on isolation
4 days
13 exercises
Conditioning and HIIT
Interval and timed work only. No sets and reps anywhere in the file.
- Every intervals field
- duration blocks
- tracksWeight false throughout
3 days
16 exercises
No Barbell
Dumbbells, cables, machines and bodyweight. Nothing carries a bar weight.
- No barWeight anywhere, so no plate calculator
- Bodyweight with tracksWeight false
4 days
22 exercises
Intensity Techniques
Rest-pause, supersets, giant sets and a mechanical drop, expressed with nothing but exercise order and rest values.
- Rest-pause as short rest across more sets
- Supersets as a 10 second rest on the first lift
- Giant sets, four in a row
- A mechanical drop across two real exercises
8 days
13 exercises
Eight Week Progression
Four blocks of two weeks, from 12 reps down to 5, over one shared exercise list. The only sample that uses preset-wide targets.
- usesExerciseDefaults false
- Preset-wide sets and reps per block
- Eight presets, one exercise list
Presets are appended on import, never merged, so importing a six day programme adds six days to whatever you already have. Delete the ones you are replacing first.
Validator
Paste a file below and it is checked here in your browser. Nothing is uploaded, because there is nowhere to upload it to.
The validator needs JavaScript. Everything else on this page works without it.
Building a file by hand
- Generate a fresh UUID per exercise and per preset.
On a Mac,
uuidgengives you one. Any unique string in UUID form works, which is why the sample uses2222…style ids. - Set
usesExerciseDefaultstotrue, then give each exercise its owndefaultSets,defaultRepsanddefaultRestSeconds. - Use the exact names from your own library for anything that already exists, so it matches instead of creating a near-duplicate. "Bench press" and "Bench Press" are the same exercise, but "Barbell Bench Press" is a new one.
- Only put
barWeighton lifts loaded with plates. - Save with a
.jsonextension, put it in Files or iCloud Drive, then import it.
Something not behaving as documented? support@tabset.app.