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

  1. Open the Presets tab on iPhone.
  2. Tap the + menu in the top right.
  3. Choose Import from File.
  4. Pick a .json file from Files or iCloud Drive.

How to export

  1. Same + menu, choose Export All.
  2. You get tabset-presets.json, holding every preset plus the exercises they use.
  3. That file re-imports through the same route, so it doubles as a backup.
  4. 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.

program.json
{
  "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.json
{
  "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.

FieldTypeNotes
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.

FieldTypeNotes
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 exerciseIDs entry that matches nothing in exercises is 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.

tabset-sample-program.json Download
{
  "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.

TechniqueHow 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 restSeconds is 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

push-pull-legs-6-day.json

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

upper-lower-4-day.json

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

full-body-3-day.json

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

conditioning-and-hiit.json

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

no-barbell-machines-only.json

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

intensity-techniques.json

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

eight-week-progression.json

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, uuidgen gives you one. Any unique string in UUID form works, which is why the sample uses 2222… style ids.
  • Set usesExerciseDefaults to true, then give each exercise its own defaultSets, defaultReps and defaultRestSeconds.
  • 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 barWeight on lifts loaded with plates.
  • Save with a .json extension, put it in Files or iCloud Drive, then import it.

Something not behaving as documented? support@tabset.app.