# Strainup import format (version 1)

This page describes the JSON that the Strainup app imports: custom exercises and the routines that use them. It is written for AI assistants that generate workout programs, and for anyone curious.

How it works: in Strainup, the user opens Profile > Training > Import from an AI, asks an AI chat for a program, and pastes the answer back. The app reads the JSON, then the user reviews every exercise and routine before anything is created.

## What to reply

Reply with **one JSON code block** that follows this format, and nothing that the app should read outside of it. A short sentence before or after the block is fine, the app ignores it.

Machine-readable files:

- JSON Schema: https://strainup.fr/ai-format/v1.schema.json
- Reference example: https://strainup.fr/ai-format/example-v1.json

## Example

```json
{
  "strainup": 1,
  "weightUnit": "kg",
  "exercises": [
    {
      "key": "weighted_pull_up",
      "title": "Weighted pull-up",
      "notes": "Full extension between reps.",
      "trackRpe": true,
      "targetRpe": 8,
      "sets": { "count": 4, "reps": 6, "weight": 10, "rest": 120 }
    },
    {
      "key": "tuck_front_lever",
      "title": "Tuck front lever",
      "sets": [
        { "time": 15, "rest": 90 },
        { "time": 12, "rest": 90 },
        { "time": 10 }
      ]
    },
    {
      "key": "easy_run",
      "title": "Easy run",
      "sets": { "count": 1, "distance": 5, "distanceUnit": "km", "time": 1800 }
    }
  ],
  "routines": [
    {
      "title": "Pull day",
      "days": ["monday", "thursday"],
      "items": ["weighted_pull_up", { "rest": 180 }, "tuck_front_lever"]
    }
  ]
}
```

## Root

| Field | Type | Required | Rules |
|---|---|---|---|
| `strainup` | integer | yes | Format version. Always `1`. |
| `weightUnit` | `"kg"` or `"lb"` | no | Unit of every `weight` in the file. Defaults to the user's unit. |
| `exercises` | array | no | At most 100. |
| `routines` | array | no | At most 20. |

The file must contain at least one exercise or one routine.

## Exercise

| Field | Type | Required | Rules |
|---|---|---|---|
| `key` | text | no | Identifier inside this file, used by routines to point to the exercise. Lowercase with underscores, for example `bench_press`. Each key must be unique. |
| `title` | text | yes | 1 to 100 characters. Short and specific: `Incline dumbbell press`, not `Chest exercise 2`. |
| `notes` | text | no | Up to 1000 characters: cues, tempo, setup. |
| `trackRpe` | boolean | no | `true` to ask the user for the RPE (rate of perceived exertion) of each set. Default `false`. |
| `targetRpe` | number | no | Target RPE, 6 to 10 in steps of 0.5. Default 8. Only useful with `trackRpe`. |
| `sets` | object or array | yes | An **object** when every set is the same, with `count` (1 to 50) sets. An **array** when sets differ, one element per set (1 to 50). |

## Set

The fields of a set, in the `sets` object or in each element of the `sets` array.

| Field | Type | Rules |
|---|---|---|
| `reps` | integer | 1 to 1000. |
| `weight` | number | 0 to 1000 kg (or the same in lb), in `weightUnit`. **Omit it for bodyweight exercises.** For weighted bodyweight moves (weighted pull-up, weighted dip), it is the added load. |
| `time` | integer | **Seconds**, 1 to 86400. Duration of the set: a hold, an interval, a run. |
| `autoAdvance` | boolean | For a timed set: move on automatically when the countdown ends. Default `false`. |
| `distance` | number | Greater than 0. |
| `distanceUnit` | `"m"`, `"km"`, `"yd"` or `"mi"` | Unit of `distance`. Defaults to the user's unit. |
| `rest` | integer | **Seconds** of rest after the set, 0 to 3600. |

A set needs at least one of `reps`, `time` or `distance`. Only the fields you write are tracked: a set with `reps` and `weight` tracks reps and load, a set with `time` only is a timed hold.

## Routine

| Field | Type | Required | Rules |
|---|---|---|---|
| `title` | text | yes | 1 to 100 characters. |
| `days` | array | no | Days of the week to plan it on: `"monday"` to `"sunday"`. |
| `items` | array | yes | The exercises in order. Each item is the `key` of an exercise of this file, the exact `title` of an exercise of this file or of one the user already has in the app, or `{ "rest": seconds }` for a rest block between exercises. |

Every exercise used by a routine should be defined in `exercises`, unless the user told you its exact title in the app. Prefer keys over titles in `items`: they cannot be misspelled.

## Language

Write titles and notes in the language the user speaks, but name exercises the way people at the gym actually say them in that language. Keep the English name when that is the usual term: in French, "Biceps curl" and "Hip thrust" stay as they are. Do not translate a name just to translate it.

## Common mistakes

- `time` or `rest` in minutes: `"rest": 2` is two seconds. Write `"rest": 120`.
- `weight` on a bodyweight exercise: push-ups have no `weight`. Write `"weight": 0` only if you really mean no load on a loaded exercise.
- A routine that uses an exercise not defined in the file: the item is dropped.
- Two exercises with the same `key`: only the first one is kept.
- Several JSON blocks, or the program split in two answers: the app reads one block.
- Units written in values (`"rest": "90s"`, `"weight": "20 kg"`): use plain numbers, and `weightUnit` / `distanceUnit` for units.

## What the app tolerates

The app is forgiving, but a clean answer avoids surprises:

- Text around the JSON block is ignored.
- Trailing commas, comments, single quotes and curly quotes are accepted.
- Numbers written as text (`"8"`) are accepted, and `time` / `rest` also accept `"m:ss"` (`"2:00"`).
- Unknown fields are ignored.
- An invalid exercise or routine is skipped with its reason; the rest is still imported.

## Another example: full body with dumbbells, three days a week

```json
{
  "strainup": 1,
  "weightUnit": "kg",
  "exercises": [
    {
      "key": "goblet_squat",
      "title": "Goblet squat",
      "notes": "Elbows inside the knees at the bottom.",
      "sets": { "count": 3, "reps": 10, "weight": 16, "rest": 90 }
    },
    {
      "key": "db_bench_press",
      "title": "Dumbbell bench press",
      "trackRpe": true,
      "targetRpe": 8,
      "sets": [
        { "reps": 10, "weight": 14, "rest": 90 },
        { "reps": 8, "weight": 16, "rest": 90 },
        { "reps": 8, "weight": 16, "rest": 90 }
      ]
    },
    {
      "key": "one_arm_row",
      "title": "One-arm dumbbell row",
      "sets": { "count": 3, "reps": 10, "weight": 16, "rest": 60 }
    },
    {
      "key": "plank",
      "title": "Plank",
      "sets": { "count": 3, "time": 40, "rest": 45, "autoAdvance": true }
    }
  ],
  "routines": [
    {
      "title": "Full body",
      "days": ["monday", "wednesday", "friday"],
      "items": ["goblet_squat", "db_bench_press", "one_arm_row", { "rest": 120 }, "plank"]
    }
  ]
}
```
