Esquema de treino
Última atualização: 2026-05-29
Your Trainer é um aplicativo de ciclismo indoor para vários ciclistas em tablets Android. Controle do smart trainer com dados locais + controle local. Compra única.
O arquivo de treino .ytw é JSON puro — escreva-o em qualquer editor de texto, importe pela folha de compartilhamento, compartilhe com amigos. O editor visual cobre os casos comuns; o esquema é a saída de emergência quando você precisa de controle total.
Quando escrever à mão
A maioria dos ciclistas nunca precisa desta página — o editor visual de treinos e o AI Workout Coach cobrem tudo, de uma sessão de Limiar 4×8 a uma pilha de microbursts. Recorra ao esquema quando:
- Você quiser uma estrutura que o editor não expõe (Aquecimentos muito longos, scripts elaborados de avisos, alvos mistos de Cadência por Intervalo).
- Você estiver traduzindo um treino que já tem no formato de outra ferramenta.
- Você quiser enviar um treino para outra pessoa como um único arquivo.
- Você quiser gerar treinos em massa de forma programática.
Exemplo mínimo
O menor arquivo .ytw válido é um programa com um Intervalo. Salve com a extensão .ytw e compartilhe para dentro do 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" }
]
}
Campos de nível superior
O objeto de nível superior descreve um programa de treino. Os campos obrigatórios estão sinalizados.
| Campo | Tipo | Descrição |
|---|---|---|
programId obrigatório | string | Identificador estável. Use kebab-case (my-sweet-spot). A chave única do treino na sua biblioteca — reimportar um arquivo com o mesmo programId atualiza a entrada existente em vez de criar uma duplicata. |
programName obrigatório | string | Nome de exibição no locale primário do ciclista. |
description obrigatório | string | Descrição de uma ou duas frases mostrada no card do treino. |
totalDuration obrigatório | inteiro (segundos) | Duração total do treino. O app recalcula esse valor a partir dos Intervalos ao salvar, então é seguro deixá-lo inconsistente durante a autoria. |
intervals obrigatório | array | Lista ordenada de objetos de Intervalo ou grupos de repetição. |
workoutType | string | Família do treino: POWER (padrão), HR_ZONE ou ROUTE. Veja Tipos de treino. |
variant | string | Subformato dentro da família: STANDARD (padrão) ou RAMP_TEST. |
primaryLocale | string (BCP-47) | Locale em que as strings foram escritas. Padrão "en". Define a cadeia de fallback entre locales. |
category | string | Categorização em texto livre (ex.: "threshold", "endurance"). Opcional. |
difficulty | inteiro (1–5) | Dificuldade subjetiva. Aparece no card do treino. |
isUserCreated | boolean | True para treinos criados no app pelo ciclista; false para treinos importados / incluídos. Padrão false. |
isFavorite | boolean | Sinalizador de fixar no topo. Os ciclistas alternam isso no app; normalmente omitido em arquivos compartilhados. |
routeProfile | array de { distanceMeters, elevationMeters } | Perfil completo de elevação para treinos ROUTE. Null para POWER e HR_ZONE. |
strings | object (locale → LocaleStrings) | Traduções por locale do nome, descrição, rótulos de Intervalos e avisos. |
Campos de Intervalo
Cada objeto de Intervalo descreve um bloco do treino. A forma do bloco depende do workoutType do pai — blocos de Potência usam percentuais de potência, blocos de Zona de FC usam um alvo de Zona.
| Campo | Tipo | Descrição |
|---|---|---|
duration obrigatório | inteiro (segundos) | Duração do bloco em segundos. |
targetPowerPercent obrigatório para potência | inteiro (% do FTP) | Alvo de potência para treinos POWER. Mutuamente exclusivo com targetHrZone. |
targetPowerEndPercent | inteiro (% do FTP) | Potência final opcional para rampas. Quando presente, a wattagem-alvo interpola linearmente de targetPowerPercent → targetPowerEndPercent ao longo do bloco. |
targetHrZone obrigatório para FC | inteiro (1–5) | Zona de FC para treinos HR_ZONE. Mutuamente exclusivo com targetPowerPercent. |
intensityZone obrigatório | string | Token visual de Zona: Z1–Z5. Define a cor na visualização do terreno. Veja Zonas de treino. |
intervalType | string | WARMUP, COOLDOWN ou INTERVAL (padrão). Blocos de Aquecimento e Desaquecimento são excluídos dos resumos que consideram apenas o trabalho (potência média dos blocos de trabalho, tempo em Zona da parte de trabalho, etc.). |
label obrigatório | string | Texto exibido no bloco no locale primário do ciclista. Variantes entre locales ficam em strings.<locale>.labels. |
id | string | Slug estável — a chave usada em strings.<locale>.labels e na composição da chave de avisos. Recomendado para qualquer treino que seja distribuído com traduções. |
autoLabel | boolean | True quando o rótulo foi gerado por um preset do editor em vez de digitado pelo ciclista. Rótulos automáticos são localizados pelo próprio Your Trainer e não precisam de entradas por locale em strings. Padrão false. |
cadenceTarget | inteiro (RPM) | Alvo opcional de Cadência para o bloco (ex.: 60 para subidas em baixa Cadência, 100 para spin-ups). |
cues | array de CoachingCue | Avisos de coaching que disparam durante o bloco. |
Avisos de coaching
Um aviso de coaching é uma sobreposição curta de texto que aparece no cockpit durante o pedal. Cada aviso tem um deslocamento dentro do Intervalo pai, o texto a exibir e por quanto tempo deixá-lo na tela.
| Campo | Tipo | Descrição |
|---|---|---|
offsetSec obrigatório | inteiro (segundos) | Segundos a partir do início do Intervalo pai em que o aviso dispara. |
text obrigatório | string | Texto do aviso no locale primário do treino. Variantes entre locales ficam em strings.<locale>.cues, com chave <intervalId>:<cueIndex>. |
durationSec | inteiro (segundos) | Por quanto tempo o aviso permanece na tela. Padrão 5. |
Exemplo de Intervalo com três avisos (a composição da chave usa o id do Intervalo pai + o índice do aviso no array):
{
"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 localizadas & cadeia de fallback
O bloco strings carrega traduções por locale de cada string visível ao ciclista no treino. Cada entrada de locale tem a mesma forma:
"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" }
}
}
As chaves de avisos seguem o padrão <intervalId>:<cueIndex> — então o primeiro aviso no Intervalo work tem a chave work:0.
Para cada string visível ao ciclista, o app escolhe a melhor correspondência de locale nesta ordem:
strings[<rider-locale>]— o locale do próprio ciclista.strings[primaryLocale]— o locale do autor.strings["en"]— fallback universal.- O campo de nível superior (
programName,labeldo Intervalo,textdo aviso).
Strings exibidas a partir de qualquer locale que não seja o do próprio ciclista aparecem em itálico nos cards de treino e no cockpit, para que o ciclista saiba quais strings ainda não foram traduzidas.
strings blocks for additional locales when prompted.Grupos de repetição
Para estruturas repetitivas, um grupo de repetição escreve a unidade uma única vez e diz ao app quantas vezes reproduzi-la. Os grupos de repetição são expandidos em blocos individuais na importação, para que o ciclista veja cada bloco na faixa de Intervalos seguintes durante o pedal.
{
"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" }
]
}
O exemplo acima expande para 1 Aquecimento + 4×(Limiar + Recuperação) + 1 Desaquecimento = 10 blocos. Os grupos de repetição podem ser aninhados, mas o formato plano é preferível para a legibilidade.
Tipos de treino & variantes
O campo workoutType seleciona o paradigma; variant seleciona um subformato dentro dele.
workoutType | O que é | Forma de Intervalo exigida |
|---|---|---|
POWER (padrão) | Intervalos ancorados no FTP. O trainer mantém a wattagem-alvo em ERG, ou segue curvas de resistência em SIM. | Cada Intervalo tem targetPowerPercent (e, opcionalmente, targetPowerEndPercent para rampas). |
HR_ZONE | Intervalos guiados pela Frequência cardíaca. O trainer ajusta a wattagem em tempo real para manter a FC do ciclista na Zona-alvo — útil quando a carga cardiovascular é a métrica de treino (Recuperação, base, trabalho polarizado). | Cada Intervalo tem targetHrZone (1–5). O botão START exibe VINCULAR HRM até que o HRM esteja conectado. |
ROUTE | Pedais de simulação guiados por inclinação. O trainer segue o perfil de elevação a partir de routeProfile; o ciclista escolhe a Cadência e a relação de marchas. | Os Intervalos normalmente ficam vazios ou contêm um único placeholder com a duração total. O conteúdo real do pedal fica em routeProfile. |
O campo variant seleciona um subformato:
STANDARD(padrão) — treino estruturado comum.RAMP_TEST— sinalizado como um teste de FTP. Pede ao ciclista para atualizar o FTP ao final do pedal e é excluído dos resumos de carga de treino (para que um esforço máximo não distorça o gráfico de carga semanal).
Pacotes (.ytwpack)
Quando você quer um lote curado de treinos de uma só vez, o formato .ytwpack agrupa muitos arquivos .ytw junto com um manifesto por pacote. Um .ytwpack é, no fundo, um arquivo ZIP — renomeie para .zip e descompacte para ver o conteúdo, ou importe-o pelo instalador de pacotes do Your Trainer para instalar todos os treinos com um único toque.
Um arquivo .ytwpack contém:
manifest.jsonna raiz do arquivo — os metadados do nível do pacote descritos abaixo.- Um
<slug>.ytwpor treino no pacote, em que cada arquivo segue o esquema documentado acima.
O manifesto do pacote carrega informação suficiente para uma tela de decisão de instalação sem precisar abrir cada treino. Campos de nível superior:
| Campo | Tipo | Descrição |
|---|---|---|
schema_version obrigatório | inteiro | Atualmente sempre 1. |
pack_id obrigatório | string | Identificador estável em kebab-case (ex.: sweet-spot). |
name obrigatório | string | Nome de exibição mostrado no catálogo de pacotes. |
description obrigatório | string | Resumo de uma ou duas frases visível antes de o ciclista tocar para instalar. |
version obrigatório | string (SemVer) | MAJOR.MINOR.PATCH, com -prerelease opcional. Patch para correções de conteúdo; minor para treinos adicionados; major para mudanças incompatíveis no esquema. Incorporado no nome de arquivo publicado (v1.0.2.ytwpack). |
content_hash obrigatório | string | sha256: sobre a concatenação ordenada por slug dos bytes de cada arquivo .ytw. Estável entre regenerações de conteúdo inalterado; muda toda vez que os treinos dentro mudam. |
generated_at obrigatório | string (ISO 8601) | Timestamp em UTC. |
set obrigatório | string | power ou hr-zone — a família de treino à qual este pacote pertence. |
category obrigatório | string | Sub-taxonomia dentro do set (ex.: sweet-spot). |
workout_count obrigatório | inteiro | Número de entradas .ytw no pacote. |
total_ride_time_seconds obrigatório | inteiro | Soma das durações de cada treino no pacote. |
experience_level obrigatório | string | Calculado a partir da faixa de dificuldade do conteúdo — um de beginner / intermediate / advanced / mixed. Formato de transmissão em minúsculas; o app capitaliza para exibição. |
hrm_required obrigatório | boolean | True se algum treino no pacote usar HR_ZONE. |
type_mix obrigatório | object | Percentual por categoria do tempo total de pedal (soma 100). Alimenta o gráfico de rosca de mistura de tipos na tela de instalação no app. |
duration_histogram obrigatório | object | Contagem de treinos por faixa de duração: 0-30, 30-60, 60-90, 90+ (minutos). Alimenta o gráfico de duração da tela de instalação. |
contents obrigatório | array | Entradas completas por treino — superconjunto do formato de entrada do manifesto da biblioteca; cada uma traz slug, name, duration_seconds, métricas de resumo, além de um array sparkline para a renderização da miniatura. |
Dois caminhos de instalação para um .ytwpack baixado:
- Instalação com um toque no Your Trainer — abra o
.ytwpackna folha de compartilhamento, o Your Trainer lê o manifesto do pacote, mostra o que há dentro (mistura de tipos, histograma de duração, tempo total de pedal) e instala todos os treinos de uma vez. Disponível assim que o instalador de pacotes no app for lançado. - Descompactação manual — renomeie para
.zip(ou descompacte diretamente com qualquer ferramenta de arquivamento) e então compartilhe cada.ytwpara dentro do Your Trainer, um por vez, pela folha de compartilhamento.
Exemplo de manifesto de pacote (com contents truncado para a legibilidade):
{
"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 … */
]
}
Manifestos de biblioteca & de pacote
Dois manifestos são publicados ao lado dos artefatos para download. Ambos são JSON puro; ambos são descritos por documentos JSON Schema que você pode obter diretamente.
| URL | O que ele lista | JSON Schema |
|---|---|---|
library/manifest.json |
Cada .ytw curado na biblioteca — metadados por treino para clientes de navegação / busca / filtro. Também lista os downloads de .ytwpack disponíveis (caminho do arquivo, versão, hash de conteúdo, resumo de mistura de tipos, URL do ícone). |
/schemas/workout-manifest.json |
packs/manifest.json |
Endpoint do catálogo de pacotes: cada .ytwpack publicado com metadados de resumo. Mesmo formato de entrada por pacote do array packs do manifesto da biblioteca; a Pack Library no app busca isso quando o ciclista solicita uma atualização. |
/schemas/workout-manifest.json |
(dentro de cada .ytwpack) |
Manifesto por pacote carregado como manifest.json na raiz do arquivo — a tabela acima documenta seu formato. |
/schemas/workout-pack-manifest.json |
Se você está construindo ferramentas que consomem a biblioteca — um navegador de treinos personalizado, um conversor de treinos que tem como alvo .zwo, um painel de coach que exibe pacotes — estes são os contratos contra os quais validar. O mesmo formato de entrada por treino aparece nos arrays contents / workouts de ambos os manifestos, então um cliente que lida com um lida com o outro.
Exemplos resolvidos
Intervalo em rampa
Um Aquecimento de 5 minutos que sobe em rampa de 40 % do FTP a 75 % do FTP via interpolação linear:
{
"id": "rampup",
"duration": 300,
"targetPowerPercent": 40,
"targetPowerEndPercent": 75,
"intensityZone": "Z1",
"label": "Ramp up",
"intervalType": "WARMUP"
}
Over-under
Três séries de 2 minutos a 95 % do FTP / 1 minuto a 105 % do FTP, expressas como um grupo de repetição:
{
"repeat": 3,
"intervals": [
{ "id": "under", "duration": 120, "targetPowerPercent": 95, "intensityZone": "Z4", "label": "Under" },
{ "id": "over", "duration": 60, "targetPowerPercent": 105, "intensityZone": "Z5", "label": "Over" }
]
}
Treino por Zona de FC
Pedal de Resistência de 30 minutos em Z2 com uma arrancada de 3 minutos em Z4 no meio:
{
"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" }
]
}
Multilíngue
O exemplo mínimo de Sweet Spot com blocos strings em EN + DE + NL. Mesmo treino, três experiências nativas:
{
"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" }
}
}
}
Armadilhas comuns
idausente nos Intervalos. O runtime aceita Intervalos semid, mas você perde seus pontos de entrada para tradução —strings.<locale>.labelse o mapa de chaves de avisos dependem dele. Se você pretende distribuir um treino comstrings, dê umida cada Intervalo.- Chaves de avisos incompatíveis. O padrão da chave de avisos é
<intervalId>:<cueIndex>— começa em zero. O terceiro aviso no Intervaloworkéwork:2, nãowork:3. - Ambos
targetPowerPercentetargetHrZoneno mesmo Intervalo. Eles são mutuamente exclusivos — mantenha um ou o outro com base noworkoutTypedo pai. - Esquecer o
intervalTypeem Aquecimentos e Desaquecimentos. O padrão éINTERVAL— defina explicitamenteWARMUP/COOLDOWNpara que a camada de análise não inclua esses blocos no total de trabalho. totalDurationinflado. Inofensivo durante a autoria — o Your Trainer o recalcula a partir deintervalsao salvar. Mas vale a pena acertar antes de compartilhar, porque algumas ferramentas externas exibem o campo literalmente.- Token
intensityZoneerrado. Deve ser um deZ1–Z5como string."Z6"ou"3"não vão renderizar a cor certa na visualização do terreno.
Referência
- O AI Workout Coach gera JSON
.ytwválido a partir de uma descrição em texto puro — útil como ponto de partida que você depois edita à mão. - Para autoria programática determinística (sem LLM): a Your Trainer MCP — Documentação para Integradores cobre
build_workout_from_intentedecompose_workout, que compõem para / a partir deste esquema. - Para treinos que não importam: Solução de problemas → Importações de treino & rota.
- Para definições de Zona: Zonas de treino.
- Para termos desconhecidos: Glossário.
- Os treinos incluídos com o Your Trainer exercitam cada formato que o esquema suporta — examinar um deles em um editor de texto (exportando pelo editor visual) é uma forma rápida de ver um exemplo real de qualquer campo.
Para buscas programáticas de spec de formato (tabelas de campos, exemplos, constraints, glossário), a fonte canônica legível por máquina é o Your Trainer MCP — chame get_format_spec, get_canonical_examples, get_format_constraints ou get_format_glossary de qualquer cliente MCP. Essas tools servem do mesmo registro de conhecimento que alimenta esta página; se a resposta de uma tool alguma vez divergir desta página, o MCP é canônico e esta página está obsoleta.
JSON Schemas (contratos legíveis por máquina para clientes que constroem sobre estes formatos):
/schemas/workout-manifest.json— manifesto da biblioteca + formato de entrada por pacote (cobre tantolibrary/manifest.jsonquantopacks/manifest.json)./schemas/workout-pack-manifest.json— omanifest.jsoncarregado dentro de cada.ytwpack./schemas/workout-intent.json— formato de intenção estruturada usado pelas ferramentas de autoria de treinos que compõem para.ytw.