The .badger file format

A .badger file is how Badger hands a routine, a gym, or a set of machines to someone else. It is a single JSON text file, so you can open one in any text editor and read all of it.

What a file holds

  • A routine file holds a program: its days, exercises, planned sets and rep targets, supersets and tempo, and, where the program has them, progression rules, percentage sets and weekly waves.
  • A gym file holds a gym's equipment: machines, attachments, bars, accessories, dumbbell and kettlebell racks, and plates.
  • A machines file holds just machines.

A file never holds your workout history: no logged workouts, sets or PRs, no training max, and nothing about you. To move your own history to a new phone, use a backup instead (see moving to a new phone). The full list is under what a file does not carry.

Opening a file on your phone

Tap the file wherever it arrived (a message, an email, a download), or open it from your file manager (Files on an iPhone). Badger shows a preview of what the file will add before anything is saved.

Importing only adds. Nothing you already have is changed, and exercises you already have are matched by name and type, so they keep their history.

Where to find ready-made files

Programs has every program built into Badger as a file you can download and open. To have an AI write one for you, the Use your own AI guide has a prompt for it. The rest of this page is the specification, for anyone writing a file by hand, with a tool, or with an AI.

Specification

From here to the end, this page defines what a file may contain and what import does with it. Fields marked required must be present. Any other field may be left out or written as null, and either reads as absent. Unknown fields are ignored.

Keys marked "Needs Badger 1.1.12 or later" are ignored by older versions; see versions and compatibility.

Terms used below

  • Slot: one exercise on one day of a routine, with its planned sets. A slot is a DayExercise.
  • Progression group: the slots of one routine that share a progression_group_key, on any of its days. They progress as one: a single reference weight, failure counter, stage and remembered weight between them. A rule in the group reads sessions from its own day and from the days of the other members that carry a rule. A slot with no key is a group of its own.
  • Training max: the number a set's percentage is taken from. A progression rule moves it, and it never goes on the bar itself.
  • Reference weight: the number a slot's percentage sets are a percentage of. Under a rule whose reference_kind is training_max, it is the training max. Each lifter has their own, and a file never carries it. Slots in one progression group share one. A slot with no reference weight of its own takes another slot's; see percent_reference.
  • Waved day: a day whose sets change by week of a repeating cycle (5/3/1's four weeks) or by stage. Each version of the day's sets is a variant. See Waves.
  • Stage: on a stage-based day (GZCLP, StrongLifts), which of the day's set variants the slot is on, counting from 0. Only next_stage and reset_stage move it.
  • Driver set: the one planned set whose logged reps some triggers read, marked with is_progression_driver.
  • Missed session (a failure): a session of this slot in which at least one logged working set was left incomplete or came in under its planned minimum. A skipped set counts neither way. A session where every logged working set met its minimum is a success. A session with no completed set of the exercise at all is neither, and the rule ignores it.
  • Failure counter: the run of missed sessions in a row. A success resets it to 0, and so does a consecutive_failures_at_least or stage_is_last clause firing. hold lets it move; none undoes this session's change to it.
  • Remembered weight: the slot's weight as it stood while the slot was on its first stage, recorded each time the rule reads a session there. set_from_remembered restarts from it.

The envelope

Every file has the same five top-level fields. Import needs three of them.

FieldTypeMeaning
badger string, required Schema version. Today it is always share/v1. A file with any other value is refused.
app_version string The app version that wrote the file, for example 1.1.1. Informational.
created_at string UTC timestamp in ISO 8601. Informational.
type string, required One of routine, gym, machines. Any other value and the file is rejected as invalid.
payload object, required The contents. Its shape depends on type: a routine payload or a gym or machines payload.

Files Badger writes leave optional fields out rather than writing null.

A routine payload

type: "routine". The payload has four keys. Only routine is required: a missing days, exercises or machines reads as an empty list, though a routine needs days and exercise definitions to be of any use.

KeyTypeContents
routineRoutine, requiredThe routine itself: name, notes, and its settings.
daysarray of DayThe days, in order. Each day carries its own exercises array: that day's slots, in order, with their planned sets.
exercisesarray of ExerciseThe definition of every exercise any day references: its name, type, and category. Slots point into this list by exercise_name.
machinesarray of MachineMachines the slots pin. May be left out. The import preview asks before creating any.

Two arrays are called exercises. The top-level one is an array of Exercise and defines what each exercise is. Each day's exercises is an array of DayExercise: that day's slots, which point at the definitions by name. A slot whose name matches no definition is skipped, and the import preview warns about it.

Routine

The object in the payload's routine key.

FieldTypeMeaning
namestring, requiredThe routine name.
notesstringProgram-level notes.
weight_source string Where starting weights come from: last_performance (default), routine_defaults, or routine_defaults_then_last.
history_scope string Which history the weight lookup reads: any_workout (default) or this_routine.

Day

The objects in days, one per session in the program.

FieldTypeMeaning
namestring, requiredDay name, for example "Push" or "Workout A".
scheduled_days array of integers Weekdays this day is scheduled on: [1, 3, 5] means Monday, Wednesday, Friday. 1 is always Monday and 7 is always Sunday, whatever the app's first-day-of-week setting and wherever the file is opened. Values outside 1 to 7 are dropped on import. Leave the field out for a program you rotate through instead of pinning to weekdays.
default_gym_name string Gym this day defaults to. The gym is created on import if the name is new.
cycle_length integer How many set variants this day cycles through, if it is waved: 4 for a 5/3/1 day (four weeks), 3 for a GZCLP day (three stages). Leave it out for a day that runs the same session every time. When it is present, each set's week_index says which variant the set belongs to. Waves explains what picks the variant. Needs Badger 1.1.12 or later.
exercisesarray of DayExerciseThe day's slots. Left out, the day imports empty.

Exercise

The objects in the payload's top-level exercises: the definitions the slots point at. List only the exercises the routine uses. Import matches each one against the recipient's library first, so most usually resolve to exercises they already have.

FieldTypeMeaning
name string, required Matching is by name, so plain full names travel best. "Barbell Back Squat" lands on the recipient's existing squat; "BB Squat (heavy day)" creates a new exercise.
type string One of weight_reps (the default), reps_only, time_only, distance_only, weight_only, weight_time, weight_distance, reps_time, reps_distance, distance_time. An unrecognized value falls back to weight_reps.
category_name string Category for an exercise that has to be created. Any name is valid: an existing category is reused by name, a new name creates one, and absent reads as "Other". A fresh install has Chest, Back, Shoulders, Biceps, Triceps, Legs, Core, and Cardio. These are not a fixed list, since categories can be renamed or deleted, but using them puts new exercises where most libraries already sort them.

DayExercise

The objects in a Day's exercises array. Each is one slot: one exercise on that day, with its planned sets.

FieldTypeMeaning
exercise_name string, required Must match a name in the payload's exercises list.
exercise_type string, same values as Exercise.type The referenced exercise's type, repeated here. Optional, but include it: it tells a "Plank" you time apart from a "Plank" someone counts reps on. When present it must exactly equal the definition's type, because import matches the slot by name and type together. A mismatch matches nothing, and the slot is skipped and counted in the import preview's warning, like an unmatched name.
rest_override_seconds integer Rest for this slot, when it should differ from the exercise's own default.
default_machine_name string Machine this slot is pinned to, by name. Import looks for it among the recipient's own machines and the payload's machines list, so a machine the recipient already has does not need to be in the file. Include it in machines if the file should also work for someone who lacks it. A name that matches nothing leaves the slot unpinned. Import links the matched machine to the exercise.
superset_key string Slots in the same day sharing a key become a superset, in the order they appear. The key can be anything ("1", "a"); it only has to match between the members. A key held by a single slot is ignored.
superset_name string Display name for the superset. The first member's value wins. Leave it out and the app names the group itself. Group color does not travel: the recipient's own palette assigns it.
tempo string Tempo prescription for the slot, carried word for word, for example "3-1-2-0".
alternatives array of objects Swap options for this slot, in order. Each is { "exercise_name": "...", "exercise_type": "..." }, with exercise_name required, and is matched by the same rules as the slot itself, so each needs a definition in the payload's exercises list. An unmatched alternative is skipped and counted in the import preview's warning.
progression Progression This slot's progression rule: what happens to its weight next session. At most one per slot. Leave the key out for a slot with no rule. Needs Badger 1.1.12 or later.
progression_group_key string Slots anywhere in the routine sharing a key form one progression group and progress as one. StrongLifts groups the squat on Workout A with the squat on Workout B, so three misses in a row count across both days. Only the sessions of members that carry a progression rule move the group; a member with no rule reads the group's number, as Madcow's Monday and Wednesday squats do. Give different lifts, and two tiers of one lift (GZCLP's T1 and T2), different keys.

The key can be anything ("sl-squat", "1"); it only has to match between the members, case included. Surrounding spaces are ignored, and an empty key reads as absent. Import gives each group a new key of its own and keeps who is in it. Leave the key out and the slot's progress is its own. A key no other slot shares also keeps progress separate, but it stops a percentage set on that slot from borrowing a reference weight (see percent_reference), so leave the key out rather than giving a slot one of its own. Needs Badger 1.1.12 or later.
percent_reference object The lift this slot's percent_of_reference sets are a percentage of, when it is a different lift: close-grip bench at 80% of the bench press's training max. Written like an alternative, { "exercise_name": "...", "exercise_type": "..." } with exercise_name required, and matched against the payload's exercises list by the same rules.

It applies while the slot has no reference weight of its own. The slot then takes the reference weight of a slot in this routine that programs the named lift and carries a rule, and looks nowhere else. Leave the key out and the reference weight comes from the slot's progression group if the slot has a progression_group_key, and otherwise from a ruled slot in the routine programming the same exercise.

A name that matches no definition, or that names the slot's own exercise, reads as absent. A lift the routine does not program is kept: the log preview says those sets need a reference weight, and their weight can be typed in. Do not put the two lifts in one progression group to get this, since a group also shares one failure counter and one stage. Needs Badger 1.1.12 or later.
predefined_sets array of PredefinedSet The planned sets. May be left out or empty.

PredefinedSet

The objects in a DayExercise's predefined_sets, one per planned set. Fill in the fields the exercise type uses: weight and reps for a bench press, duration_seconds for a plank, distance for a row. Leave out fields that do not apply.

FieldTypeMeaning
is_warmup boolean, defaults to false Marks a warmup set. Warmup sets do not count toward working volume.
weight number A planned weight, in the unit named by weight_unit. Most shared programs leave it out so each lifter's own weights fill in.
weight_unit string "kg" or "lbs". Weights are stored in the unit they were entered in, never converted. Absent means kg.
reps integer The rep target. With reps_max present it is the bottom of a range instead.
reps_max integer Top of a rep range: reps: 8, reps_max: 10 reads as 8-10. Leave it out for an exact target. Never valid on a warmup; import drops it there.
is_amrap boolean, defaults to false An open-topped target: reps becomes a required minimum with no ceiling, shown as 8+. reps: 1, is_amrap: true is how a set to failure is written. Mutually exclusive with reps_max: if a set carries both, import keeps the open target and drops the range. Never valid on a warmup.
duration_secondsintegerPlanned time, for timed exercises.
distancenumberPlanned distance in kilometers.
percent_of_reference number This set's weight as a percentage of the slot's reference weight: 85.0 is 85%. Badger works out the load from the recipient's own reference weight and snaps it to a weight they can load. Leave it out for an ordinary set with an absolute weight. Import accepts values above 0 and up to 200; anything outside that is read as an ordinary absolute-weight set.

weight is optional beside it. While the percentage applies, Badger ignores the weight but keeps it, so turning the percentage off restores the number. The built-in program files leave the weight out. Needs Badger 1.1.12 or later.
is_progression_driver boolean, defaults to false Marks the driver set: the set whose logged reps the driver_reps_at_least, driver_reps_between, driver_reps_below_plan and cycle_missed_minimum triggers read. At most one per week of the slot's cycle, and never on a warmup. On a waved day each week_index may have its own driver, as 5/3/1's plus set does in weeks 1 to 3. A set with no week_index belongs to every week, so it rules out any other driver in the slot. If a file carries more than that, import keeps the first in array order and clears the rest, and a driver on a warmup is dropped. Needs Badger 1.1.12 or later.
week_index integer Which week of its day's cycle this set belongs to, counting from 0. Leave it out and the set applies to every week: that is how every set on an unwaved day is written, and how a 5/3/1 file writes its accessory work once instead of four times. A value at or above the day's cycle_length is out of range, and the set applies to every week.
target_rpe integer How hard the set should feel, on the RPE scale: 8 is RPE 8, about two reps left in reserve. Whole numbers from 6 to 10. Import rounds a half up (8.5 becomes 9) and moves a value outside 6 to 10 to the nearer end. Mutually exclusive with target_rir: if a set carries both, import keeps target_rpe and drops target_rir. Never valid on a warmup; import drops it there. It is shown on the set as a target, and no progression trigger reads it. Needs Badger 1.1.12 or later.
target_rir integer The same target in reps in reserve: 2 means stop with two reps left. Whole numbers from 0 to 5. Import drops any fraction (2.5 becomes 2) and moves a value outside 0 to 5 to the nearer end. Write target_rpe or target_rir, never both. Never valid on a warmup; import drops it there. Needs Badger 1.1.12 or later.
is_unilateral boolean true plans the set for each arm: one target, reps and optionally reps_max, that applies to the left and to the right. Leave the key out when the set is not marked. It only takes effect on an exercise whose unilateral_mode resolves to "optional", or to "always", where every set is left and right anyway. Needs Badger 1.1.14 or later.

Waves: cycle_length and week_index

A day whose sets change from week to week is written once, not once per week. 5/3/1's four-week cycle is the standard case. Two keys do it, and they only make sense together. Both need Badger 1.1.12 or later.

  • Day's cycle_length says how many weeks the cycle runs for. Absent means the day is not waved.
  • Each PredefinedSet's week_index says which week that set belongs to, counting from 0. Absent means every week, which is how accessory work done all cycle long is written once.

A week_index that matches no week (negative, or at or above the day's cycle_length) is read as every week. The recipient starts the program at its first week: which week the sender is on does not travel.

The week counter belongs to the routine, not the day. It is one number that every waved day in the routine reads. It moves on once every day waved to the same length has been trained since the last move. Stage-based days (below) do not move it.

Each day reads the counter against its own cycle_length, so give every waved day in a routine the same length. cycle_complete fires when the counter wraps back to the first week. If lengths differ, each length moves the shared counter on its own schedule, and cycle_complete wraps at each day's own length.

Stage-based programs (GZCLP, StrongLifts) use the same two keys, with each week_index a stage and cycle_length the number of stages.

A slot whose rule has a next_stage or reset_stage clause, or any clause targeting stage, shows the variant for its current stage. The stage changes only when one of those clauses fires; in GZCLP and StrongLifts that happens when you stall. Every other slot shows the variant for the routine's current week.

A complete routine file

One routine, one day scheduled Monday and Thursday, two exercises. The bench press has a warmup set and three working sets of 8-10; the plank is timed. This is the real example-routine.badger, indented for reading. Files the app writes are minified, and whitespace makes no difference either way.

{
  "badger": "share/v1",
  "app_version": "1.1.11",
  "created_at": "2026-09-13T05:09:30.410810Z",
  "type": "routine",
  "payload": {
    "routine": {
      "name": "Example Routine",
      "notes": "A two-exercise day, kept small enough to read.",
      "weight_source": "last_performance",
      "history_scope": "any_workout"
    },
    "days": [
      {
        "name": "Day 1",
        "scheduled_days": [
          1,
          4
        ],
        "exercises": [
          {
            "exercise_name": "Flat Barbell Bench Press",
            "rest_override_seconds": 180,
            "exercise_type": "weight_reps",
            "predefined_sets": [
              {
                "is_warmup": true,
                "reps": 8
              },
              {
                "is_warmup": false,
                "reps": 8,
                "reps_max": 10
              },
              {
                "is_warmup": false,
                "reps": 8,
                "reps_max": 10
              },
              {
                "is_warmup": false,
                "reps": 8,
                "reps_max": 10
              }
            ]
          },
          {
            "exercise_name": "Plank",
            "exercise_type": "time_only",
            "predefined_sets": [
              {
                "is_warmup": false,
                "duration_seconds": 60
              },
              {
                "is_warmup": false,
                "duration_seconds": 60
              }
            ]
          }
        ]
      }
    ],
    "exercises": [
      {
        "name": "Flat Barbell Bench Press",
        "type": "weight_reps",
        "category_name": "Chest"
      },
      {
        "name": "Plank",
        "type": "time_only",
        "category_name": "Core"
      }
    ],
    "machines": []
  }
}

For files that use progression rules, percentage sets and waves, open the files on the Programs page, such as 531-bbb.badger and gzclp.badger.

Progression rules

A slot's progression rule says what happens to its weight after each session. It is a Progression object holding a list of Clause objects. The example below shows a complete one.

Progression

The object in a DayExercise's progression key. At most one per slot. Needs Badger 1.1.12 or later.

The rule travels; the sender's progress against it does not, including their training max (see what a file does not carry). A training_max key written into a file is an unknown key and is ignored.

FieldTypeMeaning
cadence string, required How often the program means the rule to be looked at: session, week or cycle. Badger stores it and nothing reads it, like scheme below. Badger evaluates the most recent qualifying session whenever the log preview opens. An unrecognized value still drops the whole rule.
reference_kind string, required Which number the rule moves: working_weight (a real weight on the bar) or training_max. A working_weight rule may not carry a clause targeting reference, and a file that has one loses the rule.
clauses array of Clause, required The rule itself, in evaluation order: the first clause whose trigger matches is the one that fires. Any clauses directly after it that state the same trigger with the same numbers, and move a different target, fire with it; that is how one outcome both resets the stage and cuts the weight. Nothing else runs. Must be non-empty and must end with an always clause.
scheme string Where the rule came from, as a label: linear, double_progression, gzclp_t1, gzclp_t2, gzclp_t3, wave_531, nsuns_t1, nsuns_t2, madcow_weekly, custom. Nothing behaves differently because of it; the clauses are the behavior. An unrecognized value reads as custom, and the rule still imports.
note string The author's own line about the rule. Displayed word for word and never parsed, like an accessory's label.

Clause

The objects in clauses. Each one is a trigger and the action it fires. count, reps_min and reps_max belong to the trigger; target, amount, amount_unit and percent belong to the action. The terms missed session, failure counter, stage and remembered weight are defined under Terms used below, at the start of the specification.

FieldTypeMeaning
trigger string, required What makes the clause fire. One of:
  • all_sets_met: every planned working set reached its rep target (at or above the minimum, for a range).
  • all_sets_top_of_range: every ranged working set hit the top of its range. Open (N+) sets are skipped.
  • any_set_missed: at least one planned working set came in under its minimum.
  • driver_reps_at_least: the driver set's logged reps are at least reps_min.
  • driver_reps_between: the driver set's reps fall in [reps_min, reps_max], inclusive at both ends.
  • driver_reps_below_plan: the driver set did not reach the minimum its own plan row asked for. It states no number: the minimum comes from the plan, and on a waved day that is this week's own minimum. Cannot fire when the session has no driver set, or when the driver's plan states no minimum.
  • cycle_missed_minimum: driver_reps_below_plan with a memory. A driver set has come up short at some point in the cycle now running, this session included. Clears when the cycle ends. A rule on a day with no cycle_length can never fire it.
  • consecutive_failures_at_least: the failure counter has reached count, counting this session.
  • stage_is_last: the slot is on its final stage and the session failed.
  • cycle_complete: the routine's cycle has come round since this slot last acted.
  • always: unconditionally. The terminal "otherwise" clause every rule ends with.
Any other value drops the whole rule.
count integer How many qualifying sessions in a row the trigger needs. Leave it out and the trigger fires on a single occurrence. A written 1 behaves the same as an absent key, but Badger prints the written one as "for 1 session".
reps_min / reps_max integer Inclusive rep bounds for driver_reps_at_least and driver_reps_between. With both left out, the trigger does not look at reps at all; they are never defaulted to zero or to the plan's own numbers. Either of those two triggers with no reps_min, or a reps_max below the reps_min, drops the whole rule.
action string, required What happens when it fires. One of:
  • add_absolute: add amount.
  • add_percent: change the weight by percent percent. 5 adds 5%, -10 takes 10% off.
  • hold: the weight stays; counters still move.
  • none: nothing changes at all, counters included.
  • next_stage: move up a stage, weight unchanged.
  • reset_stage: back to the first stage (stage 0), weight unchanged. To reset and change the weight on the same outcome, follow it with a second clause carrying the same trigger and a weight action, as GZCLP does: reset_stage on stage, then add_percent -10 on working_weight.
  • set_from_remembered: the remembered weight plus amount. With nothing remembered, the clause does nothing and says so.
  • halve_increment: halve the increment this rule adds from here on.
Any other value drops the whole rule.
target string, required What the action moves: working_weight, reference (the training max), stage, or increment. Required on every clause, including hold and none. The pairs Badger's own programs use:
  • add_absolute (needs amount), add_percent (needs percent), set_from_remembered (needs amount), hold and none: the weight the rule moves, which is reference in a training_max rule and working_weight otherwise.
  • next_stage and reset_stage: stage.
  • halve_increment: increment, with amount and amount_unit set to the rule's add_absolute step, which is what it halves the first time.
The only pairing that drops a rule is reference in a working_weight rule.
amount number The absolute amount, in the unit named by amount_unit. Required for add_absolute and set_from_remembered; leaving it out on either drops the whole rule. It never falls back to the exercise's own increment, so write the number.
amount_unit string "kg" or "lbs", for amount. Absent means kg.
percent number The percentage for add_percent: -10 means drop 10%. Required for that action and meaningless for any other.
What drops a rule

If a rule breaks any of these, import skips the whole rule and counts it in the import preview's warning. The rest of the file imports normally.

  1. An unrecognized trigger, action, target, cadence or reference_kind.
  2. A driver_reps_at_least or driver_reps_between trigger with no reps_min. (driver_reps_below_plan takes its minimum from the plan.)
  3. A driver_reps_between whose reps_max is below its reps_min.
  4. A missing amount or percent that the action requires.
  5. A clause targeting reference under reference_kind: "working_weight".
  6. No clauses at all, or a clauses array that does not end with an always. An always in the middle leaves every clause after it unable to fire.
  7. A clause that can never fire because an earlier one already covers it, such as consecutive_failures_at_least 2 before the same trigger at 3, or a run of same-trigger clauses that moves one target twice.

Example: a linear rule

Add 2.5 kg after every session in which all working sets hit their target, take 10% off after three missed sessions in a row, and otherwise keep the weight. This is one slot, with three sets of 5:

{
  "exercise_name": "Barbell Back Squat",
  "exercise_type": "weight_reps",
  "progression": {
    "scheme": "linear",
    "cadence": "session",
    "reference_kind": "working_weight",
    "clauses": [
      {
        "trigger": "consecutive_failures_at_least",
        "count": 3,
        "action": "add_percent",
        "target": "working_weight",
        "percent": -10
      },
      {
        "trigger": "all_sets_met",
        "action": "add_absolute",
        "target": "working_weight",
        "amount": 2.5,
        "amount_unit": "kg"
      },
      {
        "trigger": "always",
        "action": "hold",
        "target": "working_weight"
      }
    ]
  },
  "predefined_sets": [
    {
      "is_warmup": false,
      "reps": 5
    },
    {
      "is_warmup": false,
      "reps": 5
    },
    {
      "is_warmup": false,
      "reps": 5
    }
  ]
}

Badger reads the clauses in order and fires the first that matches. After a third missed session in a row, the first clause takes 10% off and resets the failure counter to 0. After a session where every planned working set reached 5 reps, the second clause adds 2.5 kg. Any other session reaches the always clause, and the weight holds while the failure counter still moves.

The rule ends with always, every clause names a target, and add_absolute and add_percent carry the amount and percent they need, so none of the conditions above drops it.

Gym and machines payloads

type: "gym" shares a whole gym: the gym itself, its machines, attachments, bars and accessories, which of each belong to it, its dumbbell and kettlebell racks, and its plates. type: "machines" is the smaller version: one key, machines, an array of Machine, with no gym, bars, accessories, or inventory.

A gym file carries what describes the gym. Notes about a machine travel. Your photo of it, and the setup cues you saved against one exercise on it, stay on your phone.

FieldTypeMeaning
gymGym, requiredA Gym is just { "name": "..." }.
machinesarray of MachineThe same Machine objects a routine payload carries.
attachmentsarray of AttachmentAn Attachment is a name and optional notes.
barsarray of BarNamed bars. Omitted when the gym has none.
accessoriesarray of AccessoryBands, chains, and straps. Omitted when the gym has none.
gym_machinesarray of stringsNames of the machines that belong to this gym.
gym_attachmentsarray of stringsNames of the attachments that belong to this gym.
gym_barsarray of stringsNames of the bars that belong to this gym.
gym_accessoriesarray of stringsNames of the accessories that belong to this gym.
racksarray of RackThe gym's dumbbell and kettlebell racks. Omitted when the gym has none set up.
plate_inventoryarray of PlateInventoryThe plates the gym actually has. Omitted when nobody has set them up.

Equipment is defined in its own arrays, and the four gym_* lists name which of it belongs to this gym, so one machine can belong to several gyms. Files Badger writes list exactly the objects defined beside them.

Leave a membership list out to include everything defined in the file. Writing it as [] means none of them, which leaves the gym with nothing of that kind and is almost never what you want.

Machine

The objects in machines, in a routine payload and a gym or machines payload alike. In a routine payload, importing the machines is a toggle the recipient controls. In a gym or machines payload, machines are always created.

FieldTypeMeaning
namestring, requiredMachine name.
notesstringFree text about the machine, such as "third from the left, seat pin sticks".
uses_attachmentsboolean, defaults to falseWhether the machine takes attachments (bars, handles, ropes).
uses_grip_typesboolean, defaults to falseWhether grip is worth recording on this machine.
supports_bands boolean, defaults to false Whether bands can be attached here. It decides whether a machine exercise offers accessories at all, and then only band-kind ones.
machine_type string "selectorized" (a stack with a pin) or "plate_loaded".
weight_increment number Smallest real step on this machine, in the machine's own unit.
weight_increment2 number Second step size, for a stack whose plates get heavier further up.
increment_threshold number Weight at and above which weight_increment2 applies.
plate_calculator_mode string "dual_loaded" (plates on both sides) or "single_loaded" (one lever arm).
bar_weight number Weight of the bar or empty carriage, in the unit named by bar_weight_unit, so a 45 lb bar travels as 45.
bar_weight_unit string "kg" or "lbs", for bar_weight.
weight_unit string "kg" or "lbs", when this machine is labeled in a different unit from the rest of your logging. Absent means the machine follows the recipient's own logging unit, and weight_increment, weight_increment2 and increment_threshold are read in that unit. Write it whenever the file states those numbers.
logs_weight_per_side boolean What a weight logged on this machine means: true for one side (iso-lateral machines), false for the total. Leave the key out if the machine has no preference; Badger then uses the exercise's own setting, which is not the same as writing false. Only meaningful on a plate-loaded machine; a value on any other kind is ignored.
unilateral_mode string Whether sets logged on this machine are left and right: "off", "optional" or "always". It overrides the exercise's own setting, including "off". Leave the key out if the machine has no preference. Any other value is read as absent. Needs Badger 1.1.14 or later.

Bar

The objects in bars: a named bar, with its own weight.

FieldTypeMeaning
namestring, requiredBar name, for example "Olympic bar" or "Trap bar".
notesstringFree text about the bar.
bar_weight number What the bar weighs empty, in the unit named by bar_weight_unit, so a 45 lb bar travels as 45. Leave it out for a bar nobody has weighed; Badger then falls back to the machine's or the exercise's own bar weight.
bar_weight_unit string "kg" or "lbs", for bar_weight. Absent means kg.

Accessory

The objects in accessories: a band, a chain, a set of suspension straps, or another add-on.

FieldTypeMeaning
namestring, requiredAccessory name, for example "Green band".
kind string One of band, chain, suspension, other. A missing or unrecognized value reads as other.
color integer Packed ARGB, for example 4283215696. Omitted when the accessory has no color set. It travels because people name a band by its color.
label string Free text shown beside the name, for example "rated 25 lbs". Carried word for word and never parsed. Accessories never feed any calculation in Badger, so a number in here stays text.
notesstringFree text about the accessory.

Rack

The objects in racks: the dumbbells or kettlebells a gym owns. List every weight on the rack, since racks do not step evenly (5, 10, 15 up to 50, then a jump). The plus and minus buttons step through this list when you log.

FieldTypeMeaning
equipmentstring, required"dumbbell" or "kettlebell". Any other value is skipped.
unitstring, required"kg" or "lbs", for every weight in this rack.
weights array of objects, required Each one is a weight (number, required) and an optional color (packed ARGB integer), for competition kettlebells, which are identified by color. Leave the color out for an unpainted weight.

One entry per equipment-and-unit pair, so a gym with kilogram kettlebells and pound dumbbells carries two. Nothing is converted between them: 45 in a pound rack is a 45 lb dumbbell.

PlateInventory

The objects in plate_inventory: what the plate calculator has to work with.

FieldTypeMeaning
unitstring, required"kg" or "lbs", for every number in this entry.
bar_weightnumberThe bar the calculator starts from, in the unit above, so 45 in a pound entry is a 45 lb bar. Absent falls back to the ordinary bar for that unit.
plates array of objects, required Each one is a weight (number, required), a color (packed ARGB integer), a quantity (how many are available per side, defaults to 2) and enabled (defaults to true). A plate that exists but is switched off has enabled: false.

One entry per unit, and nothing converts between them. A gym still on Badger's built-in default plates writes no plate_inventory at all.

What import does

  • Nothing is overwritten. Import only adds. An existing exercise, machine, gym, or routine is never edited by a file you open.
  • Exercises match on name and type, ignoring case and surrounding spaces. The same name with a different type is a different exercise. With no match, the exercise is created in the named category, which is created too if it is new.
  • Machines, attachments, bars, accessories, gyms, and categories match on name alone, again ignoring case and spaces.
  • A routine name collision does not merge. Importing a routine whose name you already use gives you "Name (imported)", then "Name (imported) 2", and so on. You keep both.
  • It is one transaction. A file that fails partway through leaves your database exactly as it was.
  • Days, slots, and sets import in array order.
  • A warmup never carries a rep range or an open target. Import drops reps_max and is_amrap from a warmup set. A working set carrying both keeps the open target and loses the range.
  • An unreadable progression rule is skipped whole and counted in the import preview's warning; the rest of the file imports normally. What drops a rule is listed with the clause fields. Per-set flags are repaired instead: extra driver sets are cleared as described under is_progression_driver, a driver on a warmup is cleared, and a week_index or percent_of_reference that cannot mean anything is read as absent. An intensity target is repaired as described under target_rpe.
  • Files over 10 MB are refused without being read.

What a file does not carry

A routine file is the plan, and only the parts of it that work for a different lifter in a different gym.

  • No history. No workouts, no logged sets, no PRs. To move your own history to a new phone, use a backup instead: see moving to a new phone.
  • No notes below the routine level. Routine notes travel; per-day and per-exercise notes do not. In a gym file, notes on a machine, attachment, bar or accessory do travel. The setup cues you save against one exercise on one machine never do.
  • No default attachment, default bar, or default grip on a slot, and no superset group color; the recipient's palette assigns it. (An accessory's color does travel.)
  • No training max, and no progress against a rule. A slot's progression rule travels. The training max it multiplies does not, and neither does any counter behind it: a run of missed sessions, a stage, a cycle position, which session was last evaluated. The recipient sets their own training max, from their own history, the first time they run the program.
  • No per-exercise detail beyond name, type, and category. An exercise that gets created on import starts blank: no instructions, no muscles, no rest default, no increment. Anything you already had keeps everything it already has. That includes an exercise's own Log left and right separately setting, which never travels, like its per-side weight setting. A machine's unilateral_mode does.
  • No photos of machines, attachments, or bars.
  • Nothing about you. No account, no device identifier, no settings, no training notes.

Versions and compatibility

Unknown fields are ignored, so a file written by a newer Badger still opens in an older one, minus whatever that older version has no concept of. If the shape ever has to change in a way that breaks, badger gets a new value and share/v1 keeps being read.

A file you exported today should still import years from now. See also the promise.

  • Optional keys are left out when unused. A file that uses none of the newer keys is byte-identical to one written before they existed, and an older copy of Badger reads it unchanged.
  • 1.1.12 keys. progression, progression_group_key, percent_reference, percent_of_reference, is_progression_driver, cycle_length, week_index, target_rpe and target_rir need Badger 1.1.12 or later. An older version ignores progression and imports the slot without a rule. When a percentage set also carries a weight, an older version that has never heard of percent_of_reference still finds the weight as the target. It reads a set with an intensity target as the same set with no target.
  • is_amrap is omitted when false. Because reps still holds the minimum, an older version reads an open target as an exact one.
  • logs_weight_per_side: older versions ignore the key and lose nothing else.
  • 1.1.14 keys. A machine's unilateral_mode and a set's is_unilateral are omitted when not set. An older version ignores both and imports the routine or machine without them, so the sets read as ordinary both-arms sets.
  • Older values still read. machine_type: "plateLoaded"; the camelCase plate_calculator_mode values; a machine's bar_weight_kg (always kg) in place of bar_weight and bar_weight_unit; and the retired sort_order field, which is ignored.
Verified

Last checked against Badger 1.1.11, schema share/v1. The example above and the files on the Programs page were exported by that build and imported back into it before publishing. The progression, percentage, driver and wave keys were checked against Badger 1.1.12's own import and export code. The published files are regenerated whenever the seeded programs change.