ワークアウトスキーマ
最終更新: 2026-05-29
Your Trainer は、複数ライダー対応の Android タブレット向け屋内サイクリングアプリです。スマートトレーナー制御、ローカルデータ + ローカル制御。買い切り型。
.ytw ワークアウトファイルはプレーンな JSON です — 任意のテキストエディタで作成し、共有シート経由でインポートして、友人と共有できます。ビジュアルエディタは一般的なケースに対応しますが、スキーマは完全な制御が必要なときの逃げ道です。
手書き作成すべきとき
ほとんどのライダーはこのページを必要としません — ビジュアルワークアウトエディタと AI ワークアウトコーチが、4×8 の閾値セッションからマイクロバーストスタックまですべてをカバーします。スキーマに手を伸ばすのは次のような場合です:
- エディタが公開していない構造が欲しいとき(非常に長いウォームアップ、凝ったキュースクリプト、インターバルごとに異なるケイデンスターゲットなど)。
- 他のツールの形式ですでに持っているワークアウトを変換しているとき。
- 1 つのファイルとして誰かにワークアウトを送りたいとき。
- プログラム的にワークアウトを一括生成したいとき。
最小限の例
最短の有効な .ytw ファイルは、1 つのインターバルを持つ 1 つのプログラムです。.ytw 拡張子で保存し、Your Trainer に共有しよう。
{
"programId": "my-sweet-spot",
"programName": "My Sweet Spot 30",
"description": "A short sweet-spot workout.",
"totalDuration": 1800,
"workoutType": "POWER",
"primaryLocale": "en",
"intervals": [
{ "id": "warmup", "duration": 300, "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Warmup", "intervalType": "WARMUP" },
{ "id": "work", "duration": 1200, "targetPowerPercent": 88, "intensityZone": "Z3", "label": "Sweet Spot", "intervalType": "INTERVAL" },
{ "id": "cooldown", "duration": 300, "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Cooldown", "intervalType": "COOLDOWN" }
]
}
トップレベルフィールド
トップレベルオブジェクトは 1 つのワークアウトプログラムを表します。必須フィールドはフラグで示されています。
| フィールド | 型 | 説明 |
|---|---|---|
programId 必須 | string | 安定した識別子。ケバブケース(my-sweet-spot)を使ってください。ライブラリ内でのワークアウトの一意キーです — 同じ programId のファイルを再インポートすると、重複を作らず既存のエントリを更新します。 |
programName 必須 | string | ライダーの主要ロケールでの表示名。 |
description 必須 | string | ワークアウトカードに表示される 1〜2 文の説明。 |
totalDuration 必須 | integer(秒) | ワークアウト全体の長さ。アプリは保存時にインターバルから再計算するため、作成中は一貫していなくても問題ありません。 |
intervals 必須 | array | インターバルオブジェクト または リピートグループ の順序付きリスト。 |
workoutType | string | ワークアウトファミリー: POWER(デフォルト)、HR_ZONE、または ROUTE。ワークアウトタイプ を参照。 |
variant | string | ファミリー内のサブ形状: STANDARD(デフォルト)または RAMP_TEST。 |
primaryLocale | string(BCP-47) | 文字列が作成されたロケール。デフォルトは "en"。クロスロケールフォールバックチェーンを駆動します。 |
category | string | 自由記述のカテゴリ分け(例: "threshold"、"endurance")。オプション。 |
difficulty | integer(1〜5) | 主観的な難易度。ワークアウトカードに表示されます。 |
isUserCreated | boolean | ライダーがアプリ内で作成したワークアウトは true、インポートまたは同梱されたワークアウトは false。デフォルトは false。 |
isFavorite | boolean | トップ固定フラグ。ライダーがアプリ内で切り替えます。共有ファイルでは通常省略されます。 |
routeProfile | array of { distanceMeters, elevationMeters } | ROUTE ワークアウト用の完全な標高プロファイル。POWER と HR_ZONE では null。 |
strings | object (locale → LocaleStrings) | 名前、説明、インターバルラベル、キューのロケール別翻訳。 |
インターバルフィールド
各インターバルオブジェクトはワークアウトの 1 ブロックを表します。ブロックの形状は親の workoutType によって決まります — パワーブロックはパワーパーセンテージを、心拍ゾーンブロックはゾーンターゲットを使います。
| フィールド | 型 | 説明 |
|---|---|---|
duration 必須 | integer(秒) | ブロックの長さ(秒)。 |
targetPowerPercent パワー必須 | integer(FTP の %) | POWER ワークアウトのパワーターゲット。targetHrZone とは相互排他的です。 |
targetPowerEndPercent | integer(FTP の %) | オプションのランプ終了パワー。存在する場合、ターゲットワット数はブロック全体で targetPowerPercent → targetPowerEndPercent へ線形に補間されます。 |
targetHrZone 心拍必須 | integer(1〜5) | HR_ZONE ワークアウト用の心拍ゾーン。targetPowerPercent とは相互排他的です。 |
intensityZone 必須 | string | ビジュアルゾーントークン: Z1〜Z5。地形ビジュアライゼーションの色を決定します。トレーニングゾーン を参照。 |
intervalType | string | WARMUP、COOLDOWN、または INTERVAL(デフォルト)。ウォームアップとクールダウンのブロックはワーク専用サマリー(ワークブロックの平均パワー、ワーク部分のゾーン滞在時間など)から除外されます。 |
label 必須 | string | ライダーの主要ロケールでブロックに表示されるテキスト。クロスロケールバリアントは strings.<locale>.labels に格納されます。 |
id | string | 安定したスラッグ — strings.<locale>.labels およびキューキー構成で使用されるキーです。翻訳付きで配布するワークアウトには推奨されます。 |
autoLabel | boolean | ラベルがライダーによって入力されたものではなくエディタプリセットで生成された場合に true。自動ラベルは Your Trainer 自体がローカライズするため、strings 内にロケール別エントリは不要です。デフォルトは false。 |
cadenceTarget | integer(RPM) | ブロック用のオプションのケイデンスターゲット(例: 低ケイデンスのクライミングなら 60、スピンアップドリルなら 100)。 |
cues | array of CoachingCue | ブロック中に発火するコーチングキュー。 |
コーチングキュー
コーチングキューは、ライド中にコックピットに表示される短いテキストオーバーレイです。各キューは親インターバル内のオフセット、表示するテキスト、画面に表示する時間の長さを持ちます。
| フィールド | 型 | 説明 |
|---|---|---|
offsetSec 必須 | integer(秒) | キューが発火する親インターバルの開始からの秒数。 |
text 必須 | string | ワークアウトの主要ロケールでのキューテキスト。クロスロケールバリアントは strings.<locale>.cues 内に <intervalId>:<cueIndex> をキーとして格納されます。 |
durationSec | integer(秒) | キューが画面に表示される時間の長さ。デフォルトは 5。 |
3 つのキューを持つインターバルの例(キー構成は親インターバルの id と配列内のキューのインデックスを使います):
{
"id": "work",
"duration": 600,
"targetPowerPercent": 95,
"intensityZone": "Z4",
"label": "Threshold",
"intervalType": "INTERVAL",
"cues": [
{ "offsetSec": 0, "text": "Settle in — find your rhythm." },
{ "offsetSec": 300, "text": "Halfway. Stay smooth.", "durationSec": 8 },
{ "offsetSec": 540, "text": "One minute. Hold form." }
]
}
ローカライズ文字列とフォールバックチェーン
strings ブロックは、ワークアウト内のすべてのライダー向け文字列のロケール別翻訳を保持します。各ロケールエントリは同じ形状です:
"strings": {
"en": {
"name": "Sweet Spot 30",
"description": "A short sweet-spot workout.",
"labels": { "warmup": "Warmup", "work": "Sweet Spot", "cooldown": "Cooldown" },
"cues": { "work:0": "Settle in", "work:1": "Halfway" }
},
"de": {
"name": "Sweet Spot 30",
"description": "Ein kurzes Sweet-Spot-Training.",
"labels": { "warmup": "Aufwärmen", "work": "Sweet Spot", "cooldown": "Ausrollen" },
"cues": { "work:0": "Locker einrollen", "work:1": "Halbzeit" }
}
}
キューキーは <intervalId>:<cueIndex> のパターンに従います — そのため work インターバルの最初のキューは work:0 をキーとします。
各ライダー向け文字列について、アプリは次の順序で最適なロケール一致を選びます:
strings[<rider-locale>]— ライダー自身のロケール。strings[primaryLocale]— 作成者のロケール。strings["en"]— 普遍的なフォールバック。- トップレベルフィールド(
programName、インターバルlabel、キューtext)。
ライダー自身のロケール以外から表示される文字列は、ワークアウトカードとコックピットで斜体で表示されるため、ライダーはどの文字列がまだ翻訳されていないかを判別できます。
strings blocks for additional locales when prompted.リピートグループ
繰り返し構造には、リピートグループでユニットを 1 回作成し、アプリに何回再生するかを伝えます。リピートグループはインポート時に個別のブロックに展開されるため、ライダーはライド中の次のインターバルストリップで各ブロックを確認できます。
{
"intervals": [
{ "id": "warmup", "duration": 600, "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Warmup", "intervalType": "WARMUP" },
{
"repeat": 4,
"intervals": [
{ "id": "on", "duration": 480, "targetPowerPercent": 95, "intensityZone": "Z4", "label": "Threshold" },
{ "id": "off", "duration": 240, "targetPowerPercent": 55, "intensityZone": "Z1", "label": "Recovery" }
]
},
{ "id": "cooldown", "duration": 600, "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Cooldown", "intervalType": "COOLDOWN" }
]
}
上記の例は 1 つのウォームアップ + 4×(閾値 + リカバリー)+ 1 つのクールダウン = 10 ブロックに展開されます。リピートグループはネスト可能ですが、可読性のためにフラットの方が望ましいです。
ワークアウトタイプとバリアント
workoutType フィールドはパラダイムを選択し、variant はその中のサブ形状を選択します。
workoutType | 概要 | 必要なインターバル形状 |
|---|---|---|
POWER(デフォルト) | FTP を基準としたインターバル。トレーナーは ERG でターゲットワット数を維持するか、SIM でレジスタンスカーブに従います。 | 各インターバルには targetPowerPercent(ランプの場合はオプションで targetPowerEndPercent)があります。 |
HR_ZONE | 心拍駆動のインターバル。トレーナーはライダーの心拍をターゲットゾーン内に保つようにワット数をリアルタイムで調整します — 心血管負荷がトレーニング指標となる場合(リカバリー、ベース、ポラライズドワーク)に有用です。 | 各インターバルには targetHrZone(1〜5)があります。HRM が接続されるまで START ボタンは HRM 接続 と表示されます。 |
ROUTE | スロープ駆動のシミュレーションライド。トレーナーは routeProfile の標高プロファイルに従います。ライダーはケイデンスとギアリングを選択します。 | インターバルは通常空であるか、全長プレースホルダー 1 つを含みます。実際のライドコンテンツは routeProfile 内にあります。 |
variant フィールドはサブ形状を選択します:
STANDARD(デフォルト)— 通常の構造化ワークアウト。RAMP_TEST— FTP テストとしてフラグ付けされます。ライド終了時にライダーに FTP の更新を促し、トレーニング負荷サマリーから除外されます(最大努力が週次負荷チャートを歪めないようにするため)。
パック(.ytwpack)
厳選されたワークアウトのバッチを一度に欲しいときは、.ytwpack 形式が多数の .ytw ファイルをパック単位のマニフェストと一緒にまとめます。.ytwpack は内部的には ZIP アーカイブです — .zip にリネームして解凍すれば中身を確認できますし、Your Trainer のパックインストーラー経由でインポートすれば、すべてのワークアウトをワンタップでインストールできます。
.ytwpack アーカイブには次が含まれます:
- アーカイブのルートにある
manifest.json— 後述するパックレベルのメタデータ。 - パック内のワークアウトごとに 1 つの
<slug>.ytw、各ファイルは上記のスキーマに従います。
パックマニフェストは、すべてのワークアウトを開かなくてもインストール判断シートに必要な情報を保持します。トップレベルフィールド:
| フィールド | 型 | 説明 |
|---|---|---|
schema_version 必須 | integer | 現在は常に 1。 |
pack_id 必須 | string | 安定したケバブケース識別子(例: sweet-spot)。 |
name 必須 | string | パックカタログに表示される表示名。 |
description 必須 | string | ライダーがインストールをタップする前に表示される 1〜2 文の要約。 |
version 必須 | string(SemVer) | MAJOR.MINOR.PATCH、オプションで -prerelease。コンテンツ修正はパッチ、追加ワークアウトはマイナー、破壊的スキーマ変更はメジャー。公開ファイル名に埋め込まれます(v1.0.2.ytwpack)。 |
content_hash 必須 | string | すべての .ytw ファイルのバイトをスラッグでソートして連結したものに対する sha256:。変更されていないコンテンツの再生成では安定し、内部のワークアウトが変わるたびに更新されます。 |
generated_at 必須 | string(ISO 8601) | UTC タイムスタンプ。 |
set 必須 | string | power または hr-zone — このパックが属するワークアウトファミリー。 |
category 必須 | string | セット内のサブタクソノミー(例: sweet-spot)。 |
workout_count 必須 | integer | パック内の .ytw エントリ数。 |
total_ride_time_seconds 必須 | integer | パック内のすべてのワークアウトの長さの合計。 |
experience_level 必須 | string | コンテンツの難易度範囲から計算されます — beginner / intermediate / advanced / mixed のいずれか。ワイヤー形式は小文字で、アプリが表示用に大文字化します。 |
hrm_required 必須 | boolean | パック内のワークアウトのいずれかが HR_ZONE を使用する場合は true。 |
type_mix 必須 | object | 総ライド時間に対するカテゴリ別パーセンテージ(合計 100)。インストールシート上のアプリ内タイプミックスドーナツを駆動します。 |
duration_histogram 必須 | object | 持続時間ビンごとのワークアウト数: 0-30、30-60、60-90、90+(分)。インストールシートの持続時間チャートを駆動します。 |
contents 必須 | array | ワークアウトごとの完全なエントリ — ライブラリマニフェストのエントリ形状のスーパーセット。各エントリは slug、name、duration_seconds、サマリー指標、サムネイル描画用の sparkline 配列を持ちます。 |
ダウンロードした .ytwpack のインストール経路は 2 つあります:
- Your Trainer でのワンタップインストール —
.ytwpackを共有シートで開くと、Your Trainer がパックマニフェストを読み込み、中身(タイプミックス、持続時間ヒストグラム、総ライド時間)を表示し、すべてのワークアウトを一度にインストールします。アプリ内パックインストーラーが出荷されたら利用可能です。 - 手動解凍 —
.zipにリネーム(または任意のアーカイブツールで直接解凍)してから、各.ytwを共有シート経由で Your Trainer に 1 つずつ共有してください。
サンプルパックマニフェスト(可読性のため contents を省略):
{
"schema_version": 1,
"pack_id": "sweet-spot",
"name": "Sweet Spot",
"description": "26 sweet-spot sessions across classic intervals, sustained stacks, and over-unders.",
"version": "1.0.2",
"content_hash": "sha256:9432a3a76015158dc71ec63…",
"generated_at": "2025-05-28T00:00:00Z",
"set": "power",
"category": "sweet-spot",
"workout_count": 26,
"total_ride_time_seconds": 119340,
"experience_level": "intermediate",
"hrm_required": false,
"type_mix": { "sweet-spot": 100 },
"duration_histogram": { "0-30": 0, "30-60": 5, "60-90": 12, "90+": 9 },
"contents": [
{ "slug": "sweet-spot-3x10min-at-88pct-ftp", "name": "Sweet Spot 3×10min @ 88% FTP", "duration_seconds": 3300, "tss": 55.8, "intensity_factor": 0.764, "sparkline": […] },
{ "slug": "sweet-spot-3x15min-at-90pct-ftp", "name": "Sweet Spot 3×15min @ 90% FTP", "duration_seconds": 4500, "tss": 81.6, "intensity_factor": 0.808, "sparkline": […] }
/* … 24 more workouts … */
]
}
ライブラリとパックマニフェスト
ダウンロード可能なアーティファクトと一緒に 2 つのマニフェストが公開されています。どちらもプレーンな JSON であり、直接フェッチできる JSON Schema ドキュメントで記述されています。
| URL | リスト内容 | JSON Schema |
|---|---|---|
library/manifest.json |
ライブラリ内のすべての厳選された .ytw — 閲覧 / 検索 / フィルタークライアント向けのワークアウトごとのメタデータ。利用可能な .ytwpack ダウンロード(ファイルパス、バージョン、コンテンツハッシュ、タイプミックスサマリー、アイコン URL)もリストします。 |
/schemas/workout-manifest.json |
packs/manifest.json |
パックカタログエンドポイント: 公開されたすべての .ytwpack とサマリーメタデータ。ライブラリマニフェストの packs 配列と同じパックごとのエントリ形状で、アプリ内のパックライブラリはライダーが開始したリフレッシュ時にこれを取得します。 |
/schemas/workout-manifest.json |
(各 .ytwpack 内部) |
アーカイブのルートに manifest.json として格納されたパックごとのマニフェスト — 上記の表がその形状を記述しています。 |
/schemas/workout-pack-manifest.json |
ライブラリを利用するツール(カスタムワークアウトブラウザ、.zwo 形式に変換するワークアウトコンバーター、パックを表示するコーチダッシュボードなど)を作っている場合、これらが検証すべき契約です。両マニフェストの contents / workouts 配列には同じワークアウトごとのエントリ形状が現れるため、一方を扱えるクライアントはもう一方も扱えます。
実例
ランプインターバル
FTP の 40 % から 75 % まで線形補間でランプする 5 分間のウォームアップ:
{
"id": "rampup",
"duration": 300,
"targetPowerPercent": 40,
"targetPowerEndPercent": 75,
"intensityZone": "Z1",
"label": "Ramp up",
"intervalType": "WARMUP"
}
オーバー・アンダー
FTP の 95 % で 2 分間 / FTP の 105 % で 1 分間を 3 セット、リピートグループとして表現:
{
"repeat": 3,
"intervals": [
{ "id": "under", "duration": 120, "targetPowerPercent": 95, "intensityZone": "Z4", "label": "Under" },
{ "id": "over", "duration": 60, "targetPowerPercent": 105, "intensityZone": "Z5", "label": "Over" }
]
}
心拍ゾーンワークアウト
中盤に 3 分間の Z4 サージを含む 30 分間の Z2 持久力ライド:
{
"programId": "hr-z2-with-surge",
"programName": "Z2 with a Z4 surge",
"description": "Steady Zone 2 with a single 3-minute Zone 4 surge.",
"totalDuration": 1800,
"workoutType": "HR_ZONE",
"primaryLocale": "en",
"intervals": [
{ "id": "warmup", "duration": 300, "targetHrZone": 1, "intensityZone": "Z1", "label": "Warmup", "intervalType": "WARMUP" },
{ "id": "endure1", "duration": 600, "targetHrZone": 2, "intensityZone": "Z2", "label": "Endurance" },
{ "id": "surge", "duration": 180, "targetHrZone": 4, "intensityZone": "Z4", "label": "Surge" },
{ "id": "endure2", "duration": 420, "targetHrZone": 2, "intensityZone": "Z2", "label": "Endurance" },
{ "id": "cooldown","duration": 300, "targetHrZone": 1, "intensityZone": "Z1", "label": "Cooldown", "intervalType": "COOLDOWN" }
]
}
多言語対応
EN + DE + NL の strings ブロックを持つ最小限のスイートスポット例。同じワークアウト、3 つのネイティブ体験:
{
"programId": "my-sweet-spot",
"programName": "Sweet Spot 30",
"description": "A short sweet-spot workout.",
"totalDuration": 1800,
"workoutType": "POWER",
"primaryLocale": "en",
"intervals": [
{ "id": "warmup", "duration": 300, "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Warmup", "intervalType": "WARMUP" },
{ "id": "work", "duration": 1200, "targetPowerPercent": 88, "intensityZone": "Z3", "label": "Sweet Spot", "intervalType": "INTERVAL",
"cues": [
{ "offsetSec": 0, "text": "Settle in" },
{ "offsetSec": 600, "text": "Halfway" }
]
},
{ "id": "cooldown", "duration": 300, "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Cooldown", "intervalType": "COOLDOWN" }
],
"strings": {
"en": {
"name": "Sweet Spot 30",
"description": "A short sweet-spot workout.",
"labels": { "warmup": "Warmup", "work": "Sweet Spot", "cooldown": "Cooldown" },
"cues": { "work:0": "Settle in", "work:1": "Halfway" }
},
"de": {
"name": "Sweet Spot 30",
"description": "Ein kurzes Sweet-Spot-Training.",
"labels": { "warmup": "Aufwärmen", "work": "Sweet Spot", "cooldown": "Ausrollen" },
"cues": { "work:0": "Locker einrollen", "work:1": "Halbzeit" }
},
"nl": {
"name": "Sweet Spot 30",
"description": "Een korte sweet-spot-training.",
"labels": { "warmup": "Inrijden", "work": "Sweet Spot", "cooldown": "Uitrijden" },
"cues": { "work:0": "Rustig inrijden", "work:1": "Halverwege" }
}
}
}
よくある落とし穴
- インターバルに
idがない。 ランタイムはidのないインターバルを受け入れますが、翻訳のエントリポイント —strings.<locale>.labelsとキューキーマップ — を失います。strings付きでワークアウトを配布する予定なら、すべてのインターバルにidを付けてください。 - キューキーの不一致。 キューキーのパターンは
<intervalId>:<cueIndex>で、ゼロインデックスです。workインターバルの 3 番目のキューはwork:2であってwork:3ではありません。 - 同じインターバル上に
targetPowerPercentとtargetHrZoneの両方。 これらは相互排他的です — 親のworkoutTypeに基づいてどちらか一方を残してください。 - ウォームアップとクールダウンで
intervalTypeを忘れる。 デフォルトはINTERVALです — 分析レイヤーがそれらのブロックをワーク合計に組み込まないように、明示的にWARMUP/COOLDOWNを設定してください。 - 過大な
totalDuration。 作成中は無害です — Your Trainer は保存時にintervalsから再計算します。ただし、一部の外部ツールはフィールドをそのまま表示するため、共有前に正しくしておく価値があります。 - 誤った
intensityZoneトークン。 文字列としてZ1〜Z5のいずれかでなければなりません。"Z6"や"3"では地形ビジュアライゼーションで正しい色が描画されません。
リファレンス
- AI ワークアウトコーチ はプレーンテキストの説明から有効な
.ytwJSON を生成します — 出発点として生成し、その後手で編集すると便利です。 - 決定論的なプログラム的作成(LLMなし)について:Your Trainer MCP — インテグレーター向けドキュメントでは
build_workout_from_intentとdecompose_workoutを扱っており、これらは本スキーマと相互に変換できます。 - インポートに失敗するワークアウトについて: トラブルシューティング → ワークアウトとルートのインポート。
- ゾーンの定義について: トレーニングゾーン。
- 見慣れない用語について: 用語集。
- Your Trainer に同梱されているワークアウトはスキーマがサポートするあらゆる形状を網羅しています — テキストエディタで 1 つ調べる(ビジュアルエディタからエクスポート)のは、どのフィールドの実例も素早く確認する良い方法です。
プログラマチックなフォーマット仕様の検索(フィールド表、例、制約、用語集)について、機械可読の正規ソースは Your Trainer MCP です — 任意のMCPクライアントから get_format_spec、get_canonical_examples、get_format_constraints、または get_format_glossary を呼び出してください。これらのツールはこのページを駆動するのと同じナレッジレジストリから配信されます。ツールの回答がこのページと食い違った場合、MCPが正規であり、このページが古いと判断してください。
JSON Schema(これらの形式の上に構築するクライアント向けの機械可読契約):
/schemas/workout-manifest.json— ライブラリマニフェスト + パックごとのエントリ形状(library/manifest.jsonとpacks/manifest.jsonの両方をカバー)。/schemas/workout-pack-manifest.json— 各.ytwpack内に格納されるmanifest.json。/schemas/workout-intent.json—.ytwに合成するワークアウト作成ツールが使用する構造化インテント形状。