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

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.