VOLTRA Device Training
Safely operate all VOLTRA training modes through Cortex. Use when the user asks to enter a training mode (weight training, resistance band, damper, isokinetic, isometric, rowing, skiing), set load/resistance/velocity, start a rowing/skiing distance preset or Just Row, turn load on, unload, finish training, configure chains/eccentric, enter cable-length mode, switch/change weight units (lbs/kg), pair or unpair Twin devices, manage session templates (create/update/delete/list), manage custom workout actions, or create/apply custom curves. **Operation only** — if the user is asking what a mode is or wants a recommendation, use `voltra-training-modes` instead. For pure device-state inspection (battery, mode, connection), use `voltra-device-status`. This skill controls VOLTRA's built-in Rowing and Skiing modes only; Concept2 Rowing/Ski history and combined reports belong to `voltra-workout-data`.
Use Cortex's built-in guards as hard stop signals. The device may block commands when it is off the dock, not in a training mode, or the cable is not at zero. When that happens, report the exact reason and stop.
Twin pairing is supported (voltra twin pair / twin status / twin unpair). When topology is Twin (twinConnected, pairing, or degraded), do not enter customCurve or sled pull: set-mode customCurve and custom-curve apply are rejected until unpair; Cortex has no sled-pull training command. Twin can use weightTraining, resistanceBand, damper, isokinetic, isometric, rowing, and skiing. Custom-offset load is still out of scope. All listed single-device training modes remain supported, including Isometric (set-mode isometric then voltra load). Rowing and skiing: set-mode rowing|skiing plus rowing-start / skiing-start. set-auto-unload is supported. Isokinetic is supported; do not confuse it with Isometric. Chains and eccentric support both absolute values and integer percentages; Mountain chains are available through set-chain-mode mountain when the device advertises the capability. Note: auto-load, cancel-auto-load, safety-check, and recording are not supported commands — do not invent them.
Twin Target Rule
voltra twin pair and voltra twin connect are the setup commands. When exactly
two devices are already connected, omit both selectors and the first-connected
device becomes master. If either selector is provided, provide both
--master <MASTER> --slave <SLAVE>. After a Twin is confirmed, the connected logical master is the default target for status,
finish-training, unload, twin status, voltra stream, and twin unpair:
omit --device-id and --master in the normal flow. The unified voltra stream
command automatically emits the Twin v1 stream for a confirmed Twin. Use
--device-id <MASTER> with voltra stream only to disambiguate multiple
connected sessions or pairs. Never pass the slave ID to a Twin training,
finish, status, stream, or unpair command. For a Twin status snapshot, use
voltra status --json or select the master; the slave has no independent live
status session while paired. After unpair, the devices are independent again
and may be selected separately with --device-id.
Mode Context Rule (Critical)
Always check the current training mode before issuing any load/resistance/velocity command. Different modes use different commands for the same user intent. A request like "set weight to 50" or "change load to 30" must be routed to the correct command based on the active mode:
| Current Mode | User Says "set weight/load to X" → Use |
|---|---|
weightTraining | voltra set-weight <n> |
resistanceBand | voltra set-resistance-band <n> |
damper | voltra set-damper <n> (level, not load) |
isokinetic | voltra set-isokinetic-velocity <n> (velocity, not load) |
none | Tell the user to enter a training mode first |
Check the mode first. Run voltra status --json and read trainingMode. If unsure which mode is active, refresh status before issuing any control command. Do not use set-weight unless the device is confirmed to be in weightTraining mode.
Physical Risk Acceptance (Required Before Load)
The voltra load command engages the VOLTRA motor and applies physical force. It will be blocked until the user explicitly accepts the physical risk:
voltra config accept-physical-risk --jsonOr set the environment variable:
$env:VOLTRA_ACCEPT_PHYSICAL_RISK="true"This is a one-time acceptance. If voltra load returns "Physical risk not accepted", guide the user to run the command above. Do not retry load until acceptance is confirmed.
Command Prerequisites
Some settings commands require the device to be in a safe state. When a command is rejected, read the error message and fix the state before retrying:
| If the error says | Then first |
|---|---|
| "Turn weight off before..." | voltra unload --json |
| "Finish the current training mode before..." | voltra finish-training --json |
| "not docked" / "cable not zero" / "workout mode none" | Report the exact reason to the user. Do not guess a workaround. |
CRITICAL — set-weight-unit always requires unload + finish-training first. This is non-negotiable. If the user asks to switch units ("switch to kg", "use kg", "use lbs", "change to lbs"), you MUST run unload AND finish-training before set-weight-unit. Do NOT attempt set-weight-unit while power is on or a training mode is active. Follow the flow below exactly:
Units and Input Conventions
Weight values default to pounds (lbs). set-weight, set-chains, and set-eccentric all accept bare numbers as lbs. To input in kilograms, add the --unit kg flag — Cortex converts the value to lbs before sending it to the device:
voltra set-weight <lbs> --json # lbs input
voltra set-weight <kg> --unit kg --json # kg input → converted to lbs
voltra set-chains <kg> --unit kg --json # kg input → converted to lbs
voltra set-chains <percent> --percent --json # integer percentage, 0..300
voltra set-eccentric <percent> --percent --json # integer percentage, -100..300set-weight-unit is NOT an input conversion flag. It sets the device's global display preference (lbs or kg) — it affects all modes, not just weight training. It does not convert the values you type.
--percent is an alternative input interpretation for set-chains and set-eccentric; it cannot be combined with --unit kg. The command enables percentage-force interpretation before writing the value. An absolute chains/eccentric command switches back to lbs interpretation.
Prerequisites for set-weight-unit: the device must be unloaded (power off) and not in a training mode (none). If power is on or a mode is active, the device will reject the command. The correct flow:
voltra unload --json # turn power off
voltra finish-training --json # exit to mode none
voltra set-weight-unit <lbs|kg> --json # now safe to change
voltra set-mode <mode> --json # e.g. weightTraining
voltra set-weight <lbs> --json # set new weight
voltra load --json # turn power onExample:
voltra set-weight-unit <lbs|kg> --json # tell the device display preference
voltra set-weight <lbs> --json # still lbs input — set-weight-unit does not convert thisUse --unit kg for input conversion. Use set-weight-unit to change the device's preference.
Other units used across modes:
| Unit | Where | Example |
|---|---|---|
| lbs (pounds) | weight, chains, eccentric, resistance band | set-weight 60 |
| kg (via --unit kg) | weight, chains, eccentric input conversion | set-weight 22.5 --unit kg |
| m/s (meters per second) | isokinetic velocity input | set-isokinetic-velocity 0.5 → 0.500 m/s (sent to device as 500 mm/s) |
| cm (centimeters) | resistance ROM length, cable position | set-resistance-rom-length 180 |
| seconds | auto-unload hold-still trigger | set-auto-unload 3 12 |
| reps (count) | auto-unload rep-count trigger | set-auto-unload 3 12 |
| level (1-10, dimensionless) | damper | set-damper 4 |
Baseline Workflow
Every training flow starts with these steps:
- Connect or reconnect:
voltra reconnect --jsonIf the user named a device:
voltra connect <device-id-or-name> --json- Refresh and inspect state:
voltra refresh <scope> --json # e.g. all, epStatus
voltra status --json- After changes, verify:
voltra refresh <scope> --json # e.g. all, epStatus
voltra status --jsonStop and Unload
load and unload work in any training mode — they apply whatever the current mode has configured (weight, resistance band, damper level, isokinetic velocity).
Use these when the user asks to stop or when a control flow must leave the device safe:
voltra unload --json
voltra finish-training --jsonReport the resulting mode and power state from voltra status --json.
Weight Training
Use only when the user asks for weight training:
voltra set-mode <mode> --json # e.g. weightTrainingIf this says to place the device on the dock, stop. Do not retry until the user says the device is on the dock.
Set weight:
voltra set-weight <lbs> --json
voltra set-weight <kg> --unit kg --jsonLoad and unload work in any training mode (see Stop and Unload above).
If loading is blocked, stop and report the reason. Common meanings:
not-docked: the VOLTRA unit is not seated on its dock. Training requires the unit to be physically mounted on the dock.workout-mode-none: enter a training mode first.cable-not-zero: the cable is not at its fully retracted starting position. For safety, the cable must be at zero before the device will engage power.
The cable-not-zero block is a safety mechanism. Do not try custom-offset load, lower-level SDK calls, or repeated retries.
voltra load --force override (advanced, safety-sensitive): Cortex provides --force to bypass the cable-zero check and apply force immediately at the current cable position via apply_force_immediately. This is intended for Isometric holds at a specific joint angle, or advanced setups where returning the cable to zero is not practical. It applies force at a non-zero position — do not use it unless the user explicitly asks for it and understands the risk, and never use it as a workaround for a cable-not-zero block the user did not expect. Default behavior: report cable-not-zero and stop; mention --force only when the user's intent (e.g. Isometric at a set angle) clearly calls for it.
Weight Selection
- Use the user's requested weight when given.
- If the user asks for a light/default weight and gives no value, use 10 lbs.
- Do not choose aggressive loads. Ask the user for a number when the request is ambiguous and physical safety matters.
Weight Training Controls
| Command | Value | Meaning |
|---|---|---|
set-weight <n> | lbs (default) or kg with --unit kg | Target load. Ranges are device-derived; run voltra status to see limits. |
set-chains <n> | lbs (default) or integer percentage with --percent | Extra resistance that increases as the cable extends. Percentage range is 0..300. |
set-eccentric <n> | lbs (default) or integer percentage with --percent | Additional load applied during the eccentric (return) phase. Percentage range is -100..300. |
set-inverse-chains on|off | — | When on, chains resistance decreases as the cable extends (inverse of normal). |
set-chain-mode standard|inverse|mountain | — | Selects the chain curve. Mountain requires device support; inverse and Mountain are mutually exclusive. |
set-auto-unload <s> <r> | seconds, rep count | Auto-unload is triggered by either condition: the user holds still for <s> seconds, OR <r> reps are completed. Both conditions are independent — whichever happens first triggers the unload. Example: set-auto-unload 3 12. |
Examples:
voltra set-chains 125 --percent --json
voltra set-eccentric -50 --percent --json
voltra set-chain-mode mountain --json
voltra set-chain-mode standard --json # clear inverse/MountainCable Zero and Cable Length
The VOLTRA unit has a cable. The cable zero is the cable's fully retracted, starting position. For safety, the device will not allow training to begin unless the cable is at zero (cable-not-zero block in the load step).
Cable length mode lets you preset a new zero position. For example, setting cable length to 10 cm means the cable stops at 10 cm out from the dock — that 10 cm becomes the new zero for subsequent training. This is used to pre-configure starting positions for specific exercises.
voltra check-cable-zero --json # confirm cable is at zero
voltra cable-length <enter|exit> --json # enter or exit cable-length adjustment modeAlways exit cable-length mode before resuming normal training commands.
Resistance Band
Primary load command: set-resistance-band — NOT set-weight. Even if the user says "set weight" or "change load", use set-resistance-band when the device is in resistanceBand mode.
First-version scope: only set-mode resistanceBand and set-resistance-band. All other resistance band sub-commands (set-quick-adjust, set-resistance-experience, set-resistance-mode, set-resistance-curve, set-resistance-by-rom, set-resistance-rom-length, set-resistance-metric) are excluded.
voltra set-mode <mode> --json # e.g. resistanceBand| Command | Value | Meaning |
|---|---|---|
set-resistance-band <n> | lbs (or kg with --unit kg) | Target resistance in pounds. Supports --unit kg for metric input. Example: voltra set-resistance-band 22.5 --unit kg --json |
Do not invent safe default resistance. Ask for a number unless the user explicitly says to use a light/default value.
Damper
Primary control command: set-damper — NOT set-weight. Damper uses level 1-10, not lbs.
First-version scope: only set-mode damper and set-damper. set-damper-metric is excluded.
voltra set-mode <mode> --json # e.g. damper| Command | Value | Meaning |
|---|---|---|
set-damper <n> | 1–10 (dimensionless) | Resistance level. 1 = lightest, 10 = heaviest. |
Report Cortex validation errors as-is; do not guess supported level ranges.
Isokinetic
Primary control command: set-isokinetic-velocity — NOT set-weight. Isokinetic uses velocity in m/s (Cortex converts it to mm/s internally before sending to the device), not lbs.
First-version scope: only set-mode isokinetic and set-isokinetic-velocity. All other isokinetic sub-commands (set-isokinetic-metric, set-isokinetic-eccentric-mode, set-isokinetic-eccentric-velocity, set-isokinetic-eccentric-force, set-isokinetic-eccentric-overload) are excluded.
voltra set-mode <mode> --json # e.g. isokinetic| Command | Value | Meaning |
|---|---|---|
set-isokinetic-velocity <n> | m/s | Target cable velocity for the concentric (pulling) phase. Example: voltra set-isokinetic-velocity 0.5 --json (sent to device as 500 mm/s). |
Ask for velocity values when ambiguous. Do not prescribe max-effort values.
Rowing & Skiing (Cardio)
Source boundary: This section operates the connected VOLTRA device's Cardio Rowing or Skiing mode. It is not a Concept2 controller and does not query Concept2 Logbook. If the user asks for rowing or skiing history without naming a source, use voltra-workout-data to resolve VOLTRA type 8 versus Concept2 Rowing or VOLTRA type 9 versus Concept2 Ski before acting.
Cardio modes share TrainingMode::Rowing (write value 3); rowing vs skiing is selected by the cardio activity parameter (0=rowing, 1=skiing).
Two-step model — setting the mode is not the same as starting a workout:
set-mode rowing|skiing— switches the device into cardio mode and optionally configures resistance / simulated wear level. The device is in cardio mode but no target distance is set yet, so it will not start a timed/distance workout.rowing-start <preset>/skiing-start <preset>— writes the target-distance preset. This is what actually starts the workout. Without this step the device enters cardio mode but has no goal to row/ski toward.
voltra set-mode rowing --resistance 5 --json # enter rowing mode, set resistance
voltra rowing-start 500 --resistance 5 --json # start a 500m rowing workoutArgument order matters — put the positional value first, then flags. rowing-start/skiing-start take a positional <preset> (e.g. 500, just-row); set-mode takes a positional <mode> (e.g. rowing). Write voltra rowing-start 500 --resistance 5 --json, not voltra rowing-start --resistance 5 500 --json. Both orders parse (clap accepts interspersed flags), but preset-first is the documented form and avoids ambiguity with future options.
Presets
<preset> is one of (meters):
| Value | Meaning |
|---|---|
just-row | Just Row — no target distance; device reports 500m splits. |
50 | 50 m |
100 | 100 m |
500 | 500 m |
1000 | 1,000 m |
2000 | 2,000 m |
5000 | 5,000 m |
Commands
| Command | Value | Meaning |
|---|---|---|
set-mode rowing|skiing | — | Enter cardio mode. --resistance <1..10> applies to both (written 0..9). --wear-level <1..10> applies to rowing only (written 0..9, simulated wear). |
rowing-start <preset> | preset enum | Start a rowing workout at the given target distance (or Just Row). Supports --resistance, --wear-level, device selector. |
skiing-start <preset> | preset enum | Start a skiing workout at the given target distance (or Just Row). Supports --resistance, device selector. No --wear-level (skiing has no simulated wear). |
rowing-start / skiing-start handle mode entry internally — if the device is not already in Rowing mode they will switch it, set the cardio activity, apply optional resistance/wear, then write the preset. You do not need to run set-mode first, but running set-mode to pre-configure resistance/wear and then rowing-start <preset> to begin is also valid.
To stop a cardio workout and exit the mode, use voltra finish-training --json (returns to none).
Custom Curve (Cloud API)
Custom curves define non-linear force profiles applied to training modes. Managed via custom-curve commands — no device connection required (pure Cloud API, no BLE/dock/cable-zero).
Same API key resolution as workout commands. For full parameter tables and response shapes (validate/encode/create/update/delete/apply flags, JSON output schema), see references/command-reference.md → Custom Curves.
Commands
| Command | Purpose |
|---|---|
voltra custom-curve list | List all saved custom curves |
voltra custom-curve validate | Validate curve parameters locally without saving |
voltra custom-curve encode | Encode parameters to controls/path without saving |
voltra custom-curve create | Create a new custom curve |
voltra custom-curve update <id> | Update an existing curve |
voltra custom-curve delete <id> | Delete a curve |
voltra custom-curve apply <id> | Pull curve from cloud and send to connected device (requires BLE connection) |
Points Format
Control points use normalized [0, 1] values in semicolon-separated x,y pairs:
"x1,y1;x2,y2;x3,y3;...";separates each point;,separates x and y within each point- x = fraction of ROM (0 = start, 1 = full ROM)
- y = fraction of force range (0 = min-force, 1 = max-force); y increases with force (math/bottom-origin coordinates, no flip)
Wrong formats that will fail: space-delimited, comma-only (no ;), JSON arrays, or absolute units instead of [0,1] fractions.
Example — ROM 80 cm, force 10–150 lbs, 4 control points:
voltra custom-curve create \
--name "My Curve" \
--rom-cm 80 \
--min-force 10 \
--max-force 150 \
--points "0,0.1;0.3,0.4;0.7,0.8;1,1" \
--dry-run --jsonConstraints
--min-forcemust be ≥ 5- At least 2 x,y pairs; x and y must be in [0, 1]
- Name must be unique per account
- Optional:
--cable-offset-cm(cm),--dry-run(preview only)
Unit trap on backend payload: the extraConfig block returned by encode / create has inconsistent units by backend design — cableOffset is in mm, while endCableLength (= ROM) is in cm. Cortex handles the cm ↔ mm conversion at the boundary (user --cable-offset-cm ↔ backend cableOffset). Do not assume cableOffset and endCableLength share a unit when reading the raw payload.
Safe Workflow
voltra custom-curve validate --rom-cm <cm> --min-force <n> --max-force <n> --points "<points>" --json
voltra custom-curve encode --name "<name>" --rom-cm <cm> --min-force <n> --max-force <n> --points "<points>" --json
voltra custom-curve create --name "<name>" --rom-cm <cm> --min-force <n> --max-force <n> --points "<points>" --dry-run --json
# inspect validate response, then drop --dry-run to saveWhat to check in validate response (do not stop at valid: true):
| Field | Meaning |
|---|---|
valid: true | Geometry passed basic range checks. Still inspect the fields below. |
editAllowed: true | Required to be able to create / update this curve. false means the backend will reject writes. |
inflectionCount ≤ maxInflections | Too many inflections will block creation. |
maxSlope ≤ maxSlopeLimit | Slope too steep will block creation. |
minIntervalCm ≥ minIntervalRequiredCm | Control-point spacing below the minimum will block creation. |
issues[] | When valid: false, this lists the specific problems. |
Only proceed to create / update when all of the above are within limits.
Apply (Device Command)
voltra custom-curve apply <curve-id> fetches a saved curve from the cloud and sends it to the connected VOLTRA device. Unlike other custom-curve commands, apply requires an active BLE connection and is routed through the daemon.
voltra custom-curve apply <curve-id> --json
voltra custom-curve apply <curve-id> --temporary --jsonWhat it does:
- Fetches the curve (with its
path/extraConfig) from the cloud API - Switches the device to
customCurve(Professional) training mode - Sends the Bezier curve bytes to the device
- Sets cable offset if configured (or resets it);
--temporarymarks the offset as non-persistent
Flags:
| Flag | Notes |
|---|---|
<curve-id> | Required. Numeric cloud curve ID (from custom-curve list) |
--temporary | Optional. Cable offset is temporary (not saved to device persistent storage) |
--device-id <id> | Optional. Device selector (same as other device commands) |
--timeout-ms <ms> | Optional. Connection/command timeout |
Prerequisites: Device must be connected (voltra connect or voltra reconnect) and not in Twin. If voltra status topology is Twin, unpair first; Cortex rejects Twin custom-curve apply and Twin set-mode customCurve. The command handles mode switching internally — no need to run set-mode first.
Output (JSON):
{
"applied": true,
"curveId": 42,
"name": "My Curve",
"count": 3,
"endX": 1200,
"forceLowerBound": 50,
"forceUpperBound": 250,
"cableOffsetCm": 12
}Guardrails
- Preserve user-provided geometry; do not invent force curves
- Always
validate→encode/--dry-run→create/updatewhen uncertain - Do not manually construct
controlsorpatharrays; the engine generates them from--points deleteis idempotent — deleting a non-existent ID still returns successapplyrequires a live BLE connection; do not call it in cloud-only workflowsapplyandset-mode customCurveare blocked while Twin is paired; unpair first- For full parameter tables and response shapes, see
references/command-reference.md
Guardrails
- Treat Cortex errors and blocked messages as authoritative.
- Never loop on
set-mode,set-weight,load, or equivalent mode-entry commands after a blocked response. - If
voltra loadis blocked by physical risk acceptance, direct the user tovoltra config accept-physical-riskor setVOLTRA_ACCEPT_PHYSICAL_RISK=true. Do not retry until accepted. - Do not use
auto-loadorcancel-auto-load; the current Cortex version should remove them if present. set-auto-unloadis allowed only when the user asks for auto-unload behavior.- Do not add a generic 20% low-battery block for training. Cortex does not enforce a low-battery training gate; if the device rejects a command for low battery, report Cortex's message as-is.
- Do not issue training-control commands unless the user asked for device control.
- Do not treat cached known-device storage as live state.
- Twin training is allowed for weightTraining, resistanceBand, damper, isokinetic, isometric, rowing, and skiing. Never switch Twin into customCurve or sled pull; if Cortex says Twin does not support that mode, unpair instead of retrying.
- If a blocked or failed command persists and the cause is unclear, load
voltra-cli-troubleshootingfor systematic diagnosis. - For device state inspection (battery, mode, connection), use
voltra-device-status. - For first-time setup guidance, use
voltra-getting-started. - For the complete Cortex command reference, see
references/command-reference.md. Read it when you need: Twin pairing/stream (twin status/pair/connect/unpair/stream), daemon management commands (daemon status/stop), logs (logs path/tail/upload/clear), workout History upload queue commands, skills management (skills add/update/list/remove), config & identity (config set-api-key/show/clear-api-key/accept-physical-risk,whoami,doctor,ble doctor), full session / custom-curve parameter tables, or response shapes for any command.
Session Templates (Training Plans)
Session templates define reusable workout training plans. Managed via voltra session * commands — no device connection required (Cloud API only). See references/command-reference.md → Session Templates for full parameter tables.
Scope boundary: This skill owns session template CRUD (create/update/delete/rename/touch/list/detail). For analyzing past workout history (sets, reps, volume, trends), use
voltra-workout-data— do not query workout records from here.
Cortex only supports creating/managing session templates — it does not execute session training. All CRUD, rename, and touch operations happen in the cloud. However, following a session template's exercise sequence for actual training (guided workouts, auto-switching exercises/sets/weight) requires the Beyond+ App, which currently only supports this on iOS. Android does not yet support session training execution.
Mandatory reading before generating session JSON
Before writing any create payload, you MUST read references/session-schema-reference.md. It is the authority for Session Agent Contract v1, including mode-specific ranges, Set override semantics, and cross-field rules. Use references/session-template-snippets.md only after reading the contract. Run voltra session validate --from <path> before upload; backend validation remains final.
Hard rules (silent-failure guards)
These are the rules that look optional but silently break the template. They are non-negotiable when generating session JSON:
- Agent v1 create only allows
blockType=1..2andworkoutMode=1..4. Do not generate other backend modes for this endpoint. - Use consecutive, unique, 1-based
itemGroupPositionanditemDetails[].position. Zero, duplicates, and gaps are invalid. - Always include
sessionConfig,actionModeConfig, andmodeConfig. Blocks, Items, and Sets must be non-empty. - Never emit
nullfor non-null configuration scalars. Generate the canonical defaults from the schema reference even though the current Cortex client materializes omitted defaults before upload. The backend may persist omitted fields asnull, and older Cortex versions do not protect against that. - Never emit
0foreccentricValue,chainsValue, orinverseChainsValue; usenullwhen disabled. Never set both Chains fields positive. At Set level, all-null means inherit; any non-null value overrides the atomic Eccentric/Chains/Inverse Chains group. baseValueis mode-dependent. Weight5..230, Resistance Band15..230, Damper1..10, Isokinetic10..200. ResolveactionIdviavoltra actions list/get; use33only when the user explicitly wants the generic/free exercise fallback.- Direction must match Hand Mode. Bilateral uses
handMode=2anddirection=0; Unilateral useshandMode=1anddirection=1or2. - Drop Set Sets use
tag=2, only with Weight Training, and never inside a Circuit. Target-Reps Unload and Zero-Position Unload must be enabled or disabled together; intermediate Set rest is0. - Twin create payloads use Bilateral direction, disable Assist, and omit Isokinetic Eccentric fields cleared by the Twin flow.
connectionMode=1means two-device execution, not an ordinary bilateral exercise. - Update payload only accepts
title,sessionConfig,blockList,connectionMode. Do not sendaccountRole,label,originSessionId,blockId, oritemIdon update. Send the full rebuilt structure; partial item updates are not supported.
Drop set vs circuit: Drop Set Sets use tag=2 inside a Normal block; a Circuit uses blockType=2 and cannot contain Drop Sets. A session may mix them across separate blocks.
| Command | Purpose |
|---|---|
voltra session list | List all session templates |
voltra session detail <id> | Show full session details |
voltra session validate --from <path> | Validate an Agent v1 create payload locally without an API key or upload |
voltra session create --from <path> | Create from JSON file |
voltra session update <id> --from <path> | Update from JSON file |
voltra session delete <id> --yes | Delete (requires --yes). Add --delete-records to also remove related training records — destructive, confirm with user first. |
voltra session rename <id> <title> | Rename session title |
voltra session touch <id> | Update active timestamp |
All session commands use the standard API key resolution (--api-key flag → $VOLTRA_API_KEY → config).
Actions (Exercises)
Actions are the individual exercises available on VOLTRA. Each action has a numeric actionId used in session templates and workout data. Use voltra actions to discover actions and manage custom actions owned by the current account — no device connection required (Cloud API only).
| Command | Purpose |
|---|---|
voltra actions list | List all actions (id, type, name) |
voltra actions list --type-id 4 | Filter by body part (4=Chest) |
voltra actions list --name curl | Search by name (case-insensitive) |
voltra actions types | List action types (body parts) |
voltra actions groups | Actions grouped by body part |
voltra actions get 33 | Look up action by id |
voltra actions create --name "Bench Press" --type-id 4 | Create a custom action |
voltra actions update 1024 --name "Incline Bench Press" --type-id 4 | Update an owned custom action |
voltra actions delete 1024 --yes | Delete an owned custom action after explicit confirmation |
All action commands support --json for machine-readable output.
Custom action rules
--nameis required for create and update. It must contain 1–50 characters with no leading or trailing whitespace.--type-iddefaults to Functional (10); values outside1..=10also fall back to 10.- Only custom actions owned by the current account can be updated or deleted. Never attempt to mutate built-in actions or another user's actions.
- For rename-only updates, first run
voltra actions get <id> --jsonand explicitly pass the existingtypeId. Omitting--type-idresets the action to Functional (10). - Before delete, explain that referenced workout history will be reset to the default action list and obtain explicit user confirmation. Only then pass
--yes. - Prefer
--jsonand verifycreated/updated/deletedplusactionIdin the result. Do not claim success from command intent alone.
Local Caches: Action catalog results are cached at ~/.voltra/actions.json (24h TTL). Successful custom-action writes invalidate that cache, so the next catalog read fetches current server data. voltra whoami refreshes ~/.voltra/user-preferences.json with the user's preferred units and body measurements; use this before generating sessions so loads/units match the account. voltra session detail uses the action cache to translate actionId → exercise name.
Common Action Types:
| ID | Body Part |
|---|---|
| 1 | Whole Body |
| 2 | Legs |
| 3 | Back |
| 4 | Chest |
| 5 | Shoulders |
| 6 | Biceps |
| 7 | Triceps |
| 8 | Core |
| 9 | Forearms |
| 10 | Functional |
Default Action: actionId=33 (Free Exercises, type=Whole Body) is the fallback when no specific exercise is selected.
VOLTRA I Training Modes
VOLTRA I training modes knowledge base — device overview, all training modes (basic, advanced, cardio), including how VOLTRA Cardio Rowing/Skiing differs from Concept2 Rowing/Ski records, variable resistance features (eccentric, chains, inverse chains), drop sets, circuit training, session templates, twin mode, and Cortex command mapping. **Knowledge and explanation only** — does not issue commands. Use when the user asks what VOLTRA I is, what training modes are available, how VOLTRA and Concept2 cardio activities differ, how eccentric/chains/inverse chains work, what drop sets or circuits are, which mode suits a specific exercise, how session templates work, how twin mode pairing works, or wants a training recommendation. For actually operating the device or writing session/curve payloads, use `voltra-device-training`.
VOLTRA Workout Data
Use when the user asks about VOLTRA or Concept2 Rowing/Ski workout history, recent workouts, sets, reps, training volume, force, velocity, power, fatigue, progress trends, movement performance, or data completeness. Fetch and analyze training data through Cortex. When "rowing" or "skiing" is ambiguous, distinguish VOLTRA cardio records from the matching Concept2 records and combine clearly labeled results when the user wants an overall summary.