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
}
]
}
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.