{"changelogs":[{"id":"2jrVx49FgOmYaJwgGpFS","title":"Activity export gets a full-featured dialog with new report types","slug":"activity-export-gets-a-full-featured-dialog-with-new-report-types","summary":"The Export button on the User Activities tab now opens a dialog where you choose between three report types — activity log, user totals, and the existing active user profiles. Activity log and user totals are now available to all companies (scoped to their own data). No action is required; existing exports are still accessible through the new dialog.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Activity log export\"},{\"type\":\"text\",\"text\":\": You can now export a CSV with one row per workout session — showing user, date, activity name, time spent, reps, completion percentage, calories burned, and more. Rows are grouped by user and sorted oldest-first.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"User totals export\"},{\"type\":\"text\",\"text\":\": A new summary report gives you one row per user with aggregated stats: total activities, completed count, first/last activity dates, total time, total reps, total calories, and average completion across the selected period.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Export available to all companies\"},{\"type\":\"text\",\"text\":\": Activity log and user totals exports are now available to every company (each company sees only their own data). Previously, export was limited to the KinesteX team.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Export dialog with filters\"},{\"type\":\"text\",\"text\":\": Instead of a one-click download, the Export button now opens a dialog where you can pick the report type, date range (last 7 days, 2 weeks, 30 days, or custom), activity type, and optionally filter by user ID — all pre-filled from your current page selections.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Company scope indicator\"},{\"type\":\"text\",\"text\":\": The dialog shows which company's data you're exporting (\\\"All companies\\\" for KinesteX team vs. your own company name), so there's no ambiguity about what's included.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Spreadsheet safety\"},{\"type\":\"text\",\"text\":\": Exported CSVs now neutralize formula injection in user-supplied fields (user IDs, content titles), preventing accidental code execution when opened in Excel or Google Sheets.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Large export protection\"},{\"type\":\"text\",\"text\":\": Exports are capped at 10,000 rows with a notification explaining the truncation, preventing runaway downloads on high-volume accounts.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Last open date in user totals\"},{\"type\":\"text\",\"text\":\": The user totals report now includes each user's last-open date, matching what you see in the dashboard table.\"}]}]}]}]}","contentHtml":"<h2><strong>New Features</strong></h2><ul><li><p><strong>Activity log export</strong>: You can now export a CSV with one row per workout session — showing user, date, activity name, time spent, reps, completion percentage, calories burned, and more. Rows are grouped by user and sorted oldest-first.</p></li><li><p><strong>User totals export</strong>: A new summary report gives you one row per user with aggregated stats: total activities, completed count, first/last activity dates, total time, total reps, total calories, and average completion across the selected period.</p></li><li><p><strong>Export available to all companies</strong>: Activity log and user totals exports are now available to every company (each company sees only their own data). Previously, export was limited to the KinesteX team.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Export dialog with filters</strong>: Instead of a one-click download, the Export button now opens a dialog where you can pick the report type, date range (last 7 days, 2 weeks, 30 days, or custom), activity type, and optionally filter by user ID — all pre-filled from your current page selections.</p></li><li><p><strong>Company scope indicator</strong>: The dialog shows which company's data you're exporting (\"All companies\" for KinesteX team vs. your own company name), so there's no ambiguity about what's included.</p></li><li><p><strong>Spreadsheet safety</strong>: Exported CSVs now neutralize formula injection in user-supplied fields (user IDs, content titles), preventing accidental code execution when opened in Excel or Google Sheets.</p></li><li><p><strong>Large export protection</strong>: Exports are capped at 10,000 rows with a notification explaining the truncation, preventing runaway downloads on high-volume accounts.</p></li><li><p><strong>Last open date in user totals</strong>: The user totals report now includes each user's last-open date, matching what you see in the dashboard table.</p></li></ul>","coverImage":null,"category":"improved","product":"dashboard","version":"2.33.2","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-07-03T12:59:55.091Z","publishedAt":"2026-07-03T13:00:01.185Z","status":"published","updatedAt":"2026-07-03T13:00:01.185Z"},{"id":"sDJa4QDENpdQpbczE3Tq","title":"Improved Audio Language Switching","slug":"improved-audio-language-switching","summary":"This release fixes spoken coaching audio not following the selected language when the session language changes. No integration code changes are required — no PostMessage events, payloads, or field names changed. Users (or host apps) that switch the language now hear exercise voice audio in the correct language.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(the audio and language layer is shared, so any embed that plays spoken cues — Challenge, Workout, Plan, Personalized Plan, Home Page, Camera, Assessments — is affected)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Language switching now updates spoken audio\"},{\"type\":\"text\",\"text\":\": After changing the session language, exercise voice/coaching audio could keep playing in the previous language, or the new-language audio would never download. The already-loaded audio is now dropped in-memory when the language changes so the new language's speech is fetched and played correctly.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Removed a language-change timing gap\"},{\"type\":\"text\",\"text\":\": A race between loading the new language files and clearing the old speech cache could leave the stale-language audio in place. The active language is now applied immediately on switch, so speech is always requested for the correct language, and the switch is only marked complete once the old cache is successfully cleared (an interrupted switch safely retries on the next launch).\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations<br><em>(the audio and language layer is shared, so any embed that plays spoken cues — Challenge, Workout, Plan, Personalized Plan, Home Page, Camera, Assessments — is affected)</em></p><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Language switching now updates spoken audio</strong>: After changing the session language, exercise voice/coaching audio could keep playing in the previous language, or the new-language audio would never download. The already-loaded audio is now dropped in-memory when the language changes so the new language's speech is fetched and played correctly.</p></li><li><p><strong>Removed a language-change timing gap</strong>: A race between loading the new language files and clearing the old speech cache could leave the stale-language audio in place. The active language is now applied immediately on switch, so speech is always requested for the correct language, and the switch is only marked complete once the old cache is successfully cleared (an interrupted switch safely retries on the next launch).</p></li></ul>","coverImage":null,"category":"improved","product":"client","version":"2.33.4","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-07-03T07:12:58.134Z","publishedAt":"2026-07-03T07:13:05.844Z","status":"published","updatedAt":"2026-07-03T07:13:05.844Z","emailMode":"groups","notifiedGroupIds":["GknqXOUQ9ifeTRKonLDT"],"notificationSentAt":"2026-07-03T07:13:09.491Z","subscriberCountAtSend":4},{"id":"j3cMvJPm7PtSBjxxdRYq","title":"Backend-Driven Assessments & Games, Localized Game Overlays ","slug":"backend-driven-assessments-games-localized-game-overlays","summary":"Assessment and game experiences now pull their content (title, description, steps, video, and motion model) from the updated KinesteX backend, so client-configured and localized experience names now appear consistently — including on the results screen, which now shows the exact backend title, allowing you to customize it in admin dashboard. In-game overlay text is now translated into the user's language and picks up your brand color. No PostMessage events, payloads, or field names changed — integrators do not need to change any code.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Home Page\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Backend-driven experience content\"},{\"type\":\"text\",\"text\":\": Games and assessments (Boxing, balance tests, sit-to-stand, functional reach, tandem stands, ROM shoulder, Balloon Pop, Color Chase, Alien Squat Shooter, and more) now load their title, description, steps, video, and motion model directly from the backend. This lets experience content be managed and localized server-side rather than being fixed in the app.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Consistent experience titles on result screens\"},{\"type\":\"text\",\"text\":\": Assessment and game result screens now display the same backend-provided title shown on the intro screen, instead of a hardcoded built-in name — so a custom or translated experience name now shows end-to-end.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Localized in-game overlays\"},{\"type\":\"text\",\"text\":\": On-screen game text (Round, Time Left, Score, Stage, Shield, Neutralized, Prepare, Mission Complete, praise milestones, and GREAT!/WRONG! feedback) is now translated into the active language across Alien Squat Shooter, Balloon Pop, and Color Chase, rather than always displaying in English.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Brand-themed game overlays\"},{\"type\":\"text\",\"text\":\": Game heads-up display accents now follow your configured brand/theme color instead of a fixed cyan.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Smarter challenge countdown fallback\"},{\"type\":\"text\",\"text\":\": On the challenge intro screen, the host-configured countdown still takes priority, but when none is provided the experience's own countdown (e.g. Boxing's 100s) is now used instead of showing no timer.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Home Page</code>&nbsp;·&nbsp;<code>Assessments</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Backend-driven experience content</strong>: Games and assessments (Boxing, balance tests, sit-to-stand, functional reach, tandem stands, ROM shoulder, Balloon Pop, Color Chase, Alien Squat Shooter, and more) now load their title, description, steps, video, and motion model directly from the backend. This lets experience content be managed and localized server-side rather than being fixed in the app.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Consistent experience titles on result screens</strong>: Assessment and game result screens now display the same backend-provided title shown on the intro screen, instead of a hardcoded built-in name — so a custom or translated experience name now shows end-to-end.</p></li><li><p><strong>Localized in-game overlays</strong>: On-screen game text (Round, Time Left, Score, Stage, Shield, Neutralized, Prepare, Mission Complete, praise milestones, and GREAT!/WRONG! feedback) is now translated into the active language across Alien Squat Shooter, Balloon Pop, and Color Chase, rather than always displaying in English.</p></li><li><p><strong>Brand-themed game overlays</strong>: Game heads-up display accents now follow your configured brand/theme color instead of a fixed cyan.</p></li><li><p><strong>Smarter challenge countdown fallback</strong>: On the challenge intro screen, the host-configured countdown still takes priority, but when none is provided the experience's own countdown (e.g. Boxing's 100s) is now used instead of showing no timer.</p></li></ul><p><br></p>","coverImage":null,"category":"improved","product":"client","version":"2.33.3","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-07-02T17:14:07.854Z","publishedAt":"2026-07-02T17:14:57.122Z","status":"published","updatedAt":"2026-07-02T17:14:57.122Z","emailMode":"groups","notifiedGroupIds":["GknqXOUQ9ifeTRKonLDT","0IkpaHDJ3Sf13IxiIsCm"],"notificationSentAt":"2026-07-02T17:14:59.798Z","subscriberCountAtSend":5},{"id":"C8RmanpoJ8E3eu7OylZV","title":"AI Experiences management added to the admin dashboard","slug":"ai-experiences-management-added-to-the-admin-dashboard","summary":"A new Experiences section is now available under Content Customization, letting KinesteX staff create, edit, and manage AI Experiences (formerly \"Games & Assessments\") and client companies browse and customize copies for their own use. No action is required — the section appears automatically in the sidebar for authorized roles.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Experiences (new)\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Dashboard/Home\"}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Experiences section\"},{\"type\":\"text\",\"text\":\": A new \\\"Experiences\\\" tab on the main Content Customization page and a dedicated editor at its own page let you create, view, edit, and delete AI Experiences — including per-language content (title, description, steps, tips, common mistakes, rest speech) with auto-translate support.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Video upload and preview\"},{\"type\":\"text\",\"text\":\": Upload a video for each experience directly from the editor (drag-and-drop or file picker, 10 MB limit, with upload progress). Videos preview on list cards (first frame shown, plays on hover) and autoplay in the editor — matching the exercises workflow.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Fork-on-edit for client companies\"},{\"type\":\"text\",\"text\":\": When a non-owner company edits a KinesteX library experience, saving automatically creates a private copy linked to the original instead of modifying the shared library item. If the title was not changed, \\\"COPY\\\" is appended so the fork is easy to distinguish.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Active visibility toggle\"},{\"type\":\"text\",\"text\":\": KinesteX staff can toggle whether an experience is visible to client companies via an Active switch in the editor.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Delete confirmation\"},{\"type\":\"text\",\"text\":\": Deleting an experience from the editor now shows a confirmation dialog to prevent accidental loss.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Editor layout matches exercises\"},{\"type\":\"text\",\"text\":\": The experience editor uses a two-column layout — video preview on the left, language selector and content fields on the right — consistent with the exercises editor.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Language chips\"},{\"type\":\"text\",\"text\":\": Switching between content languages uses a chips bar (with add/remove) instead of a dropdown, matching the exercises editor style.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Experience cards show more context\"},{\"type\":\"text\",\"text\":\": Each card in the list now displays the description and a video preview, making it easier to identify experiences at a glance.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Tab layout stability\"},{\"type\":\"text\",\"text\":\": Adding the Experiences tab no longer pushes action buttons off-screen on the Content Customization page.\"}]}]}]}]}","contentHtml":"<blockquote><p><strong>Impacted areas</strong>:&nbsp;<code>Experiences (new)</code>&nbsp;·&nbsp;<code>Dashboard/Home</code></p></blockquote><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Experiences section</strong>: A new \"Experiences\" tab on the main Content Customization page and a dedicated editor at its own page let you create, view, edit, and delete AI Experiences — including per-language content (title, description, steps, tips, common mistakes, rest speech) with auto-translate support.</p></li><li><p><strong>Video upload and preview</strong>: Upload a video for each experience directly from the editor (drag-and-drop or file picker, 10 MB limit, with upload progress). Videos preview on list cards (first frame shown, plays on hover) and autoplay in the editor — matching the exercises workflow.</p></li><li><p><strong>Fork-on-edit for client companies</strong>: When a non-owner company edits a KinesteX library experience, saving automatically creates a private copy linked to the original instead of modifying the shared library item. If the title was not changed, \"COPY\" is appended so the fork is easy to distinguish.</p></li><li><p><strong>Active visibility toggle</strong>: KinesteX staff can toggle whether an experience is visible to client companies via an Active switch in the editor.</p></li><li><p><strong>Delete confirmation</strong>: Deleting an experience from the editor now shows a confirmation dialog to prevent accidental loss.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Editor layout matches exercises</strong>: The experience editor uses a two-column layout — video preview on the left, language selector and content fields on the right — consistent with the exercises editor.</p></li><li><p><strong>Language chips</strong>: Switching between content languages uses a chips bar (with add/remove) instead of a dropdown, matching the exercises editor style.</p></li><li><p><strong>Experience cards show more context</strong>: Each card in the list now displays the description and a video preview, making it easier to identify experiences at a glance.</p></li><li><p><strong>Tab layout stability</strong>: Adding the Experiences tab no longer pushes action buttons off-screen on the Content Customization page.</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"2.33.1","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-07-02T15:42:25.040Z","publishedAt":"2026-07-02T15:42:30.256Z","status":"published","updatedAt":"2026-07-02T15:42:30.256Z","emailMode":"groups","notifiedGroupIds":["GknqXOUQ9ifeTRKonLDT"],"notificationSentAt":"2026-07-02T15:42:33.024Z","subscriberCountAtSend":4},{"id":"nXrU1XrV2i0t0eiGTe0A","title":"New UI Language: Uzbek (Oʻzbekcha)","slug":"new-ui-language-uzbek-ozbekcha","summary":"This release adds Uzbek (uz) as a fully translated interface language. No code or PostMessage changes are required from integrators — if your integration passes a language value, you can now set it to uz to render the entire SDK UI in Uzbek. All existing behavior and message contracts are unchanged.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(the UI language layer is shared across every embedded experience)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Uzbek language support\"},{\"type\":\"text\",\"text\":\": The SDK interface can now be displayed in Uzbek (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Oʻzbekcha\"},{\"type\":\"text\",\"text\":\"), covering all on-screen UI strings. Uzbek renders left-to-right and is selectable anywhere a supported language code is accepted.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations<br><em>(the UI language layer is shared across every embedded experience)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Uzbek language support</strong>: The SDK interface can now be displayed in Uzbek (<code>Oʻzbekcha</code>), covering all on-screen UI strings. Uzbek renders left-to-right and is selectable anywhere a supported language code is accepted.</p></li></ul><p><br></p>","coverImage":null,"category":"new","product":"client","version":"2.33.2","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-07-02T12:57:48.353Z","publishedAt":"2026-07-02T12:57:58.902Z","status":"published","updatedAt":"2026-07-02T12:57:58.902Z","emailMode":"groups","notifiedGroupIds":["GknqXOUQ9ifeTRKonLDT"],"notificationSentAt":"2026-07-02T12:58:01.471Z","subscriberCountAtSend":4},{"id":"wI7u2jFvglZ60D0iwaqf","title":"Uzbek language now available across the admin dashboard","slug":"uzbek-language-now-available-across-the-admin-dashboard","summary":"Uzbek (uz) is now a fully supported content language in the admin dashboard. All language-dependent dropdowns, labels, and translation maps — across exercises, workouts, and plans — include Uzbek values. This also fixes a bug where Dutch was missing from the bulk-edit phrase panel. No action is required from admins; the new language appears automatically.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plans\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Phrase Library\"}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Uzbek language support\"},{\"type\":\"text\",\"text\":\": Uzbek is now available as a content language throughout the dashboard. All body-part labels, starting positions, categories, difficulty levels, contraindication joints, and other language-dependent fields include Uzbek translations. You can select Uzbek in any language filter or dropdown when creating and editing exercises, workouts, and plans.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Dutch missing from bulk-edit phrases\"},{\"type\":\"text\",\"text\":\": Previously, Dutch was not listed as a language option in the bulk-edit phrase panel. This has been fixed — Dutch (and Uzbek) now appear correctly alongside all other supported languages.\"}]}]}]}]}","contentHtml":"<blockquote><p><strong>Impacted areas</strong>:&nbsp;<code>Exercises</code>&nbsp;·&nbsp;<code>Workouts</code>&nbsp;·&nbsp;<code>Plans</code>&nbsp;·&nbsp;<code>Phrase Library</code></p></blockquote><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Uzbek language support</strong>: Uzbek is now available as a content language throughout the dashboard. All body-part labels, starting positions, categories, difficulty levels, contraindication joints, and other language-dependent fields include Uzbek translations. You can select Uzbek in any language filter or dropdown when creating and editing exercises, workouts, and plans.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Dutch missing from bulk-edit phrases</strong>: Previously, Dutch was not listed as a language option in the bulk-edit phrase panel. This has been fixed — Dutch (and Uzbek) now appear correctly alongside all other supported languages.</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"2.33.0","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-07-02T12:51:46.391Z","publishedAt":"2026-07-02T12:51:55.815Z","status":"published","updatedAt":"2026-07-02T12:51:55.815Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-07-02T12:52:13.742Z","subscriberCountAtSend":26},{"id":"KNrHK8oZxA6SK4Ph5roe","title":"Stability release: game audio & freeze fixes, plus startup resilience on restricted WebView storage","slug":"stability-release-game-audio-freeze-fixes-plus-startup-resilience-on-restricted-","summary":"No integration code changes required — there are no PostMessage/API changes. Users on affected devices will simply see the app behave more reliably: motion-game audio stays working, a game that could freeze now completes, and the SDK starts up even when the host WebView blocks device storage.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"All Integrations\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(the storage/startup fix is shared infrastructure used by every integration; the game fixes affect the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\" box-game experiences)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Alien Squat Shooter audio\"},{\"type\":\"text\",\"text\":\": Fixed sound effects stuttering during play and going fully silent after restarting the game. Audio now stays clear for the whole session.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Color Memory game freeze\"},{\"type\":\"text\",\"text\":\": Fixed the game getting stuck on the sequence-display screen from level 4 onward (levels with sub-second memorize times). The game now advances correctly through every level.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Startup on restricted WebView storage\"},{\"type\":\"text\",\"text\":\": The SDK no longer fails to start or crash when the host WebView's on-device storage (used for caching models, images, and videos) is unavailable or blocked — a condition seen on some iOS WKWebView configurations. Caching is now best-effort: when storage is unusable, content and preloaded videos load directly from the network instead of blocking startup.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>All Integrations</code><br><em>(the storage/startup fix is shared infrastructure used by every integration; the game fixes affect the&nbsp;</em><code>Challenge</code><em>&nbsp;box-game experiences)</em></p><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Alien Squat Shooter audio</strong>: Fixed sound effects stuttering during play and going fully silent after restarting the game. Audio now stays clear for the whole session.</p></li><li><p><strong>Color Memory game freeze</strong>: Fixed the game getting stuck on the sequence-display screen from level 4 onward (levels with sub-second memorize times). The game now advances correctly through every level.</p></li><li><p><strong>Startup on restricted WebView storage</strong>: The SDK no longer fails to start or crash when the host WebView's on-device storage (used for caching models, images, and videos) is unavailable or blocked — a condition seen on some iOS WKWebView configurations. Caching is now best-effort: when storage is unusable, content and preloaded videos load directly from the network instead of blocking startup.</p></li></ul>","coverImage":null,"category":"fixed","product":"client","version":"2.33.1","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-07-02T11:43:55.509Z","publishedAt":"2026-07-02T11:44:01.986Z","status":"published","updatedAt":"2026-07-02T11:44:01.986Z"},{"id":"mh8WAlgntFrl4ayvxb8u","title":"SDK Now Launches on Devices With Unavailable Browser Storage","slug":"sdk-now-launches-on-devices-with-unavailable-browser-storage","summary":"Local persistent storage (IndexedDB) is now a best-effort cache instead of a hard startup requirement. On devices where browser storage is unavailable or broken — most commonly some iOS WKWebViews — the SDK now launches normally and streams assets (models, videos, audio) from the network into in-memory session caches, instead of failing to start. This changes one PostMessage behavior: affected devices now emit kinestex_launched instead of an error_occurred. No code changes are required unless you specifically handle that former error.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(shared startup/verification flow plus workout and camera asset caching)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Startup resilience on restrictive WebViews\"},{\"type\":\"text\",\"text\":\": When the device's local storage can't be opened, the SDK now retries briefly (to allow storage that initializes late to appear), then gracefully continues without a persistent cache rather than aborting the launch. Users on affected devices can now complete workouts and assessments; assets simply re-download each session.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Cache is fully optional end-to-end\"},{\"type\":\"text\",\"text\":\": Reads and writes for cached exercise models, videos, and audio cues no longer fail the flow when storage is unavailable — each falls back to a network download plus in-memory reuse for the session.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"App failed to start on devices with broken/unavailable storage\"},{\"type\":\"text\",\"text\":\": Previously, if local storage could not be opened, the SDK stopped launching, showed an error toast, and emitted an \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"error_occurred\"},{\"type\":\"text\",\"text\":\" event — leaving users on affected devices unable to start any experience. The SDK now launches successfully in this scenario.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — unless your host app has special handling for the \\\"Local DB failed to start\\\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"error_occurred\"},{\"type\":\"text\",\"text\":\" message. That specific error is no longer emitted; you will now receive \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"kinestex_launched\"},{\"type\":\"text\",\"text\":\" instead.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Devices with unavailable local storage now launch instead of erroring\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Previously, when local storage (IndexedDB) failed to open, the SDK emitted an \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"error_occurred\"},{\"type\":\"text\",\"text\":\" event and did \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"not\"},{\"type\":\"text\",\"text\":\" emit \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"kinestex_launched\"},{\"type\":\"text\",\"text\":\" — the session never started. Now, the SDK skips the persistent cache, launches normally, and emits \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"kinestex_launched\"},{\"type\":\"text\",\"text\":\" as it does on any healthy device.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations<br><em>(shared startup/verification flow plus workout and camera asset caching)</em></p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>Startup resilience on restrictive WebViews</strong>: When the device's local storage can't be opened, the SDK now retries briefly (to allow storage that initializes late to appear), then gracefully continues without a persistent cache rather than aborting the launch. Users on affected devices can now complete workouts and assessments; assets simply re-download each session.</p></li><li><p><strong>Cache is fully optional end-to-end</strong>: Reads and writes for cached exercise models, videos, and audio cues no longer fail the flow when storage is unavailable — each falls back to a network download plus in-memory reuse for the session.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>App failed to start on devices with broken/unavailable storage</strong>: Previously, if local storage could not be opened, the SDK stopped launching, showed an error toast, and emitted an&nbsp;<code>error_occurred</code>&nbsp;event — leaving users on affected devices unable to start any experience. The SDK now launches successfully in this scenario.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — unless your host app has special handling for the \"Local DB failed to start\"&nbsp;<code>error_occurred</code>&nbsp;message. That specific error is no longer emitted; you will now receive&nbsp;<code>kinestex_launched</code>&nbsp;instead.</p></blockquote><h3><strong>Devices with unavailable local storage now launch instead of erroring</strong></h3><p>Previously, when local storage (IndexedDB) failed to open, the SDK emitted an&nbsp;<code>error_occurred</code>&nbsp;event and did&nbsp;<strong>not</strong>&nbsp;emit&nbsp;<code>kinestex_launched</code>&nbsp;— the session never started. Now, the SDK skips the persistent cache, launches normally, and emits&nbsp;<code>kinestex_launched</code>&nbsp;as it does on any healthy device.</p><p><br></p>","coverImage":null,"category":"improved","product":"client","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-07-02T07:43:20.332Z","version":"2.33.0","publishedAt":"2026-07-02T07:43:42.758Z","status":"published","updatedAt":"2026-07-02T07:43:42.758Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-07-02T07:43:57.998Z","subscriberCountAtSend":26},{"id":"QeJVAu2xA5jwpvpceaXM","title":"Session-Token Authentication + Stability Fixes ","slug":"session-token-authentication-stability-fixes","summary":"Host apps can now launch any KinesteX integration with a short-lived session token instead of embedding a raw API key - the SDK authenticates server-side and never puts the key in the WebView. Existing API-key launches are unchanged and require no action. This release also hardens verification retries and fixes two crashes (Exercise and Statistic screens) that could hit live users.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"All Integrations\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(the verification/auth path is shared across every embed; the crash fixes affect \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\")\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Session-token authentication\"},{\"type\":\"text\",\"text\":\": You can now start the SDK by passing a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"session\"},{\"type\":\"text\",\"text\":\" token in the launch config (URL query param or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"postMessage\"},{\"type\":\"text\",\"text\":\" data) in place of \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"key\"},{\"type\":\"text\",\"text\":\". When a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"session\"},{\"type\":\"text\",\"text\":\" is present, the SDK authenticates against a dedicated session endpoint with an \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"x-session-id\"},{\"type\":\"text\",\"text\":\" header and does not send your API key into the WebView. Provide \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"either\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"key\"},{\"type\":\"text\",\"text\":\" (legacy) \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"or\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"session\"},{\"type\":\"text\",\"text\":\" — exactly one is required. Custom workout/plan/challenge/model content is still correctly scoped to your company via a new backend-returned company identifier, so session-based launches see the same custom content as key-based ones.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Complete documentation and examples: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"link\",\"attrs\":{\"href\":\"https://rowan-handbell-aaa.notion.site/KinesteX-Session-Authentication-Integration-Guide-390853239f84805bb1d3c1e9ef16541b?source=copy_link\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer nofollow\",\"class\":\"text-primary underline underline-offset-[3px] cursor-pointer transition-colors hover:text-primary/80\"}}],\"text\":\"https://rowan-handbell-aaa.notion.site/KinesteX-Session-Authentication-Integration-Guide-390853239f84805bb1d3c1e9ef16541b?source=copy_link\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"More resilient startup\"},{\"type\":\"text\",\"text\":\": Verification now retries transient failures (network drops, 5xx, and 429 rate-limit throttling) with exponential backoff, while failing fast on permanent errors (bad/expired credentials, missing \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"userId\"},{\"type\":\"text\",\"text\":\") instead of retrying pointlessly. Startup is more reliable on flaky networks and recovers cleanly from edge rate limits.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Missing-user reporting on session launches\"},{\"type\":\"text\",\"text\":\": A session-only launch that is missing \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"userId\"},{\"type\":\"text\",\"text\":\" now emits an \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"error_occurred\"},{\"type\":\"text\",\"text\":\" event instead of silently dead-ending, matching the existing behavior for API-key launches.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer profile setup\"},{\"type\":\"text\",\"text\":\": Setup steps inside the profile bottom sheet now align to a consistent gutter, so options no longer hug the sheet edges.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise screen crash (KINESTEX-W-3YP)\"},{\"type\":\"text\",\"text\":\": Fixed a crash on the exercise screen that could occur between exercises or when the exercise list was briefly empty. The screen now waits for a valid active exercise instead of failing.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout summary crash\"},{\"type\":\"text\",\"text\":\": Fixed a crash on the workout Statistic/summary screen when a workout had no translated title, which could interrupt session-stat submission and post-workout navigation.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — session auth is additive. Existing API-key launches continue to work unchanged.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New optional \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"session\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" launch parameter\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The launch/config contract (passed via URL query params or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"postMessage\"},{\"type\":\"text\",\"text\":\" data) now accepts a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"session\"},{\"type\":\"text\",\"text\":\" token as an alternative to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"key\"},{\"type\":\"text\",\"text\":\". Send exactly one.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before (API-key launch — still supported):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"userId\\\": \\\"user-123\\\",\\n  \\\"company\\\": \\\"your-company\\\",\\n  \\\"key\\\": \\\"YOUR_API_KEY\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After (session-token launch — new):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"userId\\\": \\\"user-123\\\",\\n  \\\"company\\\": \\\"your-company\\\",\\n  \\\"session\\\": \\\"SESSION_TOKEN\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required. To adopt session auth, obtain a session token from the backend and pass it as \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"session\"},{\"type\":\"text\",\"text\":\" instead of \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"key\"},{\"type\":\"text\",\"text\":\"; omit \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"key\"},{\"type\":\"text\",\"text\":\" entirely. No SDK-emitted event payloads were renamed, removed, or restructured.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>All Integrations</code>&nbsp;<em>(the verification/auth path is shared across every embed; the crash fixes affect&nbsp;</em><code>Workout</code><em>)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Session-token authentication</strong>: You can now start the SDK by passing a&nbsp;<code>session</code>&nbsp;token in the launch config (URL query param or&nbsp;<code>postMessage</code>&nbsp;data) in place of&nbsp;<code>key</code>. When a&nbsp;<code>session</code>&nbsp;is present, the SDK authenticates against a dedicated session endpoint with an&nbsp;<code>x-session-id</code>&nbsp;header and does not send your API key into the WebView. Provide&nbsp;<strong>either</strong>&nbsp;<code>key</code>&nbsp;(legacy)&nbsp;<strong>or</strong>&nbsp;<code>session</code>&nbsp;— exactly one is required. Custom workout/plan/challenge/model content is still correctly scoped to your company via a new backend-returned company identifier, so session-based launches see the same custom content as key-based ones.</p><p>Complete documentation and examples: <a target=\"_blank\" rel=\"noopener noreferrer nofollow\" class=\"text-primary underline underline-offset-[3px] cursor-pointer transition-colors hover:text-primary/80\" href=\"https://rowan-handbell-aaa.notion.site/KinesteX-Session-Authentication-Integration-Guide-390853239f84805bb1d3c1e9ef16541b?source=copy_link\">https://rowan-handbell-aaa.notion.site/KinesteX-Session-Authentication-Integration-Guide-390853239f84805bb1d3c1e9ef16541b?source=copy_link</a></p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>More resilient startup</strong>: Verification now retries transient failures (network drops, 5xx, and 429 rate-limit throttling) with exponential backoff, while failing fast on permanent errors (bad/expired credentials, missing&nbsp;<code>userId</code>) instead of retrying pointlessly. Startup is more reliable on flaky networks and recovers cleanly from edge rate limits.</p></li><li><p><strong>Missing-user reporting on session launches</strong>: A session-only launch that is missing&nbsp;<code>userId</code>&nbsp;now emits an&nbsp;<code>error_occurred</code>&nbsp;event instead of silently dead-ending, matching the existing behavior for API-key launches.</p></li><li><p><strong>AI Trainer profile setup</strong>: Setup steps inside the profile bottom sheet now align to a consistent gutter, so options no longer hug the sheet edges.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Exercise screen crash (KINESTEX-W-3YP)</strong>: Fixed a crash on the exercise screen that could occur between exercises or when the exercise list was briefly empty. The screen now waits for a valid active exercise instead of failing.</p></li><li><p><strong>Workout summary crash</strong>: Fixed a crash on the workout Statistic/summary screen when a workout had no translated title, which could interrupt session-stat submission and post-workout navigation.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — session auth is additive. Existing API-key launches continue to work unchanged.</p></blockquote><h3><strong>New optional&nbsp;</strong><code>session</code><strong>&nbsp;launch parameter</strong></h3><p>The launch/config contract (passed via URL query params or&nbsp;<code>postMessage</code>&nbsp;data) now accepts a&nbsp;<code>session</code>&nbsp;token as an alternative to&nbsp;<code>key</code>. Send exactly one.</p><p><strong>Before (API-key launch — still supported):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"userId\": \"user-123\",\n  \"company\": \"your-company\",\n  \"key\": \"YOUR_API_KEY\"\n}</code></pre><p><strong>After (session-token launch — new):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"userId\": \"user-123\",\n  \"company\": \"your-company\",\n  \"session\": \"SESSION_TOKEN\"\n}</code></pre><p><strong>Migration</strong>: None required. To adopt session auth, obtain a session token from the backend and pass it as&nbsp;<code>session</code>&nbsp;instead of&nbsp;<code>key</code>; omit&nbsp;<code>key</code>&nbsp;entirely. No SDK-emitted event payloads were renamed, removed, or restructured.</p>","coverImage":null,"category":"new","product":"client","version":"2.32.25","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-07-01T18:25:10.801Z","publishedAt":"2026-07-01T18:25:18.293Z","status":"published","updatedAt":"2026-07-01T18:25:18.293Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-07-01T18:25:36.927Z","subscriberCountAtSend":26},{"id":"qHCrJMmg7wZHL9OhjPML","title":"Phrase Library: static phrase management for KinesteX team","slug":"phrase-library-static-phrase-management-for-kinestex-team","summary":"Static phrases in the Phrase Library now have stricter editing rules — English text is locked to prevent accidental changes that would break voice matching. The KinesteX team can now add and delete static phrases directly from the dashboard. Only the Phrase Library is affected; no other areas changed.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Phrase Library\"}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Static phrase creation and deletion for KinesteX team\"},{\"type\":\"text\",\"text\":\": The KinesteX team can now create new static phrases and delete existing ones directly from the Phrase Library. Previously, adding or removing static phrases was not available in the dashboard.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Locked English text for static phrases\"},{\"type\":\"text\",\"text\":\": English text on static phrases is now read-only. A tooltip explains that the phrase must be deleted and recreated if the English text needs to change. This prevents accidental edits that would break voice matching. Translations for static phrases can still be edited normally on each language tab.\"}]}]}]}]}","contentHtml":"<blockquote><p><strong>Impacted areas</strong>:&nbsp;<code>Phrase Library</code></p></blockquote><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Static phrase creation and deletion for KinesteX team</strong>: The KinesteX team can now create new static phrases and delete existing ones directly from the Phrase Library. Previously, adding or removing static phrases was not available in the dashboard.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Locked English text for static phrases</strong>: English text on static phrases is now read-only. A tooltip explains that the phrase must be deleted and recreated if the English text needs to change. This prevents accidental edits that would break voice matching. Translations for static phrases can still be edited normally on each language tab.</p></li></ul>","coverImage":null,"category":"improved","product":"dashboard","version":"2.32.26","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-30T12:09:31.981Z","publishedAt":"2026-06-30T12:09:44.677Z","status":"published","updatedAt":"2026-06-30T12:09:44.677Z"},{"id":"gvlFtPLqqLhAfhGc4xTO","title":"Trainer screen previews added to the Theme Editor","slug":"trainer-screen-previews-added-to-the-theme-editor","summary":"The Theme Editor now includes a new \"Trainer view\" tab with three preview screens — Profile, Muscle Group, and Fitness Level. This lets admins see and customize how the personal trainer onboarding flow will look with their chosen theme. No action is required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Theme Editor\"}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Trainer view tab in Theme Editor\"},{\"type\":\"text\",\"text\":\": A new \\\"Trainer view\\\" tab is now available in the Theme Editor preview panel, alongside the existing Category, Plan, Workout, and Challenge views.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Profile screen preview\"},{\"type\":\"text\",\"text\":\": See how the trainer's profile setup page looks with your theme — including birth year, gender, weight, and height fields with unit switchers (kg/lb, cm/ft).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Muscle Group screen preview\"},{\"type\":\"text\",\"text\":\": Preview the muscle group selection screen, which shows body part options (Full Body, Upper Body, Lower Body) and a detailed list of individual target muscles with illustrated anatomical icons.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Fitness Level screen preview\"},{\"type\":\"text\",\"text\":\": Preview the fitness assessment screen, which displays push-up and squat video demonstrations with rep-range options, plus a cardio endurance question — all styled with your current theme colors.\"}]}]}]}]}","contentHtml":"<blockquote><p><strong>Impacted areas</strong>:&nbsp;<code>Theme Editor</code></p></blockquote><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Trainer view tab in Theme Editor</strong>: A new \"Trainer view\" tab is now available in the Theme Editor preview panel, alongside the existing Category, Plan, Workout, and Challenge views.</p></li><li><p><strong>Profile screen preview</strong>: See how the trainer's profile setup page looks with your theme — including birth year, gender, weight, and height fields with unit switchers (kg/lb, cm/ft).</p></li><li><p><strong>Muscle Group screen preview</strong>: Preview the muscle group selection screen, which shows body part options (Full Body, Upper Body, Lower Body) and a detailed list of individual target muscles with illustrated anatomical icons.</p></li><li><p><strong>Fitness Level screen preview</strong>: Preview the fitness assessment screen, which displays push-up and squat video demonstrations with rep-range options, plus a cardio endurance question — all styled with your current theme colors.</p></li></ul>","coverImage":null,"category":"new","version":"2.32.25","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-25T09:19:52.848Z","product":"dashboard","publishedAt":"2026-06-25T09:20:02.067Z","status":"published","updatedAt":"2026-06-25T09:20:02.067Z","emailMode":"groups","notifiedGroupIds":["GknqXOUQ9ifeTRKonLDT"],"notificationSentAt":"2026-06-25T09:20:05.540Z","subscriberCountAtSend":4},{"id":"sxX2TzqEmBvRK6qIDPEu","title":"Workout Session Loading — Internal Reliability Improvement","slug":"workout-session-loading-internal-reliability-improvement","summary":"No integration changes required. There are no changes to PostMessage events, payloads, or any user-visible behavior. This release adds internal error reporting when a workout-session detail fails to load, so failures can be diagnosed faster. Integrators do not need to change any code.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Internal improvements only — no integration changes required.\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Added internal observability (error reporting) when a workout-session detail fetch fails, capturing the underlying HTTP status for faster diagnosis. The on-screen experience is unchanged (the same \\\"Unable to load session data.\\\" fallback is shown), and no PostMessage event or payload is affected.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code></p><hr><p>Internal improvements only — no integration changes required.</p><ul><li><p>Added internal observability (error reporting) when a workout-session detail fetch fails, capturing the underlying HTTP status for faster diagnosis. The on-screen experience is unchanged (the same \"Unable to load session data.\" fallback is shown), and no PostMessage event or payload is affected.</p></li></ul>","coverImage":null,"category":"improved","product":"client","version":"2.32.24","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-24T07:05:07.853Z","publishedAt":"2026-06-24T07:05:15.113Z","status":"published","updatedAt":"2026-06-24T07:05:15.113Z","emailMode":"groups","notifiedGroupIds":["GknqXOUQ9ifeTRKonLDT"],"notificationSentAt":"2026-06-24T07:05:17.204Z","subscriberCountAtSend":4},{"id":"o1IBOJyimHGMMNz2yGmu","title":"Pose-Tracking, Workout Pause/Resume,  and Workout Plan Day Counting Fixes ","slug":"pose-tracking-workout-pauseresume-and-workout-plan-day-counting-fixes","summary":"This release contains stability and reliability fixes only. No code changes are required by integrators and there are no changes to any PostMessage events or payloads. Users embedding the SDK get more reliable camera sessions: a very rare crash during pose-model teardown, a potential crash when pausing/resuming a workout, and workouts overcounting days in workout plans are all fixed.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Pose-model teardown crash\"},{\"type\":\"text\",\"text\":\": Fixed a very rare crash that happened once on an older android device which got triggered when the motion-tracking engine was shut down or rebuilt (e.g. switching exercises, leaving the camera, or recovering from a GPU-to-CPU fallback). Teardown is now safe and repeatable, so a failed shutdown no longer cascades into a hard crash of the camera session.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout pause/resume crash\"},{\"type\":\"text\",\"text\":\": Fixed a potential crash that could occur when pausing or resuming a workout at the moment no exercise was active. Pause and resume now handle this state safely.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan overcounting days\"},{\"type\":\"text\",\"text\":\": Fixed a rare case where a user would perform multiple workouts in a day, leading them to count days in a workout plan as completed. \"}]}]}]},{\"type\":\"horizontalRule\"}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Camera</code>&nbsp;·&nbsp;<code>Assessments</code></p><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Pose-model teardown crash</strong>: Fixed a very rare crash that happened once on an older android device which got triggered when the motion-tracking engine was shut down or rebuilt (e.g. switching exercises, leaving the camera, or recovering from a GPU-to-CPU fallback). Teardown is now safe and repeatable, so a failed shutdown no longer cascades into a hard crash of the camera session.</p></li><li><p><strong>Workout pause/resume crash</strong>: Fixed a potential crash that could occur when pausing or resuming a workout at the moment no exercise was active. Pause and resume now handle this state safely.</p></li><li><p><strong>Plan overcounting days</strong>: Fixed a rare case where a user would perform multiple workouts in a day, leading them to count days in a workout plan as completed. </p></li></ul><hr>","coverImage":null,"product":"client","version":"2.32.23","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-23T17:20:49.002Z","category":"fixed","publishedAt":"2026-06-23T17:30:10.040Z","status":"published","updatedAt":"2026-06-23T17:30:10.040Z"},{"id":"xeauSoywsz6kRymOURie","title":"More Reliable Pause Behavior — No Stray Audio During Pause or Backgrounding","slug":"more-reliable-pause-behavior-no-stray-audio-during-pause-or-backgrounding","summary":"This release fixes several pause-related defects so audio and timers behave correctly when a workout is paused or the web view is backgrounded. No integration code changes are required — there are no PostMessage event or payload changes. End users will simply experience quieter, more correct behavior while paused.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"No \\\"step back\\\" cue during a manual pause\"},{\"type\":\"text\",\"text\":\": When a user deliberately taps pause, the \\\"step back / I can't see you\\\" framing cue no longer plays. It still plays for frame-exit and gesture pauses so it can guide the user back into view.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Inactivity prompt no longer fires while paused\"},{\"type\":\"text\",\"text\":\": In instruction mode, the 20-second inactivity prompt (\\\"Repeat after me\\\") no longer counts down during a pause, so it can't trigger mid-pause or the instant a user resumes.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout pauses when backgrounded/minimized\"},{\"type\":\"text\",\"text\":\": Backgrounding or minimizing the web view now pauses the workout — audio, video, and all timers freeze together — and resumes automatically on return. A pause the user set manually stays paused after returning to the app. Previously, audio and timers kept running in the background.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Framing cue restored in the next experience\"},{\"type\":\"text\",\"text\":\": Fixed a case where leaving a workout while paused could silence the framing cue in the next assessment, game, or squat experience.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Camera</code>&nbsp;·&nbsp;<code>Assessments</code>&nbsp;·&nbsp;<code>Challenge</code></p><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>No \"step back\" cue during a manual pause</strong>: When a user deliberately taps pause, the \"step back / I can't see you\" framing cue no longer plays. It still plays for frame-exit and gesture pauses so it can guide the user back into view.</p></li><li><p><strong>Inactivity prompt no longer fires while paused</strong>: In instruction mode, the 20-second inactivity prompt (\"Repeat after me\") no longer counts down during a pause, so it can't trigger mid-pause or the instant a user resumes.</p></li><li><p><strong>Workout pauses when backgrounded/minimized</strong>: Backgrounding or minimizing the web view now pauses the workout — audio, video, and all timers freeze together — and resumes automatically on return. A pause the user set manually stays paused after returning to the app. Previously, audio and timers kept running in the background.</p></li><li><p><strong>Framing cue restored in the next experience</strong>: Fixed a case where leaving a workout while paused could silence the framing cue in the next assessment, game, or squat experience.</p></li></ul>","coverImage":null,"category":"fixed","product":"client","version":"2.32.22","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-18T16:35:21.379Z","publishedAt":"2026-06-18T16:35:31.913Z","status":"published","updatedAt":"2026-06-18T16:35:31.913Z","emailMode":"groups","notifiedGroupIds":["GknqXOUQ9ifeTRKonLDT"],"notificationSentAt":"2026-06-18T16:35:33.760Z","subscriberCountAtSend":2},{"id":"0EZbdugMwGdCYeGW0Fdq","title":"Redesigned AI Trainer Profile Setup & Onboarding","slug":"redesigned-ai-trainer-profile-setup-onboarding","summary":"This release rebuilds the AI Trainer profile-setup (onboarding) experience that users complete before a personalized workout is generated — a cleaner profile card, a tabbed weight/height editor with automatic unit conversion, a birth-year picker, and a streamlined injury flow. No PostMessage events or payloads changed, so integrators do not need to change any code. The only user-visible differences are inside the AI Trainer flow.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"AI Trainer Chat\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Redesigned trainer profile card\"},{\"type\":\"text\",\"text\":\": The profile-setup screen now presents a single consolidated profile card with bottom-sheet editors for each field, replacing the previous row-by-row layout.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Tabbed weight & height editor\"},{\"type\":\"text\",\"text\":\": Weight and height are now edited in a tabbed sheet, and values are automatically converted when the user switches between metric and imperial units (kg/lb, cm/ft).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Birth-year picker\"},{\"type\":\"text\",\"text\":\": Year of birth is now selected through a dedicated picker. The on-screen label was updated from \\\"Year of Birth\\\" to \\\"Birth year\\\".\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Injury detail flow\"},{\"type\":\"text\",\"text\":\": Each selected injury now captures a severity and a preference, shown on a dedicated detail card. Tapping \\\"No injuries\\\" now scrolls directly to the Apply button so the choice is easy to confirm.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Profile edits stay in sync\"},{\"type\":\"text\",\"text\":\": Editing profile details now updates every place the profile is shown in the trainer chat, so both profile cards reflect the same values regardless of which one was edited.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Equipment selection clarity\"},{\"type\":\"text\",\"text\":\": The equipment summary is now labeled \\\"Selected equipment\\\" for clearer wording.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Front/back body diagram\"},{\"type\":\"text\",\"text\":\": The muscle/body diagram can now display only the front or only the back view where appropriate, instead of always showing both.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Localization\"},{\"type\":\"text\",\"text\":\": All new and updated trainer setup labels are translated across the 15 supported languages.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>AI Trainer Chat</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Redesigned trainer profile card</strong>: The profile-setup screen now presents a single consolidated profile card with bottom-sheet editors for each field, replacing the previous row-by-row layout.</p></li><li><p><strong>Tabbed weight &amp; height editor</strong>: Weight and height are now edited in a tabbed sheet, and values are automatically converted when the user switches between metric and imperial units (kg/lb, cm/ft).</p></li><li><p><strong>Birth-year picker</strong>: Year of birth is now selected through a dedicated picker. The on-screen label was updated from \"Year of Birth\" to \"Birth year\".</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Injury detail flow</strong>: Each selected injury now captures a severity and a preference, shown on a dedicated detail card. Tapping \"No injuries\" now scrolls directly to the Apply button so the choice is easy to confirm.</p></li><li><p><strong>Profile edits stay in sync</strong>: Editing profile details now updates every place the profile is shown in the trainer chat, so both profile cards reflect the same values regardless of which one was edited.</p></li><li><p><strong>Equipment selection clarity</strong>: The equipment summary is now labeled \"Selected equipment\" for clearer wording.</p></li><li><p><strong>Front/back body diagram</strong>: The muscle/body diagram can now display only the front or only the back view where appropriate, instead of always showing both.</p></li><li><p><strong>Localization</strong>: All new and updated trainer setup labels are translated across the 15 supported languages.</p></li></ul>","coverImage":null,"category":"improved","product":"client","version":"2.32.21","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-18T13:02:49.774Z","publishedAt":"2026-06-18T13:02:56.048Z","status":"published","updatedAt":"2026-06-18T13:02:56.048Z"},{"id":"fmY5BCgB1EM1ELJ76feI","title":"New Pull-Up Counter Assessment + Correctly-Oriented Form Indicators in Selfie View","slug":"new-pull-up-counter-assessment-correctly-oriented-form-indicators-in-selfie-view","summary":"This release adds a new Pull-Up Counter assessment that clients can launch like any other assessment, and fixes form-correction indicator arrows that were mirrored/misplaced in the default selfie camera view. No code changes are required for existing integrations — the new assessment emits an additive pullups result payload; integrators only need to read the new fields if they choose to launch this assessment.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Pull-Up Counter assessment\"},{\"type\":\"text\",\"text\":\": A new assessment (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessmentType: \\\"pullups\\\"\"},{\"type\":\"text\",\"text\":\") that counts pull-up repetitions in a single set. It guides the user through an on-screen + spoken intro, validates each rep with anti-cheat detection (rejecting arm-only motion and partial reps that don't reach the bar), and produces a results screen with total reps, total time, average time per rep, rejected-attempt count, and a strength tier (e.g. \\\"Excellent / Good baseline / Developing strength\\\"). Available in all 16 supported languages.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Form-correction arrows in selfie view\"},{\"type\":\"text\",\"text\":\": Visual indicator arrows (the form-guidance overlays drawn on the live camera) were horizontally flipped and offset incorrectly when the camera was mirrored — which is the default selfie orientation. They now render with the correct orientation, position, and size for any camera-based exercise. This affects every integration that runs camera exercises with form indicators.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New result payload variant for the Pull-Up assessment\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The existing assessment events (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_completed\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_overview\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_restart\"},{\"type\":\"text\",\"text\":\") now support a new \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessmentType\"},{\"type\":\"text\",\"text\":\" value, \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"pullups\\\"\"},{\"type\":\"text\",\"text\":\", with its own result fields. No existing event, field, or value was renamed or removed.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New payload (e.g. on \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_completed\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"assessment_completed\\\",\\n  \\\"assessmentType\\\": \\\"pullups\\\",\\n  \\\"date\\\": \\\"2026-06-17\\\",\\n  \\\"time\\\": 42.3,\\n  \\\"reps\\\": 8,\\n  \\\"invalidAttempts\\\": 2,\\n  \\\"avgRepTimeMs\\\": 1450\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Field reference (new for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"pullups\"},{\"type\":\"text\",\"text\":\"):\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"reps\"},{\"type\":\"text\",\"text\":\" (number) — valid pull-up repetitions counted.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"time\"},{\"type\":\"text\",\"text\":\" (number, seconds) — total assessment duration.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"invalidAttempts\"},{\"type\":\"text\",\"text\":\" (number) — reps rejected by the anti-cheat checks (e.g. arm-only motion, not reaching the bar).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"avgRepTimeMs\"},{\"type\":\"text\",\"text\":\" (number, milliseconds, integer) — average time per valid rep.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required. If you display or store assessment results, add a handler for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessmentType === \\\"pullups\\\"\"},{\"type\":\"text\",\"text\":\" to surface the fields above. All other assessment types and their payloads are unaffected.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Camera</code>&nbsp;·&nbsp;<code>Assessments</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Pull-Up Counter assessment</strong>: A new assessment (<code>assessmentType: \"pullups\"</code>) that counts pull-up repetitions in a single set. It guides the user through an on-screen + spoken intro, validates each rep with anti-cheat detection (rejecting arm-only motion and partial reps that don't reach the bar), and produces a results screen with total reps, total time, average time per rep, rejected-attempt count, and a strength tier (e.g. \"Excellent / Good baseline / Developing strength\"). Available in all 16 supported languages.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Form-correction arrows in selfie view</strong>: Visual indicator arrows (the form-guidance overlays drawn on the live camera) were horizontally flipped and offset incorrectly when the camera was mirrored — which is the default selfie orientation. They now render with the correct orientation, position, and size for any camera-based exercise. This affects every integration that runs camera exercises with form indicators.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><h3><strong>New result payload variant for the Pull-Up assessment</strong></h3><p>The existing assessment events (<code>assessment_completed</code>,&nbsp;<code>assessment_overview</code>,&nbsp;<code>assessment_restart</code>) now support a new&nbsp;<code>assessmentType</code>&nbsp;value,&nbsp;<code>\"pullups\"</code>, with its own result fields. No existing event, field, or value was renamed or removed.</p><p><strong>New payload (e.g. on&nbsp;</strong><code>assessment_completed</code><strong>):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"assessment_completed\",\n  \"assessmentType\": \"pullups\",\n  \"date\": \"2026-06-17\",\n  \"time\": 42.3,\n  \"reps\": 8,\n  \"invalidAttempts\": 2,\n  \"avgRepTimeMs\": 1450\n}</code></pre><p>Field reference (new for&nbsp;<code>pullups</code>):</p><ul><li><p><code>reps</code>&nbsp;(number) — valid pull-up repetitions counted.</p></li><li><p><code>time</code>&nbsp;(number, seconds) — total assessment duration.</p></li><li><p><code>invalidAttempts</code>&nbsp;(number) — reps rejected by the anti-cheat checks (e.g. arm-only motion, not reaching the bar).</p></li><li><p><code>avgRepTimeMs</code>&nbsp;(number, milliseconds, integer) — average time per valid rep.</p></li></ul><p><strong>Migration</strong>: None required. If you display or store assessment results, add a handler for&nbsp;<code>assessmentType === \"pullups\"</code>&nbsp;to surface the fields above. All other assessment types and their payloads are unaffected.</p>","coverImage":null,"category":"new","product":"client","version":"2.32.20","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-17T14:34:04.314Z","publishedAt":"2026-06-17T14:36:10.395Z","status":"published","updatedAt":"2026-06-17T14:36:10.395Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-06-17T14:36:25.168Z","subscriberCountAtSend":25},{"id":"6Kk774JPpI55yl9xtJst","title":"Fewer false \"out of frame\" warnings during floor & lying exercises","slug":"fewer-false-out-of-frame-warnings-during-floor-lying-exercises","summary":"No integrator action required — there are no PostMessage or API changes in this release. Users will see fewer incorrect \"step back / out of frame\" prompts during regular exercises, especially floor and lying-down movements where wrists or feet naturally sit near the edge of the camera view. Assessments are unaffected and keep their strict full-body framing.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Smarter body framing\"},{\"type\":\"text\",\"text\":\": The camera now applies two framing modes. Assessments still require the full body (head, ankles, and all limbs) to stay inside the frame for accurate scoring. All other exercises now use a relaxed check that only needs the head, elbows, and knees in view — so floor, seated, and lying-down movements no longer trigger out-of-frame warnings just because a hand or foot reaches the edge.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Camera</code>&nbsp;·&nbsp;<code>Assessments</code></p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>Smarter body framing</strong>: The camera now applies two framing modes. Assessments still require the full body (head, ankles, and all limbs) to stay inside the frame for accurate scoring. All other exercises now use a relaxed check that only needs the head, elbows, and knees in view — so floor, seated, and lying-down movements no longer trigger out-of-frame warnings just because a hand or foot reaches the edge.</p></li></ul>","coverImage":null,"category":"improved","product":"client","version":"2.32.19","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-17T09:33:45.257Z","publishedAt":"2026-06-17T09:33:50.069Z","status":"published","updatedAt":"2026-06-17T09:33:50.069Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-06-17T09:34:03.646Z","subscriberCountAtSend":25},{"id":"DRC9U8ZWDcoO0xfsOTsj","coverImage":null,"category":"improved","product":"client","version":"2.32.18","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-13T15:43:13.326Z","summary":"No integration code changes are required — there are no PostMessage API changes in this release. End users will see more resilient camera startup (the SDK now self-heals corrupt pose-engine caches before showing an error), workout exercises that auto-advance when a user goes idle, and form-correction voice cues that escalate in a defined order instead of playing at random.","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Camera</code>&nbsp;·&nbsp;<code>Assessments</code><br><em>(Home Page is not impacted — these changes only affect camera/pose-based experiences.)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Inactivity auto-skip (Workout)</strong>: Rep-based workout exercises now auto-advance when no movement is detected for 30 seconds. The user is coached first (on-screen guidance at ~20s), then sees the same 5-second recovery warning overlay before the exercise is skipped. It will not trigger during rest periods, paused states, or while a user is correctly holding a timed position.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Pose-engine auto-recovery (Camera)</strong>: When the pose engine fails to start (e.g. a truncated cached model/WASM file on a flaky network or embedded WebView), the SDK now automatically clears the potentially corrupt cached data and invites the user to retry — giving a plain retry a real chance to succeed. It only escalates to a \"reach out for support\" message if a retry running on freshly downloaded files also fails. Previously users could get stuck on the support message until they manually cleared site data.</p></li><li><p><strong>Sequential mistake coaching (all camera experiences)</strong>: Form-correction voice cues for a given mistake now play in their defined order and advance on each repeat of the same mistake (wrapping at the end), instead of being chosen randomly. This produces consistent, escalating coaching when a user keeps repeating the same error.</p></li><li><p><strong>Localized startup errors</strong>: Pose-engine startup error messages are now translated across all supported languages.</p></li></ul>","title":"Pose-Engine Auto-Recovery, Inactivity Auto-Skip, and Sequential Mistake Coaching","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(Home Page is not impacted — these changes only affect camera/pose-based experiences.)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Inactivity auto-skip (Workout)\"},{\"type\":\"text\",\"text\":\": Rep-based workout exercises now auto-advance when no movement is detected for 30 seconds. The user is coached first (on-screen guidance at ~20s), then sees the same 5-second recovery warning overlay before the exercise is skipped. It will not trigger during rest periods, paused states, or while a user is correctly holding a timed position.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Pose-engine auto-recovery (Camera)\"},{\"type\":\"text\",\"text\":\": When the pose engine fails to start (e.g. a truncated cached model/WASM file on a flaky network or embedded WebView), the SDK now automatically clears the potentially corrupt cached data and invites the user to retry — giving a plain retry a real chance to succeed. It only escalates to a \\\"reach out for support\\\" message if a retry running on freshly downloaded files also fails. Previously users could get stuck on the support message until they manually cleared site data.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Sequential mistake coaching (all camera experiences)\"},{\"type\":\"text\",\"text\":\": Form-correction voice cues for a given mistake now play in their defined order and advance on each repeat of the same mistake (wrapping at the end), instead of being chosen randomly. This produces consistent, escalating coaching when a user keeps repeating the same error.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Localized startup errors\"},{\"type\":\"text\",\"text\":\": Pose-engine startup error messages are now translated across all supported languages.\"}]}]}]}]}","slug":"pose-engine-auto-recovery-inactivity-auto-skip-and-sequential-mistake-coaching","status":"published","publishedAt":"2026-06-13T16:14:15.824Z","updatedAt":"2026-06-13T16:14:15.824Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-06-13T16:14:42.753Z","subscriberCountAtSend":21},{"id":"E59WBo9cyvWAw2PI5RB0","title":"Workout Detail Page Refresh + Richer Saved Session Records","slug":"workout-detail-page-refresh-richer-saved-session-records","summary":"This release reworks the Workout detail screen layout (equipment moved into the overview area, a persistently visible Start button on desktop) and enriches the workout session records saved to the backend with the workout's image URL and difficulty level. No PostMessage events changed — integrators do not need to modify any message-handling code. The new fields only affect data persisted to the session API (and surfaced in session history list/detail responses).","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Home Page\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(these flows all use the shared workout detail screen and the workout-session save path)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout detail layout\"},{\"type\":\"text\",\"text\":\": The \\\"You will need\\\" equipment list now sits inline within the workout overview area using compact thumbnails, instead of as a separate full-width section further down the page. This keeps key workout info above the fold.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Start button always in view (desktop)\"},{\"type\":\"text\",\"text\":\": On larger/desktop viewports the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Start\"},{\"type\":\"text\",\"text\":\" button now stays pinned in the viewport over the overview column as the page scrolls, so users can launch the workout without scrolling back. RTL layouts keep the button on the correct side.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Cleaner muscles section\"},{\"type\":\"text\",\"text\":\": Removed a redundant heading above the muscle-effects diagram on the detail screen.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (Saved Session Records)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — this is additive and does not affect any PostMessage event.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout session records now include image URL and difficulty\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"When a workout session is saved, the persisted session record (and the session \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"history list/detail\"},{\"type\":\"text\",\"text\":\" responses returned from the workout-sessions API) now include two additional optional fields: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"content_image_url\"},{\"type\":\"text\",\"text\":\" (the workout's preview image) and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"content_difficulty\"},{\"type\":\"text\",\"text\":\" (the workout's difficulty level). Both fields are omitted when the underlying workout has no image or difficulty value, so consumers must treat them as optional.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before (saved session record):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"integration_option\\\": \\\"workout\\\",\\n  \\\"content_id\\\": \\\"abc123\\\",\\n  \\\"content_title\\\": \\\"Full Body Burn\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After (saved session record):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"integration_option\\\": \\\"workout\\\",\\n  \\\"content_id\\\": \\\"abc123\\\",\\n  \\\"content_title\\\": \\\"Full Body Burn\\\",\\n  \\\"content_image_url\\\": \\\"https://.../workout-preview.jpg\\\",\\n  \\\"content_difficulty\\\": \\\"Intermediate\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Note on PostMessage\"},{\"type\":\"text\",\"text\":\": The \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_session_saved\"},{\"type\":\"text\",\"text\":\" PostMessage event is \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"unchanged\"},{\"type\":\"text\",\"text\":\" — its payload still carries only \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"session_id\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_title\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"accuracy_score\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"efficiency_score\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"completion_percentage\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"completed_reps_count\"},{\"type\":\"text\",\"text\":\", and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"calories_burned\"},{\"type\":\"text\",\"text\":\". The new \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"content_image_url\"},{\"type\":\"text\",\"text\":\" / \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"content_difficulty\"},{\"type\":\"text\",\"text\":\" fields are \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"not\"},{\"type\":\"text\",\"text\":\" included in that event; they are only available via the saved session record / session-history API.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required. If you read workout session history from the API, you may optionally start consuming \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"content_image_url\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"content_difficulty\"},{\"type\":\"text\",\"text\":\" (both optional/nullable).\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Home Page</code><br><em>(these flows all use the shared workout detail screen and the workout-session save path)</em></p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>Workout detail layout</strong>: The \"You will need\" equipment list now sits inline within the workout overview area using compact thumbnails, instead of as a separate full-width section further down the page. This keeps key workout info above the fold.</p></li><li><p><strong>Start button always in view (desktop)</strong>: On larger/desktop viewports the&nbsp;<strong>Start</strong>&nbsp;button now stays pinned in the viewport over the overview column as the page scrolls, so users can launch the workout without scrolling back. RTL layouts keep the button on the correct side.</p></li><li><p><strong>Cleaner muscles section</strong>: Removed a redundant heading above the muscle-effects diagram on the detail screen.</p></li></ul><h2><strong>API Changes (Saved Session Records)</strong></h2><blockquote><p><strong>Action Required</strong>: No — this is additive and does not affect any PostMessage event.</p></blockquote><h3><strong>Workout session records now include image URL and difficulty</strong></h3><p>When a workout session is saved, the persisted session record (and the session&nbsp;<strong>history list/detail</strong>&nbsp;responses returned from the workout-sessions API) now include two additional optional fields:&nbsp;<code>content_image_url</code>&nbsp;(the workout's preview image) and&nbsp;<code>content_difficulty</code>&nbsp;(the workout's difficulty level). Both fields are omitted when the underlying workout has no image or difficulty value, so consumers must treat them as optional.</p><p><strong>Before (saved session record):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"integration_option\": \"workout\",\n  \"content_id\": \"abc123\",\n  \"content_title\": \"Full Body Burn\"\n}</code></pre><p><strong>After (saved session record):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"integration_option\": \"workout\",\n  \"content_id\": \"abc123\",\n  \"content_title\": \"Full Body Burn\",\n  \"content_image_url\": \"https://.../workout-preview.jpg\",\n  \"content_difficulty\": \"Intermediate\"\n}</code></pre><p><strong>Note on PostMessage</strong>: The&nbsp;<code>workout_session_saved</code>&nbsp;PostMessage event is&nbsp;<strong>unchanged</strong>&nbsp;— its payload still carries only&nbsp;<code>session_id</code>,&nbsp;<code>workout_title</code>,&nbsp;<code>accuracy_score</code>,&nbsp;<code>efficiency_score</code>,&nbsp;<code>completion_percentage</code>,&nbsp;<code>completed_reps_count</code>, and&nbsp;<code>calories_burned</code>. The new&nbsp;<code>content_image_url</code>&nbsp;/&nbsp;<code>content_difficulty</code>&nbsp;fields are&nbsp;<strong>not</strong>&nbsp;included in that event; they are only available via the saved session record / session-history API.</p><p><strong>Migration</strong>: None required. If you read workout session history from the API, you may optionally start consuming&nbsp;<code>content_image_url</code>&nbsp;and&nbsp;<code>content_difficulty</code>&nbsp;(both optional/nullable).</p>","coverImage":null,"category":"improved","product":"client","version":"2.32.17","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-10T19:10:34.407Z","publishedAt":"2026-06-10T19:12:20.679Z","status":"published","updatedAt":"2026-06-10T19:12:20.679Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-06-10T19:12:34.259Z","subscriberCountAtSend":25},{"id":"M9sl7jEEADNCqvAIHbdR","title":"Fix exercise and workout editing: mode switching, input fields, and feature toggling","slug":"fix-exercise-and-workout-editing-mode-switching-input-fields-and-feature-togglin","summary":"Multiple bugs in exercise and workout editing are fixed. Switching between reps and countdown mode in workouts no longer leaves stale values behind, exercise input fields (break period, reset) now properly accept zero and can be cleared, and toggling off hold position, break period, or reset now fully removes them — including their voice phrases across all languages. Affects all users who edit exercises or workouts.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout reps/countdown mode switching\"},{\"type\":\"text\",\"text\":\": Switching an exercise between reps mode and countdown mode now properly clears the unused value. Previously, switching modes left the old value stored, so both reps and countdown could appear at the same time, and reps could not be restored once a countdown was set.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Break period inputs now accept zero and can be cleared\"},{\"type\":\"text\",\"text\":\": The break period duration and next position fields now correctly accept 0 as a valid entry and can be cleared to empty. Previously, entering 0 would silently revert to a default value (5000 ms for duration, -1 for next position), and editing one field would force the other to its default.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Reset inputs now accept zero and can be cleared\"},{\"type\":\"text\",\"text\":\": The reset duration and reset delay fields now correctly accept 0 and can be cleared, matching the fix above. Previously, entering 0 would silently revert to defaults (5000 ms for duration, 3000 ms for delay).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Reset settings preserved when re-enabling across languages\"},{\"type\":\"text\",\"text\":\": Re-enabling reset on a language that already had a reset phrase now correctly initializes the duration and delay settings. Previously, these values could be silently dropped when saving.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Hold position can now be turned off\"},{\"type\":\"text\",\"text\":\": Disabling hold position on an existing exercise sequence now fully removes it. Previously, once hold position was enabled, it could never be turned off — the saved value persisted regardless of the toggle state.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Break period and reset can now be fully disabled\"},{\"type\":\"text\",\"text\":\": Disabling break period or reset on an existing exercise sequence now fully removes them, matching the hold position fix above. Previously, the saved values persisted even after toggling these features off.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Voice phrases removed across all languages when feature is disabled\"},{\"type\":\"text\",\"text\":\": When hold position, break period, or reset is turned off, the associated voice phrases are now removed for all languages — not just the currently selected one. Previously, phrases in other languages would continue playing even though the feature was visually disabled.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Switching to timer-based mode clears hold position\"},{\"type\":\"text\",\"text\":\": When changing an exercise from rep-based to timer-based mode, hold position is now automatically cleared since it only applies to rep-based exercises. Previously, hold position remained active after the switch.\"}]}]}]}]}","contentHtml":"<blockquote><p><strong>Impacted areas</strong>:&nbsp;<code>Exercises</code>&nbsp;·&nbsp;<code>Workouts</code></p></blockquote><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Workout reps/countdown mode switching</strong>: Switching an exercise between reps mode and countdown mode now properly clears the unused value. Previously, switching modes left the old value stored, so both reps and countdown could appear at the same time, and reps could not be restored once a countdown was set.</p></li><li><p><strong>Break period inputs now accept zero and can be cleared</strong>: The break period duration and next position fields now correctly accept 0 as a valid entry and can be cleared to empty. Previously, entering 0 would silently revert to a default value (5000 ms for duration, -1 for next position), and editing one field would force the other to its default.</p></li><li><p><strong>Reset inputs now accept zero and can be cleared</strong>: The reset duration and reset delay fields now correctly accept 0 and can be cleared, matching the fix above. Previously, entering 0 would silently revert to defaults (5000 ms for duration, 3000 ms for delay).</p></li><li><p><strong>Reset settings preserved when re-enabling across languages</strong>: Re-enabling reset on a language that already had a reset phrase now correctly initializes the duration and delay settings. Previously, these values could be silently dropped when saving.</p></li><li><p><strong>Hold position can now be turned off</strong>: Disabling hold position on an existing exercise sequence now fully removes it. Previously, once hold position was enabled, it could never be turned off — the saved value persisted regardless of the toggle state.</p></li><li><p><strong>Break period and reset can now be fully disabled</strong>: Disabling break period or reset on an existing exercise sequence now fully removes them, matching the hold position fix above. Previously, the saved values persisted even after toggling these features off.</p></li><li><p><strong>Voice phrases removed across all languages when feature is disabled</strong>: When hold position, break period, or reset is turned off, the associated voice phrases are now removed for all languages — not just the currently selected one. Previously, phrases in other languages would continue playing even though the feature was visually disabled.</p></li><li><p><strong>Switching to timer-based mode clears hold position</strong>: When changing an exercise from rep-based to timer-based mode, hold position is now automatically cleared since it only applies to rep-based exercises. Previously, hold position remained active after the switch.</p></li></ul>","coverImage":null,"category":"fixed","product":"dashboard","version":"2.32.11","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-10T18:59:18.945Z","publishedAt":"2026-06-10T18:59:25.015Z","status":"published","updatedAt":"2026-06-10T18:59:25.015Z"},{"id":"nqQSkoeRQaHUus2wAf7T","title":"Balloon Pop: Bigger Balloons on Phones, Easier Popping & Smoother Motion","slug":"balloon-pop-bigger-balloons-on-phones-easier-popping-smoother-motion","summary":"This release polishes the Balloon Pop game experience. No integration code changes are required and there are no PostMessage/API changes. End users on phones get noticeably larger, easier-to-pop balloons, and the game now moves smoothly and consistently across all device refresh rates.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Games\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Larger balloons on phones\"},{\"type\":\"text\",\"text\":\": Balloons now render 2× larger on phone screens (iPhone/Android phones), where the previous adaptive size felt too small. Tablets and desktop keep the existing adaptive sizing.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"More forgiving popping\"},{\"type\":\"text\",\"text\":\": The pop hit-zone is now a generous margin around each balloon and scales with the balloon on every device, so popping feels reliable even with noisy hand tracking. The hit-zone also follows the balloon's on-screen wobble, so it stays centered on what the player actually sees.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Smoother, consistent motion\"},{\"type\":\"text\",\"text\":\": Balloon drift, idle wobble, pop/fade animations, and confetti now move at the same real-time speed regardless of display refresh rate (60Hz, 120Hz, etc.). Motion no longer judders, and brief stalls (e.g. switching browser tabs) no longer cause balloons to jump.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"No more mass-pop on game start\"},{\"type\":\"text\",\"text\":\": Fixed an issue where balloons could all be popped at once in the moment before the game canvas finished measuring its size. Hit detection now waits until the play area is sized.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Games</code></p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>Larger balloons on phones</strong>: Balloons now render 2× larger on phone screens (iPhone/Android phones), where the previous adaptive size felt too small. Tablets and desktop keep the existing adaptive sizing.</p></li><li><p><strong>More forgiving popping</strong>: The pop hit-zone is now a generous margin around each balloon and scales with the balloon on every device, so popping feels reliable even with noisy hand tracking. The hit-zone also follows the balloon's on-screen wobble, so it stays centered on what the player actually sees.</p></li><li><p><strong>Smoother, consistent motion</strong>: Balloon drift, idle wobble, pop/fade animations, and confetti now move at the same real-time speed regardless of display refresh rate (60Hz, 120Hz, etc.). Motion no longer judders, and brief stalls (e.g. switching browser tabs) no longer cause balloons to jump.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>No more mass-pop on game start</strong>: Fixed an issue where balloons could all be popped at once in the moment before the game canvas finished measuring its size. Hit detection now waits until the play area is sized.</p></li></ul>","coverImage":null,"category":"improved","product":"client","version":"2.32.16","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-10T16:36:18.450Z","publishedAt":"2026-06-10T16:36:24.436Z","status":"published","updatedAt":"2026-06-10T16:36:24.436Z"},{"id":"gRGueirN3CjaQSs1oy2y","title":"Smarter AI Trainer Chat: One-Tap Yes/No Replies, Accurate Workout Duration & Instant Profile Updates","slug":"smarter-ai-trainer-chat-one-tap-yesno-replies-accurate-workout-duration-instant-","summary":"This release improves the AI Trainer (chat coach) experience. No code changes are required for integrators — all existing PostMessage events keep the same names and payloads. End users get one-tap \"Yes/No\" answers when the coach asks a question, a workout-duration badge that now matches what the coach says in chat, and profile edits made in chat that take effect immediately. The trainer_schedule_next_workout event now fires more reliably (only after a real plan), with an unchanged payload.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": AI Trainer (Chat Coach)\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(This release is scoped to the AI Trainer feature; none of the other embeddable integrations changed.)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"One-tap Yes/No replies\"},{\"type\":\"text\",\"text\":\": When the coach asks the user a question (e.g. confirming a profile change), the chat now shows quick \\\"Yes\\\" / \\\"No\\\" reply chips. These appear even before a workout plan exists and, on mobile, without the user needing to tap into the input box first.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Accurate workout duration\"},{\"type\":\"text\",\"text\":\": The workout duration badge now uses the coach's computed duration, so the number on the card matches the time the coach states in its chat message. If that value isn't available, it falls back to the previous on-device estimate.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Instant profile updates from chat\"},{\"type\":\"text\",\"text\":\": When a user changes their profile through chat (for example, \\\"I weigh 70 kg now\\\"), the settings screen reflects the change immediately, without needing a reload.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Cleaner clarifying-question handling\"},{\"type\":\"text\",\"text\":\": When the coach asks a clarifying question or makes no change to the plan, the chat keeps the coach's message readable (the plan panel no longer expands over it) and an already-confirmed workout is left untouched instead of being reset.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — no event names, fields, or payload shapes changed.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"trainer_schedule_next_workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" — improved firing reliability (payload unchanged)\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The schedule-notification event now fires only once the coach produces a real workout plan. Turns where the coach is still asking a clarifying question (or made no change) no longer trigger a premature notification. The event type and payload are identical to before.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before and After (unchanged):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"trainer_schedule_next_workout\\\", \\\"scheduledFor\\\": \\\"2026-06-11T09:00:00.000Z\\\" }\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required. Integrators listening for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"trainer_schedule_next_workout\"},{\"type\":\"text\",\"text\":\" will simply receive it at the correct moment (after a plan is ready) rather than on intermediate clarifying turns.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: AI Trainer (Chat Coach)<br><em>(This release is scoped to the AI Trainer feature; none of the other embeddable integrations changed.)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>One-tap Yes/No replies</strong>: When the coach asks the user a question (e.g. confirming a profile change), the chat now shows quick \"Yes\" / \"No\" reply chips. These appear even before a workout plan exists and, on mobile, without the user needing to tap into the input box first.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Accurate workout duration</strong>: The workout duration badge now uses the coach's computed duration, so the number on the card matches the time the coach states in its chat message. If that value isn't available, it falls back to the previous on-device estimate.</p></li><li><p><strong>Instant profile updates from chat</strong>: When a user changes their profile through chat (for example, \"I weigh 70 kg now\"), the settings screen reflects the change immediately, without needing a reload.</p></li><li><p><strong>Cleaner clarifying-question handling</strong>: When the coach asks a clarifying question or makes no change to the plan, the chat keeps the coach's message readable (the plan panel no longer expands over it) and an already-confirmed workout is left untouched instead of being reset.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — no event names, fields, or payload shapes changed.</p></blockquote><h3><code>trainer_schedule_next_workout</code><strong>&nbsp;— improved firing reliability (payload unchanged)</strong></h3><p>The schedule-notification event now fires only once the coach produces a real workout plan. Turns where the coach is still asking a clarifying question (or made no change) no longer trigger a premature notification. The event type and payload are identical to before.</p><p><strong>Before and After (unchanged):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{ \"type\": \"trainer_schedule_next_workout\", \"scheduledFor\": \"2026-06-11T09:00:00.000Z\" }</code></pre><p><strong>Migration</strong>: None required. Integrators listening for&nbsp;<code>trainer_schedule_next_workout</code>&nbsp;will simply receive it at the correct moment (after a plan is ready) rather than on intermediate clarifying turns.</p>","coverImage":null,"category":"new","product":"client","version":"2.32.15","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-10T12:26:56.957Z","publishedAt":"2026-06-10T12:29:42.492Z","status":"published","updatedAt":"2026-06-10T12:29:42.492Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-06-10T12:29:59.547Z","subscriberCountAtSend":25},{"id":"zLVfhuVVFVrOQmrK9PUw","title":"Form Guidance Bar: \"Too-Deep\" Overshoot Warning + Auto-Reset to Neutral ","slug":"form-guidance-bar-too-deep-overshoot-warning-auto-reset-to-neutral","summary":"The on-screen depth/form-guidance bar now warns when a user pushes a movement past the ideal range — it shades from green into yellow→red instead of staying green — and eases back to neutral when pose detection stops instead of freezing at the last depth. For integrators, the live form_guidance_frame PostMessage stream gains one new zone value, \"too_deep\". This is additive — no code change is required unless you exhaustively validate or switch on zone.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(Also affects any host app that consumes the live \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"form_guidance_frame\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\" / form-guidance data stream during exercises.)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Too-deep overshoot warning\"},{\"type\":\"text\",\"text\":\": On exercises configured with a depth limit deeper than the ideal band (e.g. some squats), the guidance bar now signals overshoot — staying green inside the ideal range, then transitioning yellow→red the further past it the user goes. Exercises without a configured deep limit are unchanged and can never enter this state.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bar resets to neutral on detection loss\"},{\"type\":\"text\",\"text\":\": When the user leaves the frame or steps too close for detection, the bar now smoothly eases back to the neutral (top/gray) position after roughly half a second, instead of freezing at the last measured depth. It re-engages cleanly when the user returns.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — additive only. Action is only needed if your host strictly validates or exhaustively switches on the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zone\"},{\"type\":\"text\",\"text\":\" value.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"form_guidance_frame\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" — new \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zone\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" value \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"too_deep\\\"\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The per-frame form-guidance stream (sent in headless mode) reports a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zone\"},{\"type\":\"text\",\"text\":\" describing where the user is in the movement. It can now also report \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"too_deep\\\"\"},{\"type\":\"text\",\"text\":\" when the user goes past the ideal (green) band toward maximum depth. All previously documented values are unchanged; this only adds a fourth possible value.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before\"},{\"type\":\"text\",\"text\":\" — \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zone\"},{\"type\":\"text\",\"text\":\" was one of three values:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"form_guidance_frame\\\",\\n  \\\"id\\\": \\\"squat_basic\\\",\\n  \\\"fill\\\": 0.733,\\n  \\\"target\\\": 0.733,\\n  \\\"tol\\\": 0.08,\\n  \\\"neutral\\\": 0.167,\\n  \\\"zone\\\": \\\"perfect\\\",\\n  \\\"angles\\\": { \\\"knee\\\": 66 }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zone\"},{\"type\":\"text\",\"text\":\" ∈ \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"neutral\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"approaching\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"perfect\\\"\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After\"},{\"type\":\"text\",\"text\":\" — \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zone\"},{\"type\":\"text\",\"text\":\" can additionally be \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"too_deep\\\"\"},{\"type\":\"text\",\"text\":\":\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"form_guidance_frame\\\",\\n  \\\"id\\\": \\\"squat_basic\\\",\\n  \\\"fill\\\": 0.94,\\n  \\\"target\\\": 0.733,\\n  \\\"tol\\\": 0.08,\\n  \\\"neutral\\\": 0.167,\\n  \\\"zone\\\": \\\"too_deep\\\",\\n  \\\"angles\\\": { \\\"knee\\\": 38 }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zone\"},{\"type\":\"text\",\"text\":\" ∈ \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"neutral\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"approaching\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"perfect\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"too_deep\\\"\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": No required change for hosts that map \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zone\"},{\"type\":\"text\",\"text\":\" loosely or ignore unknown values. If you have an exhaustive \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"switch\"},{\"type\":\"text\",\"text\":\"/enum/strict validator on \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zone\"},{\"type\":\"text\",\"text\":\", add a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"too_deep\\\"\"},{\"type\":\"text\",\"text\":\" branch (suggested visual: yellow→red, mirroring the built-in bar). \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"too_deep\\\"\"},{\"type\":\"text\",\"text\":\" only ever appears for exercises whose backend config sets a deep limit beyond the ideal band; other exercises will never emit it. The reset-to-neutral behavior is built-bar only and does not change the stream — frames simply stop arriving on detection loss, so apply your own idle handling if needed.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Camera</code>&nbsp;·&nbsp;<code>Workout</code><br><em>(Also affects any host app that consumes the live&nbsp;</em><code>form_guidance_frame</code><em>&nbsp;/ form-guidance data stream during exercises.)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Too-deep overshoot warning</strong>: On exercises configured with a depth limit deeper than the ideal band (e.g. some squats), the guidance bar now signals overshoot — staying green inside the ideal range, then transitioning yellow→red the further past it the user goes. Exercises without a configured deep limit are unchanged and can never enter this state.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Bar resets to neutral on detection loss</strong>: When the user leaves the frame or steps too close for detection, the bar now smoothly eases back to the neutral (top/gray) position after roughly half a second, instead of freezing at the last measured depth. It re-engages cleanly when the user returns.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — additive only. Action is only needed if your host strictly validates or exhaustively switches on the&nbsp;<code>zone</code>&nbsp;value.</p></blockquote><h3><code>form_guidance_frame</code><strong>&nbsp;— new&nbsp;</strong><code>zone</code><strong>&nbsp;value&nbsp;</strong><code>\"too_deep\"</code></h3><p>The per-frame form-guidance stream (sent in headless mode) reports a&nbsp;<code>zone</code>&nbsp;describing where the user is in the movement. It can now also report&nbsp;<code>\"too_deep\"</code>&nbsp;when the user goes past the ideal (green) band toward maximum depth. All previously documented values are unchanged; this only adds a fourth possible value.</p><p><strong>Before</strong>&nbsp;—&nbsp;<code>zone</code>&nbsp;was one of three values:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"form_guidance_frame\",\n  \"id\": \"squat_basic\",\n  \"fill\": 0.733,\n  \"target\": 0.733,\n  \"tol\": 0.08,\n  \"neutral\": 0.167,\n  \"zone\": \"perfect\",\n  \"angles\": { \"knee\": 66 }\n}</code></pre><p><code>zone</code>&nbsp;∈&nbsp;<code>\"neutral\"</code>&nbsp;|&nbsp;<code>\"approaching\"</code>&nbsp;|&nbsp;<code>\"perfect\"</code></p><p><strong>After</strong>&nbsp;—&nbsp;<code>zone</code>&nbsp;can additionally be&nbsp;<code>\"too_deep\"</code>:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"form_guidance_frame\",\n  \"id\": \"squat_basic\",\n  \"fill\": 0.94,\n  \"target\": 0.733,\n  \"tol\": 0.08,\n  \"neutral\": 0.167,\n  \"zone\": \"too_deep\",\n  \"angles\": { \"knee\": 38 }\n}</code></pre><p><code>zone</code>&nbsp;∈&nbsp;<code>\"neutral\"</code>&nbsp;|&nbsp;<code>\"approaching\"</code>&nbsp;|&nbsp;<code>\"perfect\"</code>&nbsp;|&nbsp;<code>\"too_deep\"</code></p><p><strong>Migration</strong>: No required change for hosts that map&nbsp;<code>zone</code>&nbsp;loosely or ignore unknown values. If you have an exhaustive&nbsp;<code>switch</code>/enum/strict validator on&nbsp;<code>zone</code>, add a&nbsp;<code>\"too_deep\"</code>&nbsp;branch (suggested visual: yellow→red, mirroring the built-in bar).&nbsp;<code>\"too_deep\"</code>&nbsp;only ever appears for exercises whose backend config sets a deep limit beyond the ideal band; other exercises will never emit it. The reset-to-neutral behavior is built-bar only and does not change the stream — frames simply stop arriving on detection loss, so apply your own idle handling if needed.</p>","coverImage":null,"category":"improved","product":"client","version":"2.32.14","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-09T19:38:20.923Z","publishedAt":"2026-06-09T19:38:29.037Z","status":"published","updatedAt":"2026-06-09T19:38:29.037Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-06-09T19:38:46.835Z","subscriberCountAtSend":25},{"id":"muLfZvYreObmMldp079T","title":"More Reliable Camera & Pose-Tracking Startup in Embedded WebViews","slug":"more-reliable-camera-pose-tracking-startup-in-embedded-webviews","summary":"No integration code changes are required and no PostMessage events changed. This release hardens how the camera/pose-tracking engine starts up inside embedded and headless WebViews, eliminating a class of startup failures and freezes on iOS and reducing how long users wait before a working fallback kicks in.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(all camera/pose-tracking experiences share this startup path)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"iOS pose-tracking startup failures\"},{\"type\":\"text\",\"text\":\": Fixed a class of camera-engine initialization failures (surfacing as a \\\"NetworkError\\\") that could occur inside iOS WebViews. Pose tracking now starts reliably on those devices instead of falling back unnecessarily.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Freeze on preloaded / hidden WebViews\"},{\"type\":\"text\",\"text\":\": Fixed a hang on the loading screen when the SDK is preloaded in a hidden or background WebView. Startup no longer stalls in that state, and the full-performance path is automatically picked up once the screen becomes visible.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Faster recovery on slow/stuck startup\"},{\"type\":\"text\",\"text\":\": If the high-performance pose engine genuinely fails to start, the SDK now falls back to its standard tracking path sooner, so users spend less time on the loading screen. Capable-but-slow devices are still given enough time to start normally.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Camera</code>&nbsp;·&nbsp;<code>Assessments</code><br><em>(all camera/pose-tracking experiences share this startup path)</em></p><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>iOS pose-tracking startup failures</strong>: Fixed a class of camera-engine initialization failures (surfacing as a \"NetworkError\") that could occur inside iOS WebViews. Pose tracking now starts reliably on those devices instead of falling back unnecessarily.</p></li><li><p><strong>Freeze on preloaded / hidden WebViews</strong>: Fixed a hang on the loading screen when the SDK is preloaded in a hidden or background WebView. Startup no longer stalls in that state, and the full-performance path is automatically picked up once the screen becomes visible.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Faster recovery on slow/stuck startup</strong>: If the high-performance pose engine genuinely fails to start, the SDK now falls back to its standard tracking path sooner, so users spend less time on the loading screen. Capable-but-slow devices are still given enough time to start normally.</p></li></ul>","coverImage":null,"category":"improved","product":"client","version":"2.32.13","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-08T15:40:42.390Z","publishedAt":"2026-06-08T15:40:49.685Z","status":"published","updatedAt":"2026-06-08T15:40:49.685Z"},{"id":"bAXGSEAfKhxpoyW829q8","title":"Live Form-Guidance Depth Bar + More Accurate Efficiency Scoring for Combined Exercises","slug":"live-form-guidance-depth-bar-more-accurate-efficiency-scoring-for-combined-exerc","summary":"A new live form-guidance indicator (e.g. a squat-depth bar) now appears during exercises that the backend has configured for it — on by default for every integration except the standalone Camera component. It introduces three new outbound PostMessage events you can optionally consume to drive your own depth UI, plus two new opt-in config flags. All changes are additive — no existing event or payload changed, so no code changes are required to keep working as before. Separately, combined exercises (a rep target or a time limit) are now scored by reps completed, making the efficiency score reported on completion more accurate.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Home Page\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(All integrations: the bar appears across exercise-based flows and the new events go out over the shared host message channel. Note the default differs for standalone \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"},{\"type\":\"italic\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\" — see below.)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Visual Form Guidance bar\"},{\"type\":\"text\",\"text\":\": For exercises the backend marks with a form-guidance spec (e.g. squats), users now see a vertical depth bar with a \\\"perfect\\\" target line and a color that shifts gray → yellow → green as they reach the ideal depth. It works from front, side, floor, or chair camera angles and is independent of camera mirroring.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Headless / bring-your-own-UI mode\"},{\"type\":\"text\",\"text\":\": With \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"streamFormGuidance=true\"},{\"type\":\"text\",\"text\":\", the built-in bar is hidden and the live normalized depth data is streamed to your app instead, so you can render your own slider or depth visual. See the new PostMessage events below.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Two new config flags\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"showFormGuidance\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"streamFormGuidance\"},{\"type\":\"text\",\"text\":\" can be passed like any other session flag\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Form guidance on by default (except standalone Camera)\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"showFormGuidance\"},{\"type\":\"text\",\"text\":\" defaults to ON for all integrations and OFF for the standalone Camera component. Pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"showFormGuidance=false\"},{\"type\":\"text\",\"text\":\" to suppress the built-in bar, or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"showFormGuidance=true\"},{\"type\":\"text\",\"text\":\" to force it on the Camera component. The bar only ever appears when the backend supplies a form-guidance config for that exercise — exercises without one are unchanged.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"More accurate efficiency score for combined exercises\"},{\"type\":\"text\",\"text\":\": Exercises that allow \\\"X reps \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"or\"},{\"type\":\"text\",\"text\":\" Y seconds\\\" are now scored by reps completed rather than by elapsed time. Finishing your reps early now correctly reflects full completion, and the efficiency score reported at workout completion is more accurate.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — all three events below are \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"new and additive\"},{\"type\":\"text\",\"text\":\". No existing event, field, or payload was renamed or removed. Consume them only if you want depth data; ignore them otherwise.\"}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Across all three events, the top-level \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"id\"},{\"type\":\"text\",\"text\":\" is the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"exercise identifier\"},{\"type\":\"text\",\"text\":\" (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercise_id\"},{\"type\":\"text\",\"text\":\" on the workout screen, the current exercise on the standalone Camera screen). Inside \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"angles[]\"},{\"type\":\"text\",\"text\":\", each \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"id\"},{\"type\":\"text\",\"text\":\" is the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"joint/spec key\"},{\"type\":\"text\",\"text\":\" (e.g. \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"knee\\\"\"},{\"type\":\"text\",\"text\":\").\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"1. \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"rep_depth_angle\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" — sent on each completed rep (whenever guidance is active)\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Reports the deepest angle reached per tracked joint during that rep, plus the spec describing the movement. Sent in \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"both\"},{\"type\":\"text\",\"text\":\" the built-in-bar mode and headless mode.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After (new event):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"rep_depth_angle\\\",\\n  \\\"id\\\": \\\"1024\\\",\\n  \\\"rep\\\": 3,\\n  \\\"combine\\\": \\\"primary\\\",\\n  \\\"angles\\\": [\\n    {\\n      \\\"id\\\": \\\"knee\\\", \\\"joint\\\": \\\"knee\\\", \\\"side\\\": \\\"auto\\\", \\\"source\\\": \\\"3d\\\",\\n      \\\"topAngle\\\": 175, \\\"neutralAngle\\\": 150, \\\"targetAngle\\\": 65,\\n      \\\"tolerance\\\": 12, \\\"bottomAngle\\\": 25, \\\"deepestAngle\\\": 64\\n    }\\n  ]\\n}\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"combine\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"primary\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"average\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"min\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"max\\\"\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"side\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"auto\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"left\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"right\\\"\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"source\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"2d\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"3d\\\"\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"joint\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"knee\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"hip\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"elbow\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"shoulder\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"ankle\\\"\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"deepestAngle\"},{\"type\":\"text\",\"text\":\": deepest raw (unsmoothed) angle in degrees reached that rep\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"2. \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"form_guidance_config\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" — sent once per exercise (headless mode only)\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The static spec you need to scale your own depth UI. Only emitted when \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"streamFormGuidance=true\"},{\"type\":\"text\",\"text\":\".\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After (new event):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"form_guidance_config\\\",\\n  \\\"id\\\": \\\"1024\\\",\\n  \\\"source\\\": \\\"3d\\\",\\n  \\\"combine\\\": \\\"primary\\\",\\n  \\\"smoothing\\\": 0.35,\\n  \\\"angles\\\": [\\n    {\\n      \\\"id\\\": \\\"knee\\\", \\\"joint\\\": \\\"knee\\\", \\\"side\\\": \\\"auto\\\", \\\"source\\\": \\\"3d\\\",\\n      \\\"topAngle\\\": 175, \\\"neutralAngle\\\": 150, \\\"targetAngle\\\": 65,\\n      \\\"tolerance\\\": 12, \\\"bottomAngle\\\": 25\\n    }\\n  ]\\n}\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"smoothing\"},{\"type\":\"text\",\"text\":\" is omitted if the backend did not set it.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"bottomAngle\"},{\"type\":\"text\",\"text\":\" is always resolved for you (explicit value, or the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"targetAngle - tolerance\"},{\"type\":\"text\",\"text\":\" fallback).\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"3. \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"form_guidance_frame\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" — sent per pose frame (headless mode only)\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The live normalized bar state plus the raw measured angle(s). Emitted only when the depth value actually changes (idle frames are skipped) — expect roughly the pose frame rate (~30/s) while moving, and nothing while still.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After (new event):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"form_guidance_frame\\\",\\n  \\\"id\\\": \\\"1024\\\",\\n  \\\"fill\\\": 0.73,\\n  \\\"target\\\": 0.733,\\n  \\\"tol\\\": 0.08,\\n  \\\"neutral\\\": 0.167,\\n  \\\"zone\\\": \\\"perfect\\\",\\n  \\\"angles\\\": { \\\"knee\\\": 66 }\\n}\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"fill\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"target\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"tol\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"neutral\"},{\"type\":\"text\",\"text\":\": fractions in \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"[0..1]\"},{\"type\":\"text\",\"text\":\" along the bar axis, where \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"0 = top (standing/extended)\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"1 = bottom (deepest)\"},{\"type\":\"text\",\"text\":\".\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zone\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"neutral\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"approaching\\\"\"},{\"type\":\"text\",\"text\":\" | \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"perfect\\\"\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"angles\"},{\"type\":\"text\",\"text\":\": raw measured angle(s) in degrees (unsmoothed)\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required. To render your own depth UI, set \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"streamFormGuidance=true\"},{\"type\":\"text\",\"text\":\", read the static thresholds from \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"form_guidance_config\"},{\"type\":\"text\",\"text\":\", then position your handle each frame from \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"form_guidance_frame\"},{\"type\":\"text\",\"text\":\" (use the normalized \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"fill\"},{\"type\":\"text\",\"text\":\"/\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"target\"},{\"type\":\"text\",\"text\":\"/\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"tol\"},{\"type\":\"text\",\"text\":\"/\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"neutral\"},{\"type\":\"text\",\"text\":\", or compute your own mapping from the raw \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"angles\"},{\"type\":\"text\",\"text\":\" + config thresholds). To keep the built-in bar instead, do nothing. To turn the built-in bar off, pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"showFormGuidance=false\"},{\"type\":\"text\",\"text\":\".\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Home Page</code>&nbsp;·&nbsp;<code>Camera</code>&nbsp;·&nbsp;<code>Assessments</code><br><em>(All integrations: the bar appears across exercise-based flows and the new events go out over the shared host message channel. Note the default differs for standalone&nbsp;</em><strong><em>Camera</em></strong><em>&nbsp;— see below.)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Visual Form Guidance bar</strong>: For exercises the backend marks with a form-guidance spec (e.g. squats), users now see a vertical depth bar with a \"perfect\" target line and a color that shifts gray → yellow → green as they reach the ideal depth. It works from front, side, floor, or chair camera angles and is independent of camera mirroring.</p></li><li><p><strong>Headless / bring-your-own-UI mode</strong>: With&nbsp;<code>streamFormGuidance=true</code>, the built-in bar is hidden and the live normalized depth data is streamed to your app instead, so you can render your own slider or depth visual. See the new PostMessage events below.</p></li><li><p><strong>Two new config flags</strong>:&nbsp;<code>showFormGuidance</code>&nbsp;and&nbsp;<code>streamFormGuidance</code>&nbsp;can be passed like any other session flag</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Form guidance on by default (except standalone Camera)</strong>:&nbsp;<code>showFormGuidance</code>&nbsp;defaults to ON for all integrations and OFF for the standalone Camera component. Pass&nbsp;<code>showFormGuidance=false</code>&nbsp;to suppress the built-in bar, or&nbsp;<code>showFormGuidance=true</code>&nbsp;to force it on the Camera component. The bar only ever appears when the backend supplies a form-guidance config for that exercise — exercises without one are unchanged.</p></li><li><p><strong>More accurate efficiency score for combined exercises</strong>: Exercises that allow \"X reps&nbsp;<em>or</em>&nbsp;Y seconds\" are now scored by reps completed rather than by elapsed time. Finishing your reps early now correctly reflects full completion, and the efficiency score reported at workout completion is more accurate.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — all three events below are&nbsp;<strong>new and additive</strong>. No existing event, field, or payload was renamed or removed. Consume them only if you want depth data; ignore them otherwise.</p></blockquote><p>Across all three events, the top-level&nbsp;<code>id</code>&nbsp;is the&nbsp;<strong>exercise identifier</strong>&nbsp;(<code>exercise_id</code>&nbsp;on the workout screen, the current exercise on the standalone Camera screen). Inside&nbsp;<code>angles[]</code>, each&nbsp;<code>id</code>&nbsp;is the&nbsp;<strong>joint/spec key</strong>&nbsp;(e.g.&nbsp;<code>\"knee\"</code>).</p><h3><strong>1.&nbsp;</strong><code>rep_depth_angle</code><strong>&nbsp;— sent on each completed rep (whenever guidance is active)</strong></h3><p>Reports the deepest angle reached per tracked joint during that rep, plus the spec describing the movement. Sent in&nbsp;<strong>both</strong>&nbsp;the built-in-bar mode and headless mode.</p><p><strong>After (new event):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"rep_depth_angle\",\n  \"id\": \"1024\",\n  \"rep\": 3,\n  \"combine\": \"primary\",\n  \"angles\": [\n    {\n      \"id\": \"knee\", \"joint\": \"knee\", \"side\": \"auto\", \"source\": \"3d\",\n      \"topAngle\": 175, \"neutralAngle\": 150, \"targetAngle\": 65,\n      \"tolerance\": 12, \"bottomAngle\": 25, \"deepestAngle\": 64\n    }\n  ]\n}</code></pre><ul><li><p><code>combine</code>:&nbsp;<code>\"primary\"</code>&nbsp;|&nbsp;<code>\"average\"</code>&nbsp;|&nbsp;<code>\"min\"</code>&nbsp;|&nbsp;<code>\"max\"</code></p></li><li><p><code>side</code>:&nbsp;<code>\"auto\"</code>&nbsp;|&nbsp;<code>\"left\"</code>&nbsp;|&nbsp;<code>\"right\"</code></p></li><li><p><code>source</code>:&nbsp;<code>\"2d\"</code>&nbsp;|&nbsp;<code>\"3d\"</code></p></li><li><p><code>joint</code>:&nbsp;<code>\"knee\"</code>&nbsp;|&nbsp;<code>\"hip\"</code>&nbsp;|&nbsp;<code>\"elbow\"</code>&nbsp;|&nbsp;<code>\"shoulder\"</code>&nbsp;|&nbsp;<code>\"ankle\"</code></p></li><li><p><code>deepestAngle</code>: deepest raw (unsmoothed) angle in degrees reached that rep</p></li></ul><h3><strong>2.&nbsp;</strong><code>form_guidance_config</code><strong>&nbsp;— sent once per exercise (headless mode only)</strong></h3><p>The static spec you need to scale your own depth UI. Only emitted when&nbsp;<code>streamFormGuidance=true</code>.</p><p><strong>After (new event):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"form_guidance_config\",\n  \"id\": \"1024\",\n  \"source\": \"3d\",\n  \"combine\": \"primary\",\n  \"smoothing\": 0.35,\n  \"angles\": [\n    {\n      \"id\": \"knee\", \"joint\": \"knee\", \"side\": \"auto\", \"source\": \"3d\",\n      \"topAngle\": 175, \"neutralAngle\": 150, \"targetAngle\": 65,\n      \"tolerance\": 12, \"bottomAngle\": 25\n    }\n  ]\n}</code></pre><ul><li><p><code>smoothing</code>&nbsp;is omitted if the backend did not set it.</p></li><li><p><code>bottomAngle</code>&nbsp;is always resolved for you (explicit value, or the&nbsp;<code>targetAngle - tolerance</code>&nbsp;fallback).</p></li></ul><h3><strong>3.&nbsp;</strong><code>form_guidance_frame</code><strong>&nbsp;— sent per pose frame (headless mode only)</strong></h3><p>The live normalized bar state plus the raw measured angle(s). Emitted only when the depth value actually changes (idle frames are skipped) — expect roughly the pose frame rate (~30/s) while moving, and nothing while still.</p><p><strong>After (new event):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"form_guidance_frame\",\n  \"id\": \"1024\",\n  \"fill\": 0.73,\n  \"target\": 0.733,\n  \"tol\": 0.08,\n  \"neutral\": 0.167,\n  \"zone\": \"perfect\",\n  \"angles\": { \"knee\": 66 }\n}</code></pre><ul><li><p><code>fill</code>,&nbsp;<code>target</code>,&nbsp;<code>tol</code>,&nbsp;<code>neutral</code>: fractions in&nbsp;<code>[0..1]</code>&nbsp;along the bar axis, where&nbsp;<strong>0 = top (standing/extended)</strong>&nbsp;and&nbsp;<strong>1 = bottom (deepest)</strong>.</p></li><li><p><code>zone</code>:&nbsp;<code>\"neutral\"</code>&nbsp;|&nbsp;<code>\"approaching\"</code>&nbsp;|&nbsp;<code>\"perfect\"</code></p></li><li><p><code>angles</code>: raw measured angle(s) in degrees (unsmoothed)</p></li></ul><p><strong>Migration</strong>: None required. To render your own depth UI, set&nbsp;<code>streamFormGuidance=true</code>, read the static thresholds from&nbsp;<code>form_guidance_config</code>, then position your handle each frame from&nbsp;<code>form_guidance_frame</code>&nbsp;(use the normalized&nbsp;<code>fill</code>/<code>target</code>/<code>tol</code>/<code>neutral</code>, or compute your own mapping from the raw&nbsp;<code>angles</code>&nbsp;+ config thresholds). To keep the built-in bar instead, do nothing. To turn the built-in bar off, pass&nbsp;<code>showFormGuidance=false</code>.</p>","coverImage":null,"category":"new","product":"client","version":"2.32.12","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-08T14:06:01.260Z","publishedAt":"2026-06-08T14:14:05.827Z","status":"published","updatedAt":"2026-06-08T14:14:05.827Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-06-08T14:14:27.523Z","subscriberCountAtSend":25},{"id":"VJLSi14dC3TXjEChaVkX","title":"Fixed AI Trainer Chat Crash on Older iOS In-App WebViews","slug":"fixed-ai-trainer-chat-crash-on-older-ios-in-app-webviews","summary":"No integration code changes are required and no PostMessage events changed. End users on older iOS in-app WebViews (iOS WebKit < 16.4) will no longer see the AI Trainer chat screen crash when a coaching message contains an email-style address. The chat now renders reliably across all WebView versions.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted surface\"},{\"type\":\"text\",\"text\":\": AI Trainer / coaching chat\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer chat no longer crashes on older iOS\"},{\"type\":\"text\",\"text\":\": On iOS in-app WebViews running WebKit older than 16.4, coaching messages that included an email-style address could crash the entire Trainer screen. Those messages now display correctly on every WebView version.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Graceful fallback for chat formatting\"},{\"type\":\"text\",\"text\":\": If rich-text formatting of a chat message ever fails to render, the message now falls back to readable plain text instead of taking down the screen, so the conversation always stays visible.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\"}]}]}","contentHtml":"<p><strong>Impacted surface</strong>: AI Trainer / coaching chat</p><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>AI Trainer chat no longer crashes on older iOS</strong>: On iOS in-app WebViews running WebKit older than 16.4, coaching messages that included an email-style address could crash the entire Trainer screen. Those messages now display correctly on every WebView version.</p></li><li><p><strong>Graceful fallback for chat formatting</strong>: If rich-text formatting of a chat message ever fails to render, the message now falls back to readable plain text instead of taking down the screen, so the conversation always stays visible.</p></li></ul><p><br></p>","coverImage":null,"category":"fixed","product":"client","version":"2.32.11","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-07T16:07:43.217Z","publishedAt":"2026-06-07T16:07:48.795Z","status":"published","updatedAt":"2026-06-07T16:07:48.795Z"},{"id":"wYaFuadCS7Nwyslwn9Ex","coverImage":null,"category":"new","product":"client","version":"2.32.10","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-05T11:29:38.657Z","summary":"End users will notice smoother, faster pose tracking that now auto-tunes itself to each device, a cleaner camera-framing screen, and two intentional behavior changes (exercises auto-skip sooner, and out-of-frame handling now depends on whether the spoken framing cue is enabled). Because the core camera/pose pipeline and shared device, sound, and storage code changed, every integration is affected and should be smoke-tested on real hardware.","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations&nbsp;<em>(shared camera/pose pipeline, device detection, sound, and storage changes touch every Workout, Assessment, Challenge, Game, and Plan)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Background pose detection</strong>: On capable devices (modern Chrome/Edge, Android Chrome, Safari/iOS 17+), pose detection can now run off the main thread for smoother tracking. On older iOS, recorded-video sources, or any failure, it silently falls back to the standard path — no integration change needed.</p></li><li><p><strong>Automatic model-quality selection</strong>: At startup the app now picks a lighter or full pose model based on the device's capability. An explicit model passed by the integrator (or a user's saved choice) still wins; the automatic path leans conservative on lower-end devices.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Faster, smoother tracking</strong>: Per-frame performance was optimized across the pose pipeline, and duplicate camera frames are now skipped so a 30fps camera on a 60Hz display isn't processed twice.</p></li><li><p><strong>Cleaner framing screen</strong>: The pulsing gray camera border and the glow around the body-position cutout have been removed for a calmer setup experience. Frame color and body-part indicators still convey in/out-of-frame state.</p></li><li><p><strong>Break / skip-countdown overlay</strong>: Repositioned and re-layered so it reliably covers the camera while keeping the pause and exit controls tappable.</p></li><li><p><strong>More resilient startup</strong>: Model warm-up now survives an error instead of blocking startup, and the MediaPipe runtime/model files are served from a single self-hosted location.</p></li><li><p><strong>iPad now detected as iOS</strong>: iPads that previously reported as desktop are now treated as iOS, giving them the correct camera and orientation behavior.</p></li></ul><h2><strong>Behavior Changes</strong></h2><ul><li><p><strong>Exercises auto-skip sooner</strong>: The auto-skip threshold for repeated detected mistakes was lowered from 10 to 7 — users advance past a struggling exercise faster.</p></li><li><p><strong>Out-of-frame handling now depends on the framing cue</strong>: When the spoken framing cue is&nbsp;<strong>off</strong>, rep recognition keeps running even if the user is partly out of frame. When it's&nbsp;<strong>on</strong>, recognition pauses (and audio mutes) while out of frame and resumes once re-framed — matching prior behavior only when the cue is enabled.</p></li></ul><p><br></p>","title":"Smoother, Faster Motion Tracking — Auto-Tuned Per Device, with Cleaner Framing & Break Screens","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(shared camera/pose pipeline, device detection, sound, and storage changes touch every Workout, Assessment, Challenge, Game, and Plan)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Background pose detection\"},{\"type\":\"text\",\"text\":\": On capable devices (modern Chrome/Edge, Android Chrome, Safari/iOS 17+), pose detection can now run off the main thread for smoother tracking. On older iOS, recorded-video sources, or any failure, it silently falls back to the standard path — no integration change needed.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Automatic model-quality selection\"},{\"type\":\"text\",\"text\":\": At startup the app now picks a lighter or full pose model based on the device's capability. An explicit model passed by the integrator (or a user's saved choice) still wins; the automatic path leans conservative on lower-end devices.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Faster, smoother tracking\"},{\"type\":\"text\",\"text\":\": Per-frame performance was optimized across the pose pipeline, and duplicate camera frames are now skipped so a 30fps camera on a 60Hz display isn't processed twice.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Cleaner framing screen\"},{\"type\":\"text\",\"text\":\": The pulsing gray camera border and the glow around the body-position cutout have been removed for a calmer setup experience. Frame color and body-part indicators still convey in/out-of-frame state.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Break / skip-countdown overlay\"},{\"type\":\"text\",\"text\":\": Repositioned and re-layered so it reliably covers the camera while keeping the pause and exit controls tappable.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"More resilient startup\"},{\"type\":\"text\",\"text\":\": Model warm-up now survives an error instead of blocking startup, and the MediaPipe runtime/model files are served from a single self-hosted location.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"iPad now detected as iOS\"},{\"type\":\"text\",\"text\":\": iPads that previously reported as desktop are now treated as iOS, giving them the correct camera and orientation behavior.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Behavior Changes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercises auto-skip sooner\"},{\"type\":\"text\",\"text\":\": The auto-skip threshold for repeated detected mistakes was lowered from 10 to 7 — users advance past a struggling exercise faster.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Out-of-frame handling now depends on the framing cue\"},{\"type\":\"text\",\"text\":\": When the spoken framing cue is \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"off\"},{\"type\":\"text\",\"text\":\", rep recognition keeps running even if the user is partly out of frame. When it's \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"on\"},{\"type\":\"text\",\"text\":\", recognition pauses (and audio mutes) while out of frame and resumes once re-framed — matching prior behavior only when the cue is enabled.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\"}]}]}","slug":"smoother-faster-motion-tracking-auto-tuned-per-device-with-cleaner-framing-break","publishedAt":"2026-06-07T14:34:54.017Z","status":"published","updatedAt":"2026-06-07T14:34:54.017Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-06-07T14:35:10.444Z","subscriberCountAtSend":25},{"id":"AM2xNBLLwyM8c9vpdwCB","title":"New Analyst role with read-only dashboard access","slug":"new-analyst-role-with-read-only-dashboard-access","summary":"A new Analyst staff role is now available. Analysts can view the Dashboard — including company stats and per-user analytics — but cannot access content management, design customization, API configuration, or team settings. Owners and Admins can invite team members as Analysts from the Team Management page. No action is required for existing users; all other roles are unaffected.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Dashboard\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Team Management\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Authentication\"}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Analyst role\"},{\"type\":\"text\",\"text\":\": You can now invite team members with an \\\"Analyst\\\" role. Analysts have read-only access to the Dashboard, including company statistics and individual user analytics. They cannot view or modify exercises, workouts, plans, design settings, API keys, configuration, or team membership.\"}]}]}]}]}","contentHtml":"<blockquote><p><strong>Impacted areas</strong>:&nbsp;<code>Dashboard</code>&nbsp;·&nbsp;<code>Team Management</code>&nbsp;·&nbsp;<code>Authentication</code></p></blockquote><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Analyst role</strong>: You can now invite team members with an \"Analyst\" role. Analysts have read-only access to the Dashboard, including company statistics and individual user analytics. They cannot view or modify exercises, workouts, plans, design settings, API keys, configuration, or team membership.</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"2.32.10","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-04T15:43:41.894Z","publishedAt":"2026-06-04T15:44:26.569Z","status":"published","updatedAt":"2026-06-04T15:44:26.569Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-06-04T15:44:40.404Z","subscriberCountAtSend":24},{"id":"xgbISc2fi7tHqFaQBMxg","title":"Profile data forwarded on verify, trainer setup polish, challenge alert contrast fix","slug":"profile-data-forwarded-on-verify-trainer-setup-polish-challenge-alert-contrast-f","summary":"It includes a more accurate trainer workout-duration estimate, a wording tweak in the AI Trainer setup wizard, and a contrast fix on the Challenge screen's info button.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Trainer workout duration estimate\"},{\"type\":\"text\",\"text\":\": The duration shown on the AI Trainer's workout card and the auto-generated session title is now ~17% more accurate for rep-based exercises, using a 2.5-second-per-rep estimate (down from 3s). Results are rounded to whole seconds so the badge no longer renders fractional values across any locale.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer setup wizard wording\"},{\"type\":\"text\",\"text\":\": The duration question used to open with \\\"Last thing —\\\", but the wizard still asked three follow-up readiness questions after it. The misleading lead-in has been removed in all 16 supported locales so the step now reads as a normal question.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Challenge alert button contrast\"},{\"type\":\"text\",\"text\":\": The info/alert button on the Challenge start screen used a foreground color that washed out against the brand-colored button background. It now uses the on-brand foreground token for proper contrast on every theme.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Personalized Plan</code></p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>Trainer workout duration estimate</strong>: The duration shown on the AI Trainer's workout card and the auto-generated session title is now ~17% more accurate for rep-based exercises, using a 2.5-second-per-rep estimate (down from 3s). Results are rounded to whole seconds so the badge no longer renders fractional values across any locale.</p></li><li><p><strong>AI Trainer setup wizard wording</strong>: The duration question used to open with \"Last thing —\", but the wizard still asked three follow-up readiness questions after it. The misleading lead-in has been removed in all 16 supported locales so the step now reads as a normal question.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Challenge alert button contrast</strong>: The info/alert button on the Challenge start screen used a foreground color that washed out against the brand-colored button background. It now uses the on-brand foreground token for proper contrast on every theme.</p></li></ul>","coverImage":null,"product":"client","version":"2.32.9","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-06-03T20:44:35.871Z","category":"improved","publishedAt":"2026-06-03T20:44:41.710Z","status":"published","updatedAt":"2026-06-03T20:44:41.710Z"},{"id":"nPbrAoCboSXlQ8O5kSaT","category":"improved","notificationSentAt":null,"product":"client","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>AI Trainer Chat</code></p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>AI Trainer duration step</strong>: The wizard now starts with an empty duration field and a disabled Apply button. The user must either type a duration or tap the ✨ AI-recommended chip before they can proceed, so the duration that reaches the prompt summary and workout generation always matches what the user actually chose. The in-app Settings sheet for editing an existing trainer profile is unchanged — it still pre-fills the saved duration.</p></li></ul>","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"AI Trainer Chat\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer duration step\"},{\"type\":\"text\",\"text\":\": The wizard now starts with an empty duration field and a disabled Apply button. The user must either type a duration or tap the ✨ AI-recommended chip before they can proceed, so the duration that reaches the prompt summary and workout generation always matches what the user actually chose. The in-app Settings sheet for editing an existing trainer profile is unchanged — it still pre-fills the saved duration.\"}]}]}]}]}","version":"2.32.8","authorName":"vladimir@kinestex.com","authorId":"11","subscriberCountAtSend":null,"slug":"ai-trainer-setup-now-requires-an-explicit-workout-duration","exerciseId":null,"coverImage":null,"title":"AI Trainer setup now requires an explicit workout duration","summary":"The AI Trainer setup wizard no longer silently commits the AI-recommended duration when a user opens the duration step and taps Apply without changing it. Users must now consciously pick a number (typed or via the ✨ recommendation chip), preventing duration mismatches between the user's intent and the workout that gets generated. No PostMessage or integration-contract changes — integrators do not need to update any code.","exerciseTitle":null,"createdAt":"2026-06-02T22:13:10.396Z","publishedAt":"2026-06-02T22:13:15.679Z","status":"published","updatedAt":"2026-06-02T22:13:15.679Z"},{"id":"09Kfz4UCBvsnxJFbSPDp","authorName":"vladimir@kinestex.com","exerciseId":null,"exerciseTitle":null,"content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Personalized Plan unlock threshold lowered to 50%\"},{\"type\":\"text\",\"text\":\": Users only need to reach a 50% efficiency score (down from 80%) to count a session toward their personalized plan and unlock the next workout. The post-workout warning, the \\\"didn't hit efficiency\\\" message, and the upstream progression POST all now gate on the same 50% number.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Softer, capped form penalty in efficiency score\"},{\"type\":\"text\",\"text\":\": Each detected form mistake now reduces an exercise's efficiency by 1.5% instead of 2%, and the total mistake penalty per exercise is capped at 30% (reached at ~20 mistakes). A user who completes every rep but triggers many mistakes will now land around 70% instead of being driven toward 0%.\"}]}]}]}]}","slug":"easier-personalized-plan-progression-softer-form-penalty","product":"client","coverImage":null,"notificationSentAt":null,"category":"improved","version":"2.32.7","title":"Easier Personalized Plan Progression & Softer Form Penalty","authorId":"11","subscriberCountAtSend":null,"contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Plan</code></p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>Personalized Plan unlock threshold lowered to 50%</strong>: Users only need to reach a 50% efficiency score (down from 80%) to count a session toward their personalized plan and unlock the next workout. The post-workout warning, the \"didn't hit efficiency\" message, and the upstream progression POST all now gate on the same 50% number.</p></li><li><p><strong>Softer, capped form penalty in efficiency score</strong>: Each detected form mistake now reduces an exercise's efficiency by 1.5% instead of 2%, and the total mistake penalty per exercise is capped at 30% (reached at ~20 mistakes). A user who completes every rep but triggers many mistakes will now land around 70% instead of being driven toward 0%.</p></li></ul>","summary":"Personalized plans now unlock the next workout at 50% efficiency instead of 80%, and the per-mistake penalty in the efficiency calculation has been softened and capped. No PostMessage schema changes — but the efficiency_score field will trend higher for the same performance, so any client tracking it over time should expect a one-time shift.","createdAt":"2026-05-30T16:05:12.181Z","publishedAt":"2026-05-30T16:05:35.380Z","status":"published","updatedAt":"2026-05-30T16:05:35.380Z"},{"id":"80tsVQKxtUJJq2W8ecr7","version":null,"slug":"push-ups-enhanced-processing-mode","title":"Push Ups — Enhanced Processing Mode","exerciseId":"Exercise ID 9, Model ID 4","authorId":"3","authorName":"perman@kinestex.com","contentHtml":"<p><strong>Impacted integrations:</strong> <code>Workout</code> · <code>Challenge</code> · <code>Plan</code> · <code>Personalized Plan</code></p><p></p><p><strong>Improvements</strong></p><ul><li><p><strong>Enhanced processing mode</strong>: The model has been upgraded from <code>basic</code> to <code>enhanced</code>, now tracking elbow and shoulder angles to evaluate each rep. This makes down-phase and up-phase detection more reliable, reduces false positives on partial reps, and improves form feedback accuracy regardless of the user's camera angle or hand placement width.</p></li></ul>","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations:\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"}]},{\"type\":\"paragraph\"},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Enhanced processing mode\"},{\"type\":\"text\",\"text\":\": The model has been upgraded from \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"basic\"},{\"type\":\"text\",\"text\":\" to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"enhanced\"},{\"type\":\"text\",\"text\":\", now tracking elbow and shoulder angles to evaluate each rep. This makes down-phase and up-phase detection more reliable, reduces false positives on partial reps, and improves form feedback accuracy regardless of the user's camera angle or hand placement width.\"}]}]}]}]}","product":"exercises","summary":"Upgrades the internal processing mode for Push Ups from basic to enhanced. The enhanced mode introduces angle-based joint analysis for more reliable rep detection and improved form feedback accuracy.","category":"improved","exerciseTitle":"Push Ups","coverImage":null,"createdAt":"2026-05-30T14:11:56.234Z","publishedAt":"2026-05-30T14:16:12.565Z","status":"published","updatedAt":"2026-05-30T14:16:12.565Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-30T14:16:19.057Z","subscriberCountAtSend":5},{"id":"Ht1bQgwpv21ShyiZWBSX","authorName":"perman@kinestex.com","product":"exercises","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations:\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"}]},{\"type\":\"paragraph\"},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Enhanced processing mode\"},{\"type\":\"text\",\"text\":\": The model has been upgraded from \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"basic\"},{\"type\":\"text\",\"text\":\" to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"enhanced\"},{\"type\":\"text\",\"text\":\", applying angle-based tracking to arm and leg spread. This improves rep recognition across a wider range of body types and movement speeds, reducing missed or duplicate counts caused by imprecise landmark detection.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New coordination feedback cue\"},{\"type\":\"text\",\"text\":\": A new real-time feedback message — \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"\\\"Open your arms and legs at the same time\\\"\"},{\"type\":\"text\",\"text\":\" — has been added. It is triggered when the system detects a desynchronized movement pattern, specifically when the arms are opening while the legs are closing or vice versa. This helps users build proper form and ensures the model registers clean, fully synchronized reps.\"}]}]}]}]}","exerciseTitle":"Jumping Jack","exerciseId":"Exercise ID 715, Model ID 394","coverImage":null,"category":"improved","version":null,"authorId":"3","title":"Jumping Jack — Enhanced Processing Mode & New Coordination Feedback","contentHtml":"<p><strong>Impacted integrations:</strong> <code>Workout</code> · <code>Challenge</code> · <code>Plan</code> · <code>Personalized Plan</code></p><p></p><p><strong>Improvements</strong></p><ul><li><p><strong>Enhanced processing mode</strong>: The model has been upgraded from <code>basic</code> to <code>enhanced</code>, applying angle-based tracking to arm and leg spread. This improves rep recognition across a wider range of body types and movement speeds, reducing missed or duplicate counts caused by imprecise landmark detection.</p></li><li><p><strong>New coordination feedback cue</strong>: A new real-time feedback message — <strong>\"Open your arms and legs at the same time\"</strong> — has been added. It is triggered when the system detects a desynchronized movement pattern, specifically when the arms are opening while the legs are closing or vice versa. This helps users build proper form and ensures the model registers clean, fully synchronized reps.</p></li></ul>","slug":"jumping-jack-enhanced-processing-mode-new-coordination-feedback","summary":"Upgrades the internal processing mode for Jumping Jacks from basic to enhanced and introduces a new real-time coordination feedback cue. The enhanced mode applies angle-based tracking to arm and leg spread, while the new cue helps users maintain proper synchronization between their arms and legs during the movement.","createdAt":"2026-05-30T14:09:08.840Z","publishedAt":"2026-05-30T14:16:10.301Z","status":"published","updatedAt":"2026-05-30T14:16:10.301Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-30T14:16:19.682Z","subscriberCountAtSend":11},{"id":"useCnfSQU2m3rcsbUIpz","coverImage":null,"exerciseId":"Exercise ID 6, Model ID 3","exerciseTitle":"Squats","category":"improved","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations:\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"}]},{\"type\":\"paragraph\"},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Enhanced processing mode\"},{\"type\":\"text\",\"text\":\": The model has been upgraded from \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"basic\"},{\"type\":\"text\",\"text\":\" to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"enhanced\"},{\"type\":\"text\",\"text\":\", switching from a positional landmark approach to an angle-based joint analysis system. The model now continuously tracks knee and hip angles throughout the movement, allowing more accurate detection of squat depth and cleaner rep counting even when the user shifts position or changes pace.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations:</strong> <code>Workout</code> · <code>Challenge</code> · <code>Plan</code> · <code>Personalized Plan</code></p><p></p><p><strong>Improvements</strong></p><ul><li><p><strong>Enhanced processing mode</strong>: The model has been upgraded from <code>basic</code> to <code>enhanced</code>, switching from a positional landmark approach to an angle-based joint analysis system. The model now continuously tracks knee and hip angles throughout the movement, allowing more accurate detection of squat depth and cleaner rep counting even when the user shifts position or changes pace.</p></li></ul>","title":"Squats — Enhanced Processing Mode","authorId":"3","product":"exercises","summary":"Upgrades the internal processing mode for Squats from basic to enhanced. The enhanced mode introduces angle-based joint analysis, enabling more precise rep detection and form evaluation.","authorName":"perman@kinestex.com","slug":"squats-enhanced-processing-mode","version":null,"createdAt":"2026-05-30T14:05:29.135Z","publishedAt":"2026-05-30T14:16:04.166Z","status":"published","updatedAt":"2026-05-30T14:16:04.166Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-30T14:16:10.789Z","subscriberCountAtSend":11},{"id":"6nTYSzSBJ13iKWsRy7dy","category":"new","title":"Body-alignment frame check & Alien Squat Shooter rebuild","exerciseTitle":null,"authorName":"vladimir@kinestex.com","product":"client","summary":"The pre-workout \"Get in the frame\" screen has been rebuilt with per-body-part chips (Head, Torso, Arms, Legs), a stricter all-limbs-in-cutout check, and a 25-second \"Try Again\" fallback for users the camera can't find. Alien Squat Shooter is now a 3-round, 20-ship game with a PREPARE screen, new HUD, and rescaled mastery/health-benefit values. PostMessage event shapes are unchanged, but the numeric gameScore, masteryTitle_aliensquatshooter, and healthBenefits values emitted for aliensquatshooter now use a different scale — leaderboards and dashboards that compare across versions will need to re-baseline.","version":"2.32.6","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Body-part frame check\"},{\"type\":\"text\",\"text\":\": The frame-check screen now shows four live status chips — Head, Torso, Arms, Legs — that turn green as each region enters the camera cutout. Joints that are out of frame pulse red on the on-screen skeleton.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"\\\"Can't see you\\\" fallback\"},{\"type\":\"text\",\"text\":\": If pose detection fails to find a person for 25 seconds, a new \\\"Camera can't see your full body\\\" screen appears with a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Try Again\"},{\"type\":\"text\",\"text\":\" button that fully restarts the pose detector. A gray pulsing border around the camera area indicates the no-detection state.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Alien Squat Shooter — round mode\"},{\"type\":\"text\",\"text\":\": The game is no longer a 45-second timed run. It is now a 3-round mission with a fixed 20 ships total (4 → 7 → 9), per-round \\\"Round Complete\\\" and final \\\"Mission Complete\\\" cards, milestone praise overlays at 5/10/15 kills, a new top HUD (round / shield / neutralized count), a PREPARE screen that plays the spoken intro before the 3-2-1 countdown, and a red damage-flash on hits.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Tap-to-open plan day\"},{\"type\":\"text\",\"text\":\": On the Plan onboarding weekly result, individual workout day rows are now tappable and open that day's workout directly while preserving plan context.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Stricter, more reliable cutout validation\"},{\"type\":\"text\",\"text\":\": The \\\"all body parts in frame\\\" check now reads the actual rendered cutout box (instead of a fixed normalized region) and validates every tracked limb — nose, shoulders, elbows, wrists, hips, knees and ankles — for both Workout, Assessments, BalloonPop and ColorChase.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Frame-check on wide screens\"},{\"type\":\"text\",\"text\":\": The cutout container and side panel layout were corrected so the camera area no longer becomes desynced or squeezed on screens above 1440px and on medium tablets.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Skip button delay\"},{\"type\":\"text\",\"text\":\": The Skip button on the frame-check screen and on the Accelerometer screen is now hidden for the first 5 seconds to discourage skipping before detection has a chance to kick in.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Cutout look\"},{\"type\":\"text\",\"text\":\": The cutout corners are slightly sharper, the check icon adapts to the current theme colour, and the \\\"Found you!\\\" message is now followed by a \\\"Hold your position\\\" line.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Alien Squat Shooter pacing & feedback\"},{\"type\":\"text\",\"text\":\": Round 1 only ever has one alien on screen for a gentle warm-up; rounds 2-3 cap at two concurrent aliens with mildly faster descent. The intro voice line is now played reliably even when the previous \\\"static mode\\\" gate was active, with hard fallbacks (2s / 12s) so muted users or failed-to-load intros do not stall on the PREPARE screen.\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": Yes — if you persist or compare Alien Squat Shooter scores, mastery titles, or health-benefit numbers across SDK versions. No action required if you only forward the payload as-is.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"aliensquatshooter\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" payload values are now on a 0–20 scale\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_overview\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_restart\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_completed\"},{\"type\":\"text\",\"text\":\" event \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"shapes are unchanged\"},{\"type\":\"text\",\"text\":\", but for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"type: \\\"aliensquatshooter\\\"\"},{\"type\":\"text\",\"text\":\" the numeric values returned by this PR are on a smaller, hard-capped scale because the game now has a fixed maximum of 20 ships (no time limit). Specifically:\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"gameScore\"},{\"type\":\"text\",\"text\":\" is now hard-capped at \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"20\"},{\"type\":\"text\",\"text\":\" (previously a time-limited count with no fixed cap).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"masteryTitle_aliensquatshooter\"},{\"type\":\"text\",\"text\":\" thresholds shifted from 5 / 10 / 15 / 20 / 25 down to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"4 / 8 / 12 / 16 / 20\"},{\"type\":\"text\",\"text\":\". The same player skill now lands in a different tier label.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"healthBenefits.heartDiseaseReduction\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"healthBenefits.diabetesReduction\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"healthBenefits.obesityReduction\"},{\"type\":\"text\",\"text\":\", and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"healthBenefits.depressionReduction\"},{\"type\":\"text\",\"text\":\" for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"aliensquatshooter\"},{\"type\":\"text\",\"text\":\" are now computed as \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"score / 20 * maxReduction\"},{\"type\":\"text\",\"text\":\" instead of \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"score / 100 * maxReduction\"},{\"type\":\"text\",\"text\":\", so for the same \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"gameScore\"},{\"type\":\"text\",\"text\":\" they will be roughly 5× larger (still clamped to the per-condition max).\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The mastery title slugs that may appear are unchanged: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"rookie_defender\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"space_squatter\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"galactic_gunner\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"star_shooter\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"cosmic_blaster\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"alien_annihilator\"},{\"type\":\"text\",\"text\":\".\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before\"},{\"type\":\"text\",\"text\":\" (assessment payload, aliensquatshooter, illustrative values):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"assessment_completed\\\",\\n  \\\"assessment_type\\\": \\\"aliensquatshooter\\\",\\n  \\\"gameScore\\\": 22,\\n  \\\"masteryTitle_aliensquatshooter\\\": \\\"cosmic_blaster\\\",\\n  \\\"healthBenefits\\\": {\\n    \\\"heartDiseaseReduction\\\": 3.3,\\n    \\\"diabetesReduction\\\": 4.4,\\n    \\\"obesityReduction\\\": 2.2,\\n    \\\"depressionReduction\\\": 3.3\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After\"},{\"type\":\"text\",\"text\":\" (same player skill, new scale):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"assessment_completed\\\",\\n  \\\"assessment_type\\\": \\\"aliensquatshooter\\\",\\n  \\\"gameScore\\\": 17,\\n  \\\"masteryTitle_aliensquatshooter\\\": \\\"cosmic_blaster\\\",\\n  \\\"healthBenefits\\\": {\\n    \\\"heartDiseaseReduction\\\": 12.8,\\n    \\\"diabetesReduction\\\": 17.0,\\n    \\\"obesityReduction\\\": 8.5,\\n    \\\"depressionReduction\\\": 12.8\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"A flawless run now emits:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"assessment_completed\\\",\\n  \\\"assessment_type\\\": \\\"aliensquatshooter\\\",\\n  \\\"gameScore\\\": 20,\\n  \\\"masteryTitle_aliensquatshooter\\\": \\\"alien_annihilator\\\",\\n  \\\"healthBenefits\\\": {\\n    \\\"heartDiseaseReduction\\\": 15,\\n    \\\"diabetesReduction\\\": 20,\\n    \\\"obesityReduction\\\": 10,\\n    \\\"depressionReduction\\\": 15\\n  }\\n}\"}]}]}]}]}","slug":"body-alignment-frame-check-alien-squat-shooter-rebuild","authorId":"11","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Camera</code>&nbsp;·&nbsp;<code>Assessments</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Body-part frame check</strong>: The frame-check screen now shows four live status chips — Head, Torso, Arms, Legs — that turn green as each region enters the camera cutout. Joints that are out of frame pulse red on the on-screen skeleton.</p></li><li><p><strong>\"Can't see you\" fallback</strong>: If pose detection fails to find a person for 25 seconds, a new \"Camera can't see your full body\" screen appears with a&nbsp;<strong>Try Again</strong>&nbsp;button that fully restarts the pose detector. A gray pulsing border around the camera area indicates the no-detection state.</p></li><li><p><strong>Alien Squat Shooter — round mode</strong>: The game is no longer a 45-second timed run. It is now a 3-round mission with a fixed 20 ships total (4 → 7 → 9), per-round \"Round Complete\" and final \"Mission Complete\" cards, milestone praise overlays at 5/10/15 kills, a new top HUD (round / shield / neutralized count), a PREPARE screen that plays the spoken intro before the 3-2-1 countdown, and a red damage-flash on hits.</p></li><li><p><strong>Tap-to-open plan day</strong>: On the Plan onboarding weekly result, individual workout day rows are now tappable and open that day's workout directly while preserving plan context.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Stricter, more reliable cutout validation</strong>: The \"all body parts in frame\" check now reads the actual rendered cutout box (instead of a fixed normalized region) and validates every tracked limb — nose, shoulders, elbows, wrists, hips, knees and ankles — for both Workout, Assessments, BalloonPop and ColorChase.</p></li><li><p><strong>Frame-check on wide screens</strong>: The cutout container and side panel layout were corrected so the camera area no longer becomes desynced or squeezed on screens above 1440px and on medium tablets.</p></li><li><p><strong>Skip button delay</strong>: The Skip button on the frame-check screen and on the Accelerometer screen is now hidden for the first 5 seconds to discourage skipping before detection has a chance to kick in.</p></li><li><p><strong>Cutout look</strong>: The cutout corners are slightly sharper, the check icon adapts to the current theme colour, and the \"Found you!\" message is now followed by a \"Hold your position\" line.</p></li><li><p><strong>Alien Squat Shooter pacing &amp; feedback</strong>: Round 1 only ever has one alien on screen for a gentle warm-up; rounds 2-3 cap at two concurrent aliens with mildly faster descent. The intro voice line is now played reliably even when the previous \"static mode\" gate was active, with hard fallbacks (2s / 12s) so muted users or failed-to-load intros do not stall on the PREPARE screen.</p><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: Yes — if you persist or compare Alien Squat Shooter scores, mastery titles, or health-benefit numbers across SDK versions. No action required if you only forward the payload as-is.</p></blockquote><h3><code>aliensquatshooter</code><strong> payload values are now on a 0–20 scale</strong></h3><p>The <code>assessment_overview</code>, <code>assessment_restart</code> and <code>assessment_completed</code> event <strong>shapes are unchanged</strong>, but for <code>type: \"aliensquatshooter\"</code> the numeric values returned by this PR are on a smaller, hard-capped scale because the game now has a fixed maximum of 20 ships (no time limit). Specifically:</p><ul><li><p><code>gameScore</code> is now hard-capped at <strong>20</strong> (previously a time-limited count with no fixed cap).</p></li><li><p><code>masteryTitle_aliensquatshooter</code> thresholds shifted from 5 / 10 / 15 / 20 / 25 down to <strong>4 / 8 / 12 / 16 / 20</strong>. The same player skill now lands in a different tier label.</p></li><li><p><code>healthBenefits.heartDiseaseReduction</code>, <code>healthBenefits.diabetesReduction</code>, <code>healthBenefits.obesityReduction</code>, and <code>healthBenefits.depressionReduction</code> for <code>aliensquatshooter</code> are now computed as <code>score / 20 * maxReduction</code> instead of <code>score / 100 * maxReduction</code>, so for the same <code>gameScore</code> they will be roughly 5× larger (still clamped to the per-condition max).</p></li></ul><p>The mastery title slugs that may appear are unchanged: <code>rookie_defender</code>, <code>space_squatter</code>, <code>galactic_gunner</code>, <code>star_shooter</code>, <code>cosmic_blaster</code>, <code>alien_annihilator</code>.</p><p><strong>Before</strong> (assessment payload, aliensquatshooter, illustrative values):</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"assessment_completed\",\n  \"assessment_type\": \"aliensquatshooter\",\n  \"gameScore\": 22,\n  \"masteryTitle_aliensquatshooter\": \"cosmic_blaster\",\n  \"healthBenefits\": {\n    \"heartDiseaseReduction\": 3.3,\n    \"diabetesReduction\": 4.4,\n    \"obesityReduction\": 2.2,\n    \"depressionReduction\": 3.3\n  }\n}</code></pre><p><strong>After</strong> (same player skill, new scale):</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"assessment_completed\",\n  \"assessment_type\": \"aliensquatshooter\",\n  \"gameScore\": 17,\n  \"masteryTitle_aliensquatshooter\": \"cosmic_blaster\",\n  \"healthBenefits\": {\n    \"heartDiseaseReduction\": 12.8,\n    \"diabetesReduction\": 17.0,\n    \"obesityReduction\": 8.5,\n    \"depressionReduction\": 12.8\n  }\n}</code></pre><p>A flawless run now emits:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"assessment_completed\",\n  \"assessment_type\": \"aliensquatshooter\",\n  \"gameScore\": 20,\n  \"masteryTitle_aliensquatshooter\": \"alien_annihilator\",\n  \"healthBenefits\": {\n    \"heartDiseaseReduction\": 15,\n    \"diabetesReduction\": 20,\n    \"obesityReduction\": 10,\n    \"depressionReduction\": 15\n  }\n}</code></pre></li></ul>","exerciseId":null,"coverImage":null,"createdAt":"2026-05-30T12:05:13.647Z","publishedAt":"2026-05-30T12:05:36.135Z","status":"published","updatedAt":"2026-05-30T12:05:36.135Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-30T12:05:49.496Z","subscriberCountAtSend":24},{"id":"d1r1Z8qXzNVgxlOmu0uk","exerciseId":null,"title":"Single-Leg Stand Assessment Cue Fix, New Localized Phrases, and Audio Cache Refresh","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"\\\"Lift either leg\\\" and \\\"Keep your leg lifted\\\" cues\"},{\"type\":\"text\",\"text\":\": Two new spoken/displayed lines are now available across all 14 supported locales for the Single-Leg Stand assessment, providing more accurate guidance during leg-lift phases.\"}]}]}]}]}","authorId":"11","authorName":"vladimir@kinestex.com","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Assessments</code>&nbsp;·&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>\"Lift either leg\" and \"Keep your leg lifted\" cues</strong>: Two new spoken/displayed lines are now available across all 14 supported locales for the Single-Leg Stand assessment, providing more accurate guidance during leg-lift phases.</p></li></ul>","coverImage":null,"slug":"single-leg-stand-assessment-cue-fix-new-localized-phrases-and-audio-cache-refres","version":"2.32.5","product":"client","summary":"No integration code changes are required. End users running the Single-Leg Stand assessment will now hear the correct spoken cue when holding their leg up, and a one-time refresh of cached audio will occur on first launch after update. New localized phrases are added across all 14 supported languages, and the Hebrew assessment failure message has been reworded.","exerciseTitle":null,"createdAt":"2026-05-29T18:26:37.488Z","category":"improved","publishedAt":"2026-05-29T18:27:16.661Z","status":"published","updatedAt":"2026-05-29T18:27:16.661Z","emailMode":"groups","notifiedGroupIds":["0IkpaHDJ3Sf13IxiIsCm"],"notificationSentAt":"2026-05-29T18:27:19.770Z","subscriberCountAtSend":2},{"id":"iSkTpEBC7C0fDwp2rpkC","coverImage":null,"exerciseId":null,"subscriberCountAtSend":null,"contentHtml":"<p><strong>Impacted integrations</strong>: <code>Challenge</code> · <code>Workout</code> · <code>Plan</code> · <code>Personalized Plan</code> · <code>Camera</code> · <code>Assessments</code></p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>More reliable pose tracking startup</strong>: The motion-tracking runtimes that power pose detection and per-exercise model loading are now hosted on the same origin as the SDK. Pose tracking no longer depends on a third-party CDN being reachable or unchanged, and version-pinned immutable caching means repeat sessions start faster after the first load.</p></li></ul>","slug":"updated-motion-tracking-runtimes-for-reliable-model-loading","category":"improved","product":"client","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"More reliable pose tracking startup\"},{\"type\":\"text\",\"text\":\": The motion-tracking runtimes that power pose detection and per-exercise model loading are now hosted on the same origin as the SDK. Pose tracking no longer depends on a third-party CDN being reachable or unchanged, and version-pinned immutable caching means repeat sessions start faster after the first load.\"}]}]}]}]}","title":"Updated motion-tracking runtimes for reliable model loading","exerciseTitle":null,"summary":"Motion tracking runtimes are now served from the SDK's own origin instead of a third-party CDN, eliminating model-load failures caused by upstream CDN outages or file moves.","authorName":"vladimir@kinestex.com","authorId":"11","notificationSentAt":null,"version":"2.32.4","createdAt":"2026-05-28T19:58:59.696Z","publishedAt":"2026-05-28T19:59:07.990Z","status":"published","updatedAt":"2026-05-28T19:59:07.990Z"},{"id":"fmFHcApdy8OFLAx7sH8b","authorName":"vladimir@kinestex.com","title":"Hotfix: Restore Exercise Model Loading","coverImage":null,"product":"client","summary":"Patches a CDN regression that was breaking exercise tracking model loads inside the embedded SDK.","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code></p><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Exercise tracking models failing to load</strong>: Resolved a runtime initialization error (<code>l._malloc</code>&nbsp;undefined) that prevented per-exercise accuracy/form models from loading. Any flow that enters a tracked exercise — workouts, plans, personalized plans, and challenges — will now reach the camera screen instead of stalling on model load.</p></li></ul><p><br></p>","version":"2.32.3","exerciseId":null,"slug":"hotfix-restore-exercise-model-loading","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise tracking models failing to load\"},{\"type\":\"text\",\"text\":\": Resolved a runtime initialization error (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"l._malloc\"},{\"type\":\"text\",\"text\":\" undefined) that prevented per-exercise accuracy/form models from loading. Any flow that enters a tracked exercise — workouts, plans, personalized plans, and challenges — will now reach the camera screen instead of stalling on model load.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\"}]}]}","exerciseTitle":null,"authorId":"11","category":"fixed","createdAt":"2026-05-28T18:35:25.827Z","publishedAt":"2026-05-28T18:35:30.997Z","status":"published","updatedAt":"2026-05-28T18:35:30.997Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-28T18:35:44.914Z","subscriberCountAtSend":24},{"id":"x7IKxPKqTFjyvLdTX0Ub","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"AI Trainer\"},{\"type\":\"text\",\"text\":\" (no changes to PostMessage events on any other integration)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"\\\"Anything else?\\\" preferences field\"},{\"type\":\"text\",\"text\":\": A new optional textarea (up to 500 characters) appears as the final step of the AI Trainer setup wizard alongside duration / warmup / cooldown. Whatever the user types is also shown back to the trainer as an instruction line (\\\"Also, please keep this in mind: …\\\") so the LLM consistently picks it up.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Editable from chat history and settings\"},{\"type\":\"text\",\"text\":\": The typed answer is rendered in the chat as its own card (omitted when empty) and is tap-editable from there. It also appears as a new row inside Trainer Settings → Workout Details, so users can update it any time without re-running the wizard.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Persisted on the trainer profile\"},{\"type\":\"text\",\"text\":\": The value round-trips with the trainer profile, so it survives reloads and is restored on next session. Clearing the textarea explicitly clears the saved value.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\"}]}]}","authorName":"vladimir@kinestex.com","summary":"Adds an optional free-text \"Anything else?\" step at the end of the AI Trainer setup wizard so users can share custom training requirements (e.g. low-impact moves, limited space, training for a 5K) in their own words.  The preference persists on the user's trainer profile and is folded into every workout generation request so the trainer model can take it into account.","version":"2.32.2","title":"AI Trainer Setup: \"Anything else?\" Preferences Step","slug":"ai-trainer-setup-anything-else-preferences-step","exerciseTitle":null,"category":"new","exerciseId":null,"notificationSentAt":null,"subscriberCountAtSend":null,"coverImage":null,"contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>AI Trainer</code>&nbsp;(no changes to PostMessage events on any other integration)</p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>\"Anything else?\" preferences field</strong>: A new optional textarea (up to 500 characters) appears as the final step of the AI Trainer setup wizard alongside duration / warmup / cooldown. Whatever the user types is also shown back to the trainer as an instruction line (\"Also, please keep this in mind: …\") so the LLM consistently picks it up.</p></li><li><p><strong>Editable from chat history and settings</strong>: The typed answer is rendered in the chat as its own card (omitted when empty) and is tap-editable from there. It also appears as a new row inside Trainer Settings → Workout Details, so users can update it any time without re-running the wizard.</p></li><li><p><strong>Persisted on the trainer profile</strong>: The value round-trips with the trainer profile, so it survives reloads and is restored on next session. Clearing the textarea explicitly clears the saved value.</p></li></ul><p><br></p>","authorId":"11","product":"client","createdAt":"2026-05-28T15:06:00.326Z","publishedAt":"2026-05-28T15:06:07.709Z","status":"published","updatedAt":"2026-05-28T15:06:07.709Z"},{"id":"bGHru137CIKIrCYtJdxA","category":"improved","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Home Page\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(Calorie + camera-progress changes affect all integrations that complete an exercise; AI Trainer surfaces the new launch flow and PostMessage event.)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Schedule next workout from the AI Trainer\"},{\"type\":\"text\",\"text\":\": A calendar button now sits next to the \\\"Start Workout\\\" action on both mobile and desktop. Tapping it opens a bottom-sheet modal where the user picks Tomorrow, In 2 days, In 3 days, or a custom date/time. On submit, a confirmation toast appears and a new outbound PostMessage event is emitted to the host (see API Changes).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Dedicated AI Trainer loading screen\"},{\"type\":\"text\",\"text\":\": After tapping Start Workout in the Trainer chat, the user is taken to a new full-screen loading page that runs plan confirmation and resource preload before advancing into the orientation/accelerometer step. Replaces the previous in-chat \\\"Confirm → Loading → Start\\\" two-step button.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Calibrated, personalised calorie model\"},{\"type\":\"text\",\"text\":\": Per-exercise and whole-workout calories are now scaled to match heart-rate-validated energy expenditure (~1.5× higher than before) and use a dynamic basal metabolic rate computed from the host-provided user profile (weight, height, age, gender, Mifflin-St Jeor). When the host does not supply a full profile, the model falls back to a population-average BMR (raised from 1550 to 1700 kcal/day). BMR is clamped to a safe range to neutralise garbage profile data.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Rest periods now contribute calories\"},{\"type\":\"text\",\"text\":\": Seconds spent in between-exercise rest now accrue calories at a light-activity rate and are added to the workout total. Previously rest time contributed zero calories.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Standalone Camera SDK calorie estimates\"},{\"type\":\"text\",\"text\":\": When the camera SDK is used outside a full workout (no per-second motion records), per-exercise calories now come from a time-based MET model using the same calibration and BMR as the rest of the app.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"More accurate exercise time reporting\"},{\"type\":\"text\",\"text\":\": The current/in-progress exercise now reports its elapsed wall-clock seconds in the camera SDK summary instead of a stale cached value, so the final exercise of a session no longer appears with \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"timeSpent: 0\"},{\"type\":\"text\",\"text\":\".\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer chat polish\"},{\"type\":\"text\",\"text\":\": The default Trainer \\\"thinking effort\\\" is now Low; on mobile, quick-reply chips appear only while the input is focused (swapping in over the action row); on desktop, the sidebar shows a shimmer placeholder while a new/updated plan is being generated so the stale plan is no longer briefly visible; the schedule button surfaces a dismissible first-time tooltip.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Race-condition guards on Trainer preload\"},{\"type\":\"text\",\"text\":\": If the user edits a workout plan while resources are still preloading, the new plan (not the stale one) is what actually launches. Failed preloads now route the user back to chat with an error instead of stalling on the loading bar.\"}]},{\"type\":\"paragraph\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3}}]}","authorId":"11","version":"2.32.1","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Camera</code>&nbsp;·&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Home Page</code>&nbsp;·&nbsp;<code>Assessments</code><br><em>(Calorie + camera-progress changes affect all integrations that complete an exercise; AI Trainer surfaces the new launch flow and PostMessage event.)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Schedule next workout from the AI Trainer</strong>: A calendar button now sits next to the \"Start Workout\" action on both mobile and desktop. Tapping it opens a bottom-sheet modal where the user picks Tomorrow, In 2 days, In 3 days, or a custom date/time. On submit, a confirmation toast appears and a new outbound PostMessage event is emitted to the host (see API Changes).</p></li><li><p><strong>Dedicated AI Trainer loading screen</strong>: After tapping Start Workout in the Trainer chat, the user is taken to a new full-screen loading page that runs plan confirmation and resource preload before advancing into the orientation/accelerometer step. Replaces the previous in-chat \"Confirm → Loading → Start\" two-step button.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Calibrated, personalised calorie model</strong>: Per-exercise and whole-workout calories are now scaled to match heart-rate-validated energy expenditure (~1.5× higher than before) and use a dynamic basal metabolic rate computed from the host-provided user profile (weight, height, age, gender, Mifflin-St Jeor). When the host does not supply a full profile, the model falls back to a population-average BMR (raised from 1550 to 1700 kcal/day). BMR is clamped to a safe range to neutralise garbage profile data.</p></li><li><p><strong>Rest periods now contribute calories</strong>: Seconds spent in between-exercise rest now accrue calories at a light-activity rate and are added to the workout total. Previously rest time contributed zero calories.</p></li><li><p><strong>Standalone Camera SDK calorie estimates</strong>: When the camera SDK is used outside a full workout (no per-second motion records), per-exercise calories now come from a time-based MET model using the same calibration and BMR as the rest of the app.</p></li><li><p><strong>More accurate exercise time reporting</strong>: The current/in-progress exercise now reports its elapsed wall-clock seconds in the camera SDK summary instead of a stale cached value, so the final exercise of a session no longer appears with&nbsp;<code>timeSpent: 0</code>.</p></li><li><p><strong>AI Trainer chat polish</strong>: The default Trainer \"thinking effort\" is now Low; on mobile, quick-reply chips appear only while the input is focused (swapping in over the action row); on desktop, the sidebar shows a shimmer placeholder while a new/updated plan is being generated so the stale plan is no longer briefly visible; the schedule button surfaces a dismissible first-time tooltip.</p></li><li><p><strong>Race-condition guards on Trainer preload</strong>: If the user edits a workout plan while resources are still preloading, the new plan (not the stale one) is what actually launches. Failed preloads now route the user back to chat with an error instead of stalling on the loading bar.</p><p></p></li></ul><h3></h3>","exerciseId":null,"authorName":"vladimir@kinestex.com","summary":"End users see a redesigned Trainer launch flow with a calendar/schedule action, and every integration now reports more accurate calories driven by the host-provided user profile (weight, height, age, gender).","coverImage":null,"title":"AI Trainer Workout Scheduling, Single-Tap Launch & Calibrated Calorie Model","slug":"ai-trainer-workout-scheduling-single-tap-launch-calibrated-calorie-model","exerciseTitle":null,"product":"client","createdAt":"2026-05-28T08:15:39.136Z","publishedAt":"2026-05-28T08:15:44.007Z","status":"published","updatedAt":"2026-05-28T08:15:44.007Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-28T08:16:01.477Z","subscriberCountAtSend":24},{"id":"NfuBMFhj2uGOkX79M0eQ","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>All Integrations</code><br><em>(custom/white-label theming is shared across every embedded screen, so the theming change touches all integration options.)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Multiple AI Trainer chat sessions</strong>: Users can now keep several Trainer conversations at once — start a new chat, switch between them, rename them, and delete them. A maximum chat count is enforced, with a clear prompt to delete an old chat once the limit is reached.</p></li><li><p><strong>Step-by-step \"thinking\" feedback</strong>: While the Trainer builds a plan, it now shows staged progress messages (e.g. gathering your profile, searching exercises for your goal, analyzing and finishing) instead of a generic spinner.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Custom theme application is more efficient</strong>: White-label themes are now re-applied from a local cache without waiting on a backend round-trip, reducing the chance of a brief unstyled flash on load. Existing cached themes migrate automatically with no setup.</p></li><li><p><strong>AI Trainer settings are tidier</strong>: The chat list now sits above the AI Model section, and the AI Model section is collapsed by default so the most-used controls are front and center.</p></li><li><p><strong>Smoother Trainer chat on mobile</strong>: The chat layout now stays stable when the on-screen keyboard opens and respects device safe areas (notches), and the workout plan view collapses gracefully while typing. Confirm/Start actions for a generated plan are easier to reach on small screens.</p></li><li><p><strong>More readable Trainer messages</strong>: Chat bubbles are wider for easier reading, and long content fits more comfortably.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Wide content stays inside the chat bubble</strong>: Large markdown tables and other wide content no longer overflow the Trainer chat bubble.</p></li><li><p><strong>Resumed chats get proper titles</strong>: Reopening a default-named chat now auto-titles it, and a renamed chat is no longer overwritten when resumed.</p></li></ul><hr><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — there are no changes to PostMessage event types, payloads, or field names in this release.</p></blockquote>","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"All Integrations\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(custom/white-label theming is shared across every embedded screen, so the theming change touches all integration options.)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Multiple AI Trainer chat sessions\"},{\"type\":\"text\",\"text\":\": Users can now keep several Trainer conversations at once — start a new chat, switch between them, rename them, and delete them. A maximum chat count is enforced, with a clear prompt to delete an old chat once the limit is reached.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Step-by-step \\\"thinking\\\" feedback\"},{\"type\":\"text\",\"text\":\": While the Trainer builds a plan, it now shows staged progress messages (e.g. gathering your profile, searching exercises for your goal, analyzing and finishing) instead of a generic spinner.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Custom theme application is more efficient\"},{\"type\":\"text\",\"text\":\": White-label themes are now re-applied from a local cache without waiting on a backend round-trip, reducing the chance of a brief unstyled flash on load. Existing cached themes migrate automatically with no setup.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer settings are tidier\"},{\"type\":\"text\",\"text\":\": The chat list now sits above the AI Model section, and the AI Model section is collapsed by default so the most-used controls are front and center.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Smoother Trainer chat on mobile\"},{\"type\":\"text\",\"text\":\": The chat layout now stays stable when the on-screen keyboard opens and respects device safe areas (notches), and the workout plan view collapses gracefully while typing. Confirm/Start actions for a generated plan are easier to reach on small screens.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"More readable Trainer messages\"},{\"type\":\"text\",\"text\":\": Chat bubbles are wider for easier reading, and long content fits more comfortably.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Wide content stays inside the chat bubble\"},{\"type\":\"text\",\"text\":\": Large markdown tables and other wide content no longer overflow the Trainer chat bubble.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Resumed chats get proper titles\"},{\"type\":\"text\",\"text\":\": Reopening a default-named chat now auto-titles it, and a renamed chat is no longer overwritten when resumed.\"}]}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — there are no changes to PostMessage event types, payloads, or field names in this release.\"}]}]}]}","version":"2.32.0","exerciseId":null,"slug":"ai-trainer-multi-chat-sessions-hardened-white-label-theming","authorId":"11","summary":"No integrator action is required — there are no PostMessage/API changes in this release. End users of the AI Trainer can now keep multiple saved chat conversations and see clearer step-by-step \"thinking\" feedback while a plan is generated. Separately, white-label/custom themes are now applied in an injection-proof way (security hardening) and re-applied from cache without a backend round-trip","exerciseTitle":null,"authorName":"vladimir@kinestex.com","title":"AI Trainer Multi-Chat Sessions & Hardened White-Label Theming","category":"improved","coverImage":null,"product":"client","createdAt":"2026-05-23T07:15:24.095Z","publishedAt":"2026-05-23T07:15:30.461Z","status":"published","updatedAt":"2026-05-23T07:15:30.461Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-23T07:15:48.095Z","subscriberCountAtSend":24},{"id":"jdncbR3SH2gAs0Q2pQRK","title":"Camera Access screen redesigned, Trainer settings loading fixes","slug":"camera-access-screen-redesigned-trainer-settings-loading-fixes","summary":"No PostMessage events, payloads, or API contracts changed — integrators do not need to update any code. End users will see a redesigned camera-permission screen with clearer privacy messaging in all 16 supported languages, and the in-app AI Trainer settings now load profile data without flashing default values.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Home Page\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"The Camera Access screen is the shared pre-session permission step used before any camera-driven integration. Trainer settings are surfaced from Home Page.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Redesigned Camera Access screen\"},{\"type\":\"text\",\"text\":\": The pre-session permission screen now shows three privacy reassurance cards — \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"Processed on your device\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"Nothing is recorded\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"Your data stays private\"},{\"type\":\"text\",\"text\":\" — alongside a shield illustration and a clearer \\\"Allow camera access\\\" call to action. The previous looping onboarding video has been replaced with the new static layout, and a \\\"By continuing, you agree to our Terms and Privacy Policy\\\" line replaces the older cookie-consent copy.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Localized privacy copy in 16 languages\"},{\"type\":\"text\",\"text\":\": New camera-access strings (titles, descriptions, button label, terms/privacy line) shipped in Arabic, Bengali, Danish, German, Greek, English, Spanish, French, Hebrew, Hindi, Indonesian, Italian, Dutch, Portuguese, Russian, and Chinese.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Trainer settings — loading placeholders\"},{\"type\":\"text\",\"text\":\": Opening Trainer settings now displays skeleton placeholders for profile rows (year of birth, weight, height, gender) and the fitness goal row while the saved profile is being fetched. Previously the UI briefly rendered default values (e.g. male / 30-minute duration) and then swapped them out when the real response arrived.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Trainer profile not reflecting saved values\"},{\"type\":\"text\",\"text\":\": After saving profile edits, Trainer settings could continue showing onboarding defaults instead of the user's persisted profile. The settings panel now reads the server response correctly and shows the saved profile on open.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Challenge</code>&nbsp;·&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code>&nbsp;·&nbsp;<code>Assessments</code>&nbsp;·&nbsp;<code>Home Page</code></p><p><em>The Camera Access screen is the shared pre-session permission step used before any camera-driven integration. Trainer settings are surfaced from Home Page.</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Redesigned Camera Access screen</strong>: The pre-session permission screen now shows three privacy reassurance cards —&nbsp;<em>Processed on your device</em>,&nbsp;<em>Nothing is recorded</em>,&nbsp;<em>Your data stays private</em>&nbsp;— alongside a shield illustration and a clearer \"Allow camera access\" call to action. The previous looping onboarding video has been replaced with the new static layout, and a \"By continuing, you agree to our Terms and Privacy Policy\" line replaces the older cookie-consent copy.</p></li><li><p><strong>Localized privacy copy in 16 languages</strong>: New camera-access strings (titles, descriptions, button label, terms/privacy line) shipped in Arabic, Bengali, Danish, German, Greek, English, Spanish, French, Hebrew, Hindi, Indonesian, Italian, Dutch, Portuguese, Russian, and Chinese.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Trainer settings — loading placeholders</strong>: Opening Trainer settings now displays skeleton placeholders for profile rows (year of birth, weight, height, gender) and the fitness goal row while the saved profile is being fetched. Previously the UI briefly rendered default values (e.g. male / 30-minute duration) and then swapped them out when the real response arrived.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Trainer profile not reflecting saved values</strong>: After saving profile edits, Trainer settings could continue showing onboarding defaults instead of the user's persisted profile. The settings panel now reads the server response correctly and shows the saved profile on open.</p></li></ul>","coverImage":null,"category":"improved","product":"client","version":"2.31.25","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-19T10:34:05.734Z","publishedAt":"2026-05-19T10:34:43.547Z","status":"published","updatedAt":"2026-05-19T10:34:43.547Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-19T10:34:58.901Z","subscriberCountAtSend":24},{"id":"7Vqo2yXcxuKlU0FEm3hr","title":"Translation Polish Across 15 Languages","slug":"translation-polish-across-15-languages","summary":"This release contains localization improvements only — no code changes, no PostMessage API changes, and no integration work required. End users in 15 supported languages will see clearer in-exercise cues, more natural assessment instructions, gender-inclusive phrasing, and a corrected color reference in the Functional Reach Test prompt.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(translations are shared across every embedded surface — workouts, challenges, plans, assessments, home)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Clearer in-exercise cues\"},{\"type\":\"text\",\"text\":\": The on-screen \\\"GO\\\" and \\\"HOLD\\\" labels shown during exercise reps were retranslated in French, Italian, Portuguese, Spanish, German, Greek, Arabic, Hindi, Indonesian, Dutch, Danish, and Bengali. The previous wordings were literal dictionary forms (e.g. Italian \\\"ANDARE\\\" / \\\"PRESA\\\"); the new wordings (\\\"VIA\\\" / \\\"TIENI\\\", \\\"PARTEZ\\\" / \\\"TENEZ\\\", \\\"LOS\\\", \\\"JÁ\\\" / \\\"SEGURE\\\", \\\"YA\\\" / \\\"MANTÉN\\\", etc.) match how a coach would actually shout the cue.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment phrasing polish\"},{\"type\":\"text\",\"text\":\": Spoken and on-screen instructions for the TUG, SLS, STS, FRT, Side-by-Side Stand, Tandem Stand, Balance Test, and ROM Shoulder assessments were rewritten in multiple languages to sound more like a clinician guiding the user, rather than literal translations of the English source. Examples: \\\"Walk to the green line\\\" (was \\\"Walk to the right zone\\\") in Chinese, Russian, Dutch, German; \\\"Sit down on the chair!\\\" (was \\\"Sit sideways in the zone\\\") in Chinese, Russian, Dutch, German.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Gender-inclusive phrasing\"},{\"type\":\"text\",\"text\":\": French, Italian, Spanish, Portuguese, Russian, Hindi, Arabic, and Greek now use inclusive forms (e.g. \\\"Prêt(e)\\\", \\\"Pronto/a\\\", \\\"Listo/a\\\", \\\"Готов(а)\\\", \\\"тैयार/तैयार\\\") for ready states, fatigue feedback, and post-set survey answers, so the wording reads correctly for users of any gender.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Hebrew rewrite to neutral infinitive voice\"},{\"type\":\"text\",\"text\":\": The Hebrew assessment and workout flow now uses the infinitive form (\\\"לעמוד\\\", \\\"להרים\\\", \\\"לשמור\\\") consistently instead of mixed masculine imperatives, plus inclusive masculine/feminine pairs (\\\"מוכן/ה\\\", \\\"זקוף/ה\\\"). Affects all assessment prompts, error messages, and TUG/SLS/STS/FRT/SBS instructions.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Cleaner Hebrew TUG cue\"},{\"type\":\"text\",\"text\":\": The Hebrew \\\"GO!\\\" prompt in the Timed Up and Go assessment changed from \\\"סע!\\\" (drive!) to \\\"קדימה!\\\" (forward!), which is the natural verbal cue.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improved Workout warm-up / cooldown labels\"},{\"type\":\"text\",\"text\":\": Warm-up and cooldown stage headers were standardized across languages for consistency between the workout progress bar and section headers.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"FRT instruction now references the correct color\"},{\"type\":\"text\",\"text\":\": The Functional Reach Test prompt told users in German, Spanish, Italian, Hindi, Hebrew, and Danish to \\\"stand in the BLUE zone on the right side of the screen\\\", but the on-screen zone indicator is actually GREEN. All affected languages now correctly say \\\"green zone\\\", removing a mismatch that confused users who were looking for a blue marker that did not exist.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Italian / Portuguese workout-skip reason \\\"I wasn't feeling ready\\\"\"},{\"type\":\"text\",\"text\":\": Was rendered in masculine-only form; now uses inclusive form so users of any gender see grammatically correct copy.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations<br><em>(translations are shared across every embedded surface — workouts, challenges, plans, assessments, home)</em></p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>Clearer in-exercise cues</strong>: The on-screen \"GO\" and \"HOLD\" labels shown during exercise reps were retranslated in French, Italian, Portuguese, Spanish, German, Greek, Arabic, Hindi, Indonesian, Dutch, Danish, and Bengali. The previous wordings were literal dictionary forms (e.g. Italian \"ANDARE\" / \"PRESA\"); the new wordings (\"VIA\" / \"TIENI\", \"PARTEZ\" / \"TENEZ\", \"LOS\", \"JÁ\" / \"SEGURE\", \"YA\" / \"MANTÉN\", etc.) match how a coach would actually shout the cue.</p></li><li><p><strong>Assessment phrasing polish</strong>: Spoken and on-screen instructions for the TUG, SLS, STS, FRT, Side-by-Side Stand, Tandem Stand, Balance Test, and ROM Shoulder assessments were rewritten in multiple languages to sound more like a clinician guiding the user, rather than literal translations of the English source. Examples: \"Walk to the green line\" (was \"Walk to the right zone\") in Chinese, Russian, Dutch, German; \"Sit down on the chair!\" (was \"Sit sideways in the zone\") in Chinese, Russian, Dutch, German.</p></li><li><p><strong>Gender-inclusive phrasing</strong>: French, Italian, Spanish, Portuguese, Russian, Hindi, Arabic, and Greek now use inclusive forms (e.g. \"Prêt(e)\", \"Pronto/a\", \"Listo/a\", \"Готов(а)\", \"тैयार/तैयार\") for ready states, fatigue feedback, and post-set survey answers, so the wording reads correctly for users of any gender.</p></li><li><p><strong>Hebrew rewrite to neutral infinitive voice</strong>: The Hebrew assessment and workout flow now uses the infinitive form (\"לעמוד\", \"להרים\", \"לשמור\") consistently instead of mixed masculine imperatives, plus inclusive masculine/feminine pairs (\"מוכן/ה\", \"זקוף/ה\"). Affects all assessment prompts, error messages, and TUG/SLS/STS/FRT/SBS instructions.</p></li><li><p><strong>Cleaner Hebrew TUG cue</strong>: The Hebrew \"GO!\" prompt in the Timed Up and Go assessment changed from \"סע!\" (drive!) to \"קדימה!\" (forward!), which is the natural verbal cue.</p></li><li><p><strong>Improved Workout warm-up / cooldown labels</strong>: Warm-up and cooldown stage headers were standardized across languages for consistency between the workout progress bar and section headers.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>FRT instruction now references the correct color</strong>: The Functional Reach Test prompt told users in German, Spanish, Italian, Hindi, Hebrew, and Danish to \"stand in the BLUE zone on the right side of the screen\", but the on-screen zone indicator is actually GREEN. All affected languages now correctly say \"green zone\", removing a mismatch that confused users who were looking for a blue marker that did not exist.</p></li><li><p><strong>Italian / Portuguese workout-skip reason \"I wasn't feeling ready\"</strong>: Was rendered in masculine-only form; now uses inclusive form so users of any gender see grammatically correct copy.</p></li></ul>","coverImage":null,"category":"new","product":"client","version":"2.31.24","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-17T17:15:26.105Z","publishedAt":"2026-05-17T17:15:38.072Z","status":"published","updatedAt":"2026-05-17T17:15:38.072Z","emailMode":"groups","notifiedGroupIds":["0IkpaHDJ3Sf13IxiIsCm"],"notificationSentAt":"2026-05-17T17:15:40.613Z","subscriberCountAtSend":2},{"id":"tEC7ePkh88isq7pJ8jS4","title":"Stage-tagged Workout Sections + Warm-up / Cool-down Label Cleanup","slug":"stage-tagged-workout-sections-warm-up-cool-down-label-cleanup","summary":"Trainer-built workouts can now visually distinguish warm-up, main, and cool-down sections on the rest screen and in the exercises list. The feature is purely additive and opt-in — regular workouts look unchanged. No PostMessage events were added, removed, or modified, so no integrator action is required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Stage-coded rest-screen progress bar\"},{\"type\":\"text\",\"text\":\": For trainer plans that declare warm-up and/or cool-down phases, the rest-screen progress bar now renders each segment in a phase colour (warm-up amber, main green, cool-down sky) with a matching legend underneath. Workouts without phase tags keep the existing single-colour bar.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Stage section headers in the exercises list\"},{\"type\":\"text\",\"text\":\": The in-workout exercises list modal now inserts a coloured section header (\\\"Warm-up\\\", \\\"Core / Main workout\\\", \\\"Cooldown\\\") above the first exercise of each phase, so users can see where they are in the structure of a trainer plan at a glance.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Stage-coded rest-screen progress bar</strong>: For trainer plans that declare warm-up and/or cool-down phases, the rest-screen progress bar now renders each segment in a phase colour (warm-up amber, main green, cool-down sky) with a matching legend underneath. Workouts without phase tags keep the existing single-colour bar.</p></li><li><p><strong>Stage section headers in the exercises list</strong>: The in-workout exercises list modal now inserts a coloured section header (\"Warm-up\", \"Core / Main workout\", \"Cooldown\") above the first exercise of each phase, so users can see where they are in the structure of a trainer plan at a glance.</p></li></ul><p><br></p>","coverImage":null,"category":"new","product":"client","version":"2.31.23","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-15T19:41:12.103Z","publishedAt":"2026-05-15T19:41:19.238Z","status":"published","updatedAt":"2026-05-15T19:41:19.238Z"},{"id":"4zcysXsyOrsuCSuAAhpi","title":"Shoulder Range-of-Motion Assessment, Auto-Skip Safety Net, Localized AI Personal Trainer","slug":"shoulder-range-of-motion-assessment-auto-skip-safety-net-localized-ai-personal-t","summary":"No PostMessage payloads or integration contracts have changed — integrators do not need to modify any code. End users will see a brand-new shoulder mobility assessment, a 10-mistake safety net that auto-advances stuck exercises in Workout / Plan / Personalized Plan / Challenge, a near-the-end voice cue on long exercises, and the AI Personal Trainer rendered in 16 languages with editable setup cards.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessment\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Trainer Chat\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Shoulder Range-of-Motion Assessment\"},{\"type\":\"text\",\"text\":\": A new assessment measures shoulder mobility on each arm. Users perform a 3-second \\\"get ready\\\" countdown, then a 10-second measurement of the left arm, another 3-second countdown, then a 10-second measurement of the right arm. A live radial gauge (0°–180°) and an on-body arc trace the abduction angle in real time. The result card reports per-arm max/min, the left/right symmetry delta (flagged when above 10°), and a Low / Moderate / High mobility risk tier.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Auto-Skip Safety Net (Workout family + Challenge)\"},{\"type\":\"text\",\"text\":\": After 10 total mistakes in a single exercise, a voice warning plays and a 5-second countdown overlay appears. The exercise auto-advances at zero. A correct rep, returning to the correct starting position, manually advancing, or going back all cancel the countdown. Pausing the workout freezes the countdown. Only active in workout-family contexts — Games, Squats, Training, standalone Camera, and Assessments are unaffected.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Personal Trainer — Localized in 16 Languages\"},{\"type\":\"text\",\"text\":\": Every part of the trainer chat (setup prompts, profile / goals / body-parts / injuries / equipment / duration / fitness-level cards, check-in widgets, loading states, error messages, schedule chips, and the natural-language summary sent back to the model) now renders in the active SDK language across all 16 supported locales instead of always English.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Editable Setup Cards in Trainer Chat\"},{\"type\":\"text\",\"text\":\": Each setup card in the trainer chat history now has an Edit affordance that opens a bottom-sheet editor. Saving propagates the change into the next trainer reply.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout Duration Estimate in Trainer\"},{\"type\":\"text\",\"text\":\": The duration card in the trainer flow now shows an estimated total workout time (rest + time-based + rep-based, calculated at ~3 seconds per rep) and updates automatically when duration / warmup / cooldown are edited.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code> <code>Assessment</code> <code>Trainer Chat</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Shoulder Range-of-Motion Assessment</strong>: A new assessment measures shoulder mobility on each arm. Users perform a 3-second \"get ready\" countdown, then a 10-second measurement of the left arm, another 3-second countdown, then a 10-second measurement of the right arm. A live radial gauge (0°–180°) and an on-body arc trace the abduction angle in real time. The result card reports per-arm max/min, the left/right symmetry delta (flagged when above 10°), and a Low / Moderate / High mobility risk tier.</p></li><li><p><strong>Auto-Skip Safety Net (Workout family + Challenge)</strong>: After 10 total mistakes in a single exercise, a voice warning plays and a 5-second countdown overlay appears. The exercise auto-advances at zero. A correct rep, returning to the correct starting position, manually advancing, or going back all cancel the countdown. Pausing the workout freezes the countdown. Only active in workout-family contexts — Games, Squats, Training, standalone Camera, and Assessments are unaffected.</p></li><li><p><strong>AI Personal Trainer — Localized in 16 Languages</strong>: Every part of the trainer chat (setup prompts, profile / goals / body-parts / injuries / equipment / duration / fitness-level cards, check-in widgets, loading states, error messages, schedule chips, and the natural-language summary sent back to the model) now renders in the active SDK language across all 16 supported locales instead of always English.</p></li><li><p><strong>Editable Setup Cards in Trainer Chat</strong>: Each setup card in the trainer chat history now has an Edit affordance that opens a bottom-sheet editor. Saving propagates the change into the next trainer reply.</p></li><li><p><strong>Workout Duration Estimate in Trainer</strong>: The duration card in the trainer flow now shows an estimated total workout time (rest + time-based + rep-based, calculated at ~3 seconds per rep) and updates automatically when duration / warmup / cooldown are edited.</p></li></ul><p><br></p>","coverImage":null,"category":"new","product":"client","version":"2.31.22","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-15T13:59:32.708Z","publishedAt":"2026-05-15T13:59:37.246Z","status":"published","updatedAt":"2026-05-15T13:59:37.246Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-15T13:59:56.107Z","subscriberCountAtSend":25},{"id":"z5m2BhVbX8SSN2R03iUz","title":"Dynamic Model Loading, Title/Exercise-ID Lookups, and Fixed Stickman Color on iOS","slug":"dynamic-model-loading-titleexercise-id-lookups-and-fixed-stickman-color-on-ios","summary":"Hosts can now fetch exercise models by title or exercise id (not just model id), and load additional models on the fly at runtime via a new load_models PostMessage. The brand-color stickman shown during loading now renders the exact requested color on iOS Safari (previously it silently fell back to default green). The models_loaded payload gains a modelIds field and speech_fetch_complete now also fires after model-audio caching, so listeners may need a small update.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"text\":\"New Features\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"text\":\"Load exercises by title or exercise id\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The initial verification configuration now accepts an optional \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exerciseFetchType\"},{\"type\":\"text\",\"text\":\" field with values \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"model_id\\\"\"},{\"type\":\"text\",\"text\":\" (default), \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exercise_id\\\"\"},{\"type\":\"text\",\"text\":\", or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exercise_title\\\"\"},{\"type\":\"text\",\"text\":\". Title matching is \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"case-sensitive\"},{\"type\":\"text\",\"text\":\". When the field is omitted, the SDK behaves exactly as before (model-id lookup).\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"text\":\"Runtime \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"load_models\"},{\"type\":\"text\",\"text\":\" message \"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Hosts can post a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"load_models\"},{\"type\":\"text\",\"text\":\" command after the session has started to fetch and cache extra exercise models on the fly. Once they load, the SDK widens the active allowlist with the new identifiers, so the host can then send a follow-up \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"currentExercise\"},{\"type\":\"text\",\"text\":\" message to switch the user onto one of them.\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"text\":\"Short-form hex colors accepted for brand color\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"3-character hex values such as \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"#fff\\\"\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"0af\\\"\"},{\"type\":\"text\",\"text\":\", or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"abc\\\"\"},{\"type\":\"text\",\"text\":\" (with or without \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"#\"},{\"type\":\"text\",\"text\":\", with surrounding whitespace tolerated) are now expanded to the canonical 6-character form before being applied. Previously these inputs silently rendered as the default green.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": Review — payload additions are backward-compatible, but \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"speech_fetch_complete\"},{\"type\":\"text\",\"text\":\" now fires more frequently and routes through the WebView/parent universal channel.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"text\":\"1. New inbound command: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"load_models\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"⚠️ Dispatch field is \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_activity_action\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" with value \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"load_models\"},{\"type\":\"text\",\"text\":\" . Please ensure to include updated exercises array and exerciseFetchType alongside the workout_activity_action command. See examples below \"}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Host → SDK\"},{\"type\":\"text\",\"text\":\" — Tells the SDK to fetch and cache additional exercise models mid-session. The SDK posts back \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"models_loaded\"},{\"type\":\"text\",\"text\":\" and/or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"error_occurred\"},{\"type\":\"text\",\"text\":\" and widens the active exercise allowlist with whichever identifiers actually resolved.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Payload\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"json\"},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"workout_activity_action\\\": \\\"load_models\\\",\\n  \\\"exercises\\\": [\\\"Squats\\\", \\\"Lunges\\\"],\\n  \\\"exerciseFetchType\\\": \\\"exercise_title\\\"\\n}\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_activity_action\"},{\"type\":\"text\",\"text\":\" (required): Must be the literal string \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"load_models\\\"\"},{\"type\":\"text\",\"text\":\".\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercises\"},{\"type\":\"text\",\"text\":\" (required): Non-empty array of identifiers. Values are stringified before use and must match the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exerciseFetchType\"},{\"type\":\"text\",\"text\":\" you choose.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exerciseFetchType\"},{\"type\":\"text\",\"text\":\" (optional): Controls how the backend interprets each entry in \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercises\"},{\"type\":\"text\",\"text\":\". One of \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"model_id\\\"\"},{\"type\":\"text\",\"text\":\" (default), \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exercise_id\\\"\"},{\"type\":\"text\",\"text\":\", or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exercise_title\\\"\"},{\"type\":\"text\",\"text\":\". Invalid values are silently ignored and the backend default is used.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"If \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercises\"},{\"type\":\"text\",\"text\":\" is empty or missing, the SDK posts:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"json\"},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"error_occurred\\\", \\\"message\\\": \\\"load_models: no exercises provided\\\" }\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Outcome events\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"After the fetch resolves, the SDK posts \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"one or both\"},{\"type\":\"text\",\"text\":\" of:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"json\"},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"models_loaded\\\", \\\"message\\\": \\\"Successfully loaded 2 model(s)\\\", \\\"modelIds\\\": [\\\"Squats\\\", \\\"Lunges\\\"] }\\n\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"json\"},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"error_occurred\\\", \\\"message\\\": \\\"<per-identifier failure reason>\\\" }\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"modelIds\"},{\"type\":\"text\",\"text\":\" echoes back the identifiers in the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"same form you supplied\"},{\"type\":\"text\",\"text\":\" (model id, exercise id, or title — whichever matches \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exerciseFetchType\"},{\"type\":\"text\",\"text\":\").\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"text\":\"2. End-to-end flow: add a new exercise and switch the user onto it\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Two messages, in order.\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"load_models\"},{\"type\":\"text\",\"text\":\" only fetches and widens the allowlist — it does \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"not\"},{\"type\":\"text\",\"text\":\" auto-switch the active exercise. You must send a second message with \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"currentExercise\"},{\"type\":\"text\",\"text\":\" after \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"models_loaded\"},{\"type\":\"text\",\"text\":\" fires.\"}]}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"text\"},\"content\":[{\"type\":\"text\",\"text\":\"┌──────────────────────────────────────────────────────────────────────┐\\n│ Step 1 — Host fetches new models                                     │\\n└──────────────────────────────────────────────────────────────────────┘\\n  Host → SDK:\\n    { \\\"workout_activity_action\\\": \\\"load_models\\\",\\n      \\\"exercises\\\": [\\\"Squats\\\", \\\"Jumping Jack\\\"],\\n      \\\"exerciseFetchType\\\": \\\"exercise_title\\\" }\\n\\n┌──────────────────────────────────────────────────────────────────────┐\\n│ Step 2 — SDK confirms fetch + caching                                │\\n└──────────────────────────────────────────────────────────────────────┘\\n  SDK → Host:\\n    { \\\"type\\\": \\\"models_loaded\\\",\\n      \\\"message\\\": \\\"Successfully loaded 2 model(s)\\\",\\n      \\\"modelIds\\\": [\\\"Squats\\\", \\\"Jumping Jack\\\"] }\\n  SDK → Host (async, after mistake-feedback audio caches):\\n    { \\\"type\\\": \\\"speech_fetch_complete\\\",\\n      \\\"successCount\\\": 2, \\\"failureCount\\\": 0,\\n      \\\"modelIds\\\": [\\\"Squats\\\", \\\"Jumping Jack\\\"] }\\n\\n┌──────────────────────────────────────────────────────────────────────┐\\n│ Step 3 — Host switches the active exercise                           │\\n└──────────────────────────────────────────────────────────────────────┘\\n  Host → SDK:\\n    { \\\"currentExercise\\\": \\\"Jumping Jack\\\" }\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"❗Rules for the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"currentExercise\"},{\"type\":\"text\",\"text\":\" follow-up\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Wait for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"models_loaded\"},{\"type\":\"text\",\"text\":\" (with matching \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"modelIds\"},{\"type\":\"text\",\"text\":\") before sending \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"currentExercise\"},{\"type\":\"text\",\"text\":\". Sending it earlier will produce \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"error_occurred: \\\"currentExercise is not loaded\\\"\"},{\"type\":\"text\",\"text\":\".\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The identifier you pass to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"currentExercise\"},{\"type\":\"text\",\"text\":\" must be one that was acknowledged in \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"modelIds\"},{\"type\":\"text\",\"text\":\" — i.e. it must use the same form (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"model_id\"},{\"type\":\"text\",\"text\":\" / \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercise_id\"},{\"type\":\"text\",\"text\":\" / \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"title\"},{\"type\":\"text\",\"text\":\") you originally sent to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"load_models\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exerciseFetchType\"},{\"type\":\"text\",\"text\":\" is \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"not\"},{\"type\":\"text\",\"text\":\" repeated on the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"currentExercise\"},{\"type\":\"text\",\"text\":\" message — it only applies to fetches, not switches.\"}]}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"text\":\"3. New configuration field: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exerciseFetchType\"},{\"type\":\"text\",\"text\":\" (initial verification)\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The initial verification configuration (the message that starts a session) now accepts an optional \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exerciseFetchType\"},{\"type\":\"text\",\"text\":\" alongside \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercises\"},{\"type\":\"text\",\"text\":\". This controls how \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercises\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"and\"},{\"type\":\"text\",\"text\":\" the initial \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"currentExercise\"},{\"type\":\"text\",\"text\":\" are interpreted.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before\"},{\"type\":\"text\",\"text\":\":\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"json\"},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"currentExercise\\\": \\\"abc123\\\",\\n  \\\"exercises\\\": [\\\"abc123\\\", \\\"def456\\\"]\\n}\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After\"},{\"type\":\"text\",\"text\":\" (existing behavior preserved when field omitted):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"json\"},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"currentExercise\\\": \\\"Squats\\\",\\n  \\\"exercises\\\": [\\\"Squats\\\", \\\"Lunges\\\"],\\n  \\\"exerciseFetchType\\\": \\\"exercise_title\\\"\\n}\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Allowed values: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"model_id\\\"\"},{\"type\":\"text\",\"text\":\" (default if omitted), \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exercise_id\\\"\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exercise_title\\\"\"},{\"type\":\"text\",\"text\":\". With \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exercise_title\\\"\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"currentExercise\"},{\"type\":\"text\",\"text\":\" may be the title of the exercise.\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Consistency tip\"},{\"type\":\"text\",\"text\":\": For a given session, pick one \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exerciseFetchType\"},{\"type\":\"text\",\"text\":\" and use it for both the initial verification and any subsequent \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"load_models\"},{\"type\":\"text\",\"text\":\" calls. The SDK does store aliases, so a model fetched by title is also reachable by its canonical id — but mixing forms in your own host code is a common source of \\\"currentExercise is not loaded\\\" errors.\"}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"text\":\"4. \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"models_loaded\"},{\"type\":\"text\",\"text\":\" now carries a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"modelIds\"},{\"type\":\"text\",\"text\":\" field\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Emitted after a model fetch (initial load \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"or\"},{\"type\":\"text\",\"text\":\" runtime \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"load_models\"},{\"type\":\"text\",\"text\":\") completes successfully. Now echoes back exactly which identifiers loaded, using the same form the host originally supplied.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before\"},{\"type\":\"text\",\"text\":\":\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"json\"},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"models_loaded\\\", \\\"message\\\": \\\"Successfully loaded 2 model(s)\\\" }\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After\"},{\"type\":\"text\",\"text\":\":\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"json\"},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"models_loaded\\\", \\\"message\\\": \\\"Successfully loaded 2 model(s)\\\", \\\"modelIds\\\": [\\\"Squats\\\", \\\"Lunges\\\"] }\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": Additive — strict parsers that reject unknown fields should be relaxed. Listeners that previously only used \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"message\"},{\"type\":\"text\",\"text\":\" continue to work. Use \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"modelIds\"},{\"type\":\"text\",\"text\":\" to disambiguate which \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"load_models\"},{\"type\":\"text\",\"text\":\" request this event corresponds to when multiple are in flight.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"text\":\"5. \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"speech_fetch_complete\"},{\"type\":\"text\",\"text\":\" now also fires after model-audio caching\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Previously this event fired only after the rest-speech batch finished downloading. It now also fires after a model fetch finishes caching its mistake-feedback audio. The two contexts are distinguishable by the presence of \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"modelIds\"},{\"type\":\"text\",\"text\":\".\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Rest-speech batch (unchanged)\"},{\"type\":\"text\",\"text\":\":\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"json\"},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"speech_fetch_complete\\\", \\\"successCount\\\": 12, \\\"failureCount\\\": 0 }\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Model-audio batch (new emission)\"},{\"type\":\"text\",\"text\":\":\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":\"json\"},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"speech_fetch_complete\\\", \\\"successCount\\\": 2, \\\"failureCount\\\": 0, \\\"modelIds\\\": [\\\"Squats\\\", \\\"Lunges\\\"] }\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": If your handler assumes one \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"speech_fetch_complete\"},{\"type\":\"text\",\"text\":\" per session, branch on \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"modelIds\"},{\"type\":\"text\",\"text\":\": when present, the event refers to model audio for the listed identifiers; when absent, it refers to the rest-speech batch.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"text\":\"6. \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"speech_fetch_complete\"},{\"type\":\"text\",\"text\":\" and speech-related \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"error_occurred\"},{\"type\":\"text\",\"text\":\" now use the universal channel\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"These events previously dispatched only on the browser/window channel. They now route through the SDK's universal post-message channel, so WebView hosts (React Native, iOS WKWebView, Flutter, custom \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"messageHandler\"},{\"type\":\"text\",\"text\":\") that previously did not receive them will now receive them on their standard listener.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Event shapes are unchanged — only the delivery channel changed.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": WebView hosts should ensure their universal listener can tolerate (or explicitly handle) \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"speech_fetch_complete\"},{\"type\":\"text\",\"text\":\" and speech-related \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"error_occurred\"},{\"type\":\"text\",\"text\":\" events. Web/iframe hosts using \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"window.addEventListener(\\\"message\\\", ...)\"},{\"type\":\"text\",\"text\":\" continue to receive them as before.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"text\":\"Quick Reference — Host Integration Checklist\"}]},{\"type\":\"orderedList\",\"attrs\":{\"start\":1,\"type\":null},\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Initial session\"},{\"type\":\"text\",\"text\":\": include \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exerciseFetchType\"},{\"type\":\"text\",\"text\":\" in your verification message if you want to use titles or exercise IDs in \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercises[]\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"currentExercise\"},{\"type\":\"text\",\"text\":\".\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Add a new exercise mid-session\"},{\"type\":\"text\",\"text\":\": post \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"{ workout_activity_action: \\\"load_models\\\", exercises: [...], exerciseFetchType: \\\"...\\\" }\"},{\"type\":\"text\",\"text\":\".\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Wait\"},{\"type\":\"text\",\"text\":\" for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"models_loaded\"},{\"type\":\"text\",\"text\":\" with a matching \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"modelIds\"},{\"type\":\"text\",\"text\":\" array. Optionally also wait for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"speech_fetch_complete\"},{\"type\":\"text\",\"text\":\" (with \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"modelIds\"},{\"type\":\"text\",\"text\":\") if you want mistake-feedback audio fully cached before switching.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Switch the user\"},{\"type\":\"text\",\"text\":\": post \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"{ currentExercise: \\\"<one of the modelIds>\\\" }\"},{\"type\":\"text\",\"text\":\". Do \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"not\"},{\"type\":\"text\",\"text\":\" repeat \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exerciseFetchType\"},{\"type\":\"text\",\"text\":\" on this message.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Handle partial failures\"},{\"type\":\"text\",\"text\":\": if \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"error_occurred\"},{\"type\":\"text\",\"text\":\" arrives alongside \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"models_loaded\"},{\"type\":\"text\",\"text\":\", the resolved subset is still switchable; retry only the failed identifiers.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations</p><hr><h2>New Features</h2><h3>Load exercises by title or exercise id</h3><p>The initial verification configuration now accepts an optional <code>exerciseFetchType</code> field with values <code>\"model_id\"</code> (default), <code>\"exercise_id\"</code>, or <code>\"exercise_title\"</code>. Title matching is <strong>case-sensitive</strong>. When the field is omitted, the SDK behaves exactly as before (model-id lookup).</p><h3>Runtime&nbsp;<code>load_models</code>&nbsp;message </h3><p>Hosts can post a <code>load_models</code> command after the session has started to fetch and cache extra exercise models on the fly. Once they load, the SDK widens the active allowlist with the new identifiers, so the host can then send a follow-up <code>currentExercise</code> message to switch the user onto one of them.</p><h3>Short-form hex colors accepted for brand color</h3><p>3-character hex values such as <code>\"#fff\"</code>, <code>\"0af\"</code>, or <code>\"abc\"</code> (with or without <code>#</code>, with surrounding whitespace tolerated) are now expanded to the canonical 6-character form before being applied. Previously these inputs silently rendered as the default green.</p><hr><h2>API Changes (PostMessage)</h2><p><strong>Action Required</strong>: Review — payload additions are backward-compatible, but <code>speech_fetch_complete</code> now fires more frequently and routes through the WebView/parent universal channel.</p><hr><h3>1. New inbound command:&nbsp;<code>load_models</code></h3><blockquote><p><strong>⚠️ Dispatch field is </strong><code>workout_activity_action</code><strong> with value </strong><code>load_models</code> . Please ensure to include updated exercises array and exerciseFetchType alongside the workout_activity_action command. See examples below </p></blockquote><p><strong>Host → SDK</strong> — Tells the SDK to fetch and cache additional exercise models mid-session. The SDK posts back <code>models_loaded</code> and/or <code>error_occurred</code> and widens the active exercise allowlist with whichever identifiers actually resolved.</p><p>Payload</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-json\">{\n  \"workout_activity_action\": \"load_models\",\n  \"exercises\": [\"Squats\", \"Lunges\"],\n  \"exerciseFetchType\": \"exercise_title\"\n}\n</code></pre><p><code>workout_activity_action</code> (required): Must be the literal string <code>\"load_models\"</code>.</p><p><code>exercises</code> (required): Non-empty array of identifiers. Values are stringified before use and must match the <code>exerciseFetchType</code> you choose.</p><p><code>exerciseFetchType</code> (optional): Controls how the backend interprets each entry in <code>exercises</code>. One of <code>\"model_id\"</code> (default), <code>\"exercise_id\"</code>, or <code>\"exercise_title\"</code>. Invalid values are silently ignored and the backend default is used.</p><p>If <code>exercises</code> is empty or missing, the SDK posts:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-json\">{ \"type\": \"error_occurred\", \"message\": \"load_models: no exercises provided\" }\n</code></pre><p>Outcome events</p><p>After the fetch resolves, the SDK posts <strong>one or both</strong> of:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-json\">{ \"type\": \"models_loaded\", \"message\": \"Successfully loaded 2 model(s)\", \"modelIds\": [\"Squats\", \"Lunges\"] }\n</code></pre><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-json\">{ \"type\": \"error_occurred\", \"message\": \"&lt;per-identifier failure reason&gt;\" }\n</code></pre><p><code>modelIds</code> echoes back the identifiers in the <strong>same form you supplied</strong> (model id, exercise id, or title — whichever matches <code>exerciseFetchType</code>).</p><hr><h3>2. End-to-end flow: add a new exercise and switch the user onto it</h3><blockquote><p><strong>Two messages, in order.</strong> <code>load_models</code> only fetches and widens the allowlist — it does <strong>not</strong> auto-switch the active exercise. You must send a second message with <code>currentExercise</code> after <code>models_loaded</code> fires.</p></blockquote><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-text\">┌──────────────────────────────────────────────────────────────────────┐\n│ Step 1 — Host fetches new models                                     │\n└──────────────────────────────────────────────────────────────────────┘\n  Host → SDK:\n    { \"workout_activity_action\": \"load_models\",\n      \"exercises\": [\"Squats\", \"Jumping Jack\"],\n      \"exerciseFetchType\": \"exercise_title\" }\n\n┌──────────────────────────────────────────────────────────────────────┐\n│ Step 2 — SDK confirms fetch + caching                                │\n└──────────────────────────────────────────────────────────────────────┘\n  SDK → Host:\n    { \"type\": \"models_loaded\",\n      \"message\": \"Successfully loaded 2 model(s)\",\n      \"modelIds\": [\"Squats\", \"Jumping Jack\"] }\n  SDK → Host (async, after mistake-feedback audio caches):\n    { \"type\": \"speech_fetch_complete\",\n      \"successCount\": 2, \"failureCount\": 0,\n      \"modelIds\": [\"Squats\", \"Jumping Jack\"] }\n\n┌──────────────────────────────────────────────────────────────────────┐\n│ Step 3 — Host switches the active exercise                           │\n└──────────────────────────────────────────────────────────────────────┘\n  Host → SDK:\n    { \"currentExercise\": \"Jumping Jack\" }\n</code></pre><p>❗Rules for the&nbsp;<code>currentExercise</code>&nbsp;follow-up</p><ul><li><p>Wait for&nbsp;<code>models_loaded</code>&nbsp;(with matching&nbsp;<code>modelIds</code>) before sending&nbsp;<code>currentExercise</code>. Sending it earlier will produce&nbsp;<code>error_occurred: \"currentExercise is not loaded\"</code>.</p></li><li><p>The identifier you pass to&nbsp;<code>currentExercise</code>&nbsp;must be one that was acknowledged in&nbsp;<code>modelIds</code>&nbsp;— i.e. it must use the same form (<code>model_id</code>&nbsp;/&nbsp;<code>exercise_id</code>&nbsp;/&nbsp;<code>title</code>) you originally sent to&nbsp;<code>load_models</code></p></li><li><p><code>exerciseFetchType</code>&nbsp;is&nbsp;<strong>not</strong>&nbsp;repeated on the&nbsp;<code>currentExercise</code>&nbsp;message — it only applies to fetches, not switches.</p></li></ul><hr><h3>3. New configuration field:&nbsp;<code>exerciseFetchType</code>&nbsp;(initial verification)</h3><p>The initial verification configuration (the message that starts a session) now accepts an optional <code>exerciseFetchType</code> alongside <code>exercises</code>. This controls how <code>exercises</code> <strong>and</strong> the initial <code>currentExercise</code> are interpreted.</p><p><strong>Before</strong>:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-json\">{\n  \"currentExercise\": \"abc123\",\n  \"exercises\": [\"abc123\", \"def456\"]\n}\n</code></pre><p><strong>After</strong> (existing behavior preserved when field omitted):</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-json\">{\n  \"currentExercise\": \"Squats\",\n  \"exercises\": [\"Squats\", \"Lunges\"],\n  \"exerciseFetchType\": \"exercise_title\"\n}\n</code></pre><p>Allowed values: <code>\"model_id\"</code> (default if omitted), <code>\"exercise_id\"</code>, <code>\"exercise_title\"</code>. With <code>\"exercise_title\"</code>, <code>currentExercise</code> may be the title of the exercise.</p><blockquote><p><strong>Consistency tip</strong>: For a given session, pick one <code>exerciseFetchType</code> and use it for both the initial verification and any subsequent <code>load_models</code> calls. The SDK does store aliases, so a model fetched by title is also reachable by its canonical id — but mixing forms in your own host code is a common source of \"currentExercise is not loaded\" errors.</p></blockquote><hr><h3>4.&nbsp;<code>models_loaded</code>&nbsp;now carries a&nbsp;<code>modelIds</code>&nbsp;field</h3><p>Emitted after a model fetch (initial load <strong>or</strong> runtime <code>load_models</code>) completes successfully. Now echoes back exactly which identifiers loaded, using the same form the host originally supplied.</p><p><strong>Before</strong>:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-json\">{ \"type\": \"models_loaded\", \"message\": \"Successfully loaded 2 model(s)\" }\n</code></pre><p><strong>After</strong>:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-json\">{ \"type\": \"models_loaded\", \"message\": \"Successfully loaded 2 model(s)\", \"modelIds\": [\"Squats\", \"Lunges\"] }\n</code></pre><p><strong>Migration</strong>: Additive — strict parsers that reject unknown fields should be relaxed. Listeners that previously only used <code>message</code> continue to work. Use <code>modelIds</code> to disambiguate which <code>load_models</code> request this event corresponds to when multiple are in flight.</p><hr><h3>5.&nbsp;<code>speech_fetch_complete</code>&nbsp;now also fires after model-audio caching</h3><p>Previously this event fired only after the rest-speech batch finished downloading. It now also fires after a model fetch finishes caching its mistake-feedback audio. The two contexts are distinguishable by the presence of <code>modelIds</code>.</p><p><strong>Rest-speech batch (unchanged)</strong>:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-json\">{ \"type\": \"speech_fetch_complete\", \"successCount\": 12, \"failureCount\": 0 }\n</code></pre><p><strong>Model-audio batch (new emission)</strong>:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code class=\"language-json\">{ \"type\": \"speech_fetch_complete\", \"successCount\": 2, \"failureCount\": 0, \"modelIds\": [\"Squats\", \"Lunges\"] }\n</code></pre><p><strong>Migration</strong>: If your handler assumes one <code>speech_fetch_complete</code> per session, branch on <code>modelIds</code>: when present, the event refers to model audio for the listed identifiers; when absent, it refers to the rest-speech batch.</p><hr><h3>6.&nbsp;<code>speech_fetch_complete</code>&nbsp;and speech-related&nbsp;<code>error_occurred</code>&nbsp;now use the universal channel</h3><p>These events previously dispatched only on the browser/window channel. They now route through the SDK's universal post-message channel, so WebView hosts (React Native, iOS WKWebView, Flutter, custom <code>messageHandler</code>) that previously did not receive them will now receive them on their standard listener.</p><p>Event shapes are unchanged — only the delivery channel changed.</p><p><strong>Migration</strong>: WebView hosts should ensure their universal listener can tolerate (or explicitly handle) <code>speech_fetch_complete</code> and speech-related <code>error_occurred</code> events. Web/iframe hosts using <code>window.addEventListener(\"message\", ...)</code> continue to receive them as before.</p><hr><h2>Quick Reference — Host Integration Checklist</h2><ol><li><p><strong>Initial session</strong>: include&nbsp;<code>exerciseFetchType</code>&nbsp;in your verification message if you want to use titles or exercise IDs in&nbsp;<code>exercises[]</code>&nbsp;and&nbsp;<code>currentExercise</code>.</p></li><li><p><strong>Add a new exercise mid-session</strong>: post&nbsp;<code>{ workout_activity_action: \"load_models\", exercises: [...], exerciseFetchType: \"...\" }</code>.</p></li><li><p><strong>Wait</strong>&nbsp;for&nbsp;<code>models_loaded</code>&nbsp;with a matching&nbsp;<code>modelIds</code>&nbsp;array. Optionally also wait for&nbsp;<code>speech_fetch_complete</code>&nbsp;(with&nbsp;<code>modelIds</code>) if you want mistake-feedback audio fully cached before switching.</p></li><li><p><strong>Switch the user</strong>: post&nbsp;<code>{ currentExercise: \"&lt;one of the modelIds&gt;\" }</code>. Do&nbsp;<strong>not</strong>&nbsp;repeat&nbsp;<code>exerciseFetchType</code>&nbsp;on this message.</p></li><li><p><strong>Handle partial failures</strong>: if&nbsp;<code>error_occurred</code>&nbsp;arrives alongside&nbsp;<code>models_loaded</code>, the resolved subset is still switchable; retry only the failed identifiers.</p></li></ol>","coverImage":null,"category":"new","product":"client","version":"2.31.21","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-13T18:15:09.148Z","publishedAt":"2026-05-13T18:16:04.276Z","status":"published","updatedAt":"2026-05-13T18:16:04.276Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-13T18:16:22.341Z","subscriberCountAtSend":24},{"id":"UX3X0zj942p3nxh0tfi3","summary":"End users will see a loading animation that matches the configured brand color much more closely, a new \"Retry\" bar on the workout results screen if saving the session or plan progress fails","coverImage":null,"category":"new","product":"client","version":"2.31.20","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-12T20:12:18.155Z","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>All Integrations</code>&nbsp;<em>(the loading animation appears app-wide on every entry point; the results-screen changes apply to&nbsp;</em><code>Workout</code><em>,&nbsp;</em><code>Plan</code><em>, and&nbsp;</em><code>Personalized Plan</code><em>)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Retry when an end-of-workout save fails</strong>: If saving a completed workout session and/or plan progression fails after the automatic retries, a bar with a&nbsp;<strong>Retry</strong>&nbsp;button now appears on the workout results screen. Tapping it re-submits only what failed, and the bar disappears once it succeeds. New copy (\"Retry\", \"Retrying…\", \"Couldn't save your workout / progress / workout &amp; progress\") is available in all supported languages.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Accurate brand-color theming for the loading animation</strong>: The full-screen loading animation is now tinted to the configured brand color far more accurately, including for dark or highly saturated colors (e.g. deep purple) that previously rendered as a noticeably wrong color. The default green (when no custom color is configured) is unchanged. Side effect: any internal shading in the animation now renders as a single flat tone.</p></li></ul><h2><strong>Bug Fix</strong></h2><ul><li><p><strong>Fixed assessment audio mismatch: </strong>Audio cues for some assessments and in some languages would be mismatched. We re-generated all audios for all languages and bumped cache-version to force reload audios for all consumers. </p></li></ul>","title":"More accurate brand-color loading animation, Retry on failed workout saves, Assessments fix audio mapping","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"All Integrations\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(the loading animation appears app-wide on every entry point; the results-screen changes apply to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\", and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\")\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Retry when an end-of-workout save fails\"},{\"type\":\"text\",\"text\":\": If saving a completed workout session and/or plan progression fails after the automatic retries, a bar with a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Retry\"},{\"type\":\"text\",\"text\":\" button now appears on the workout results screen. Tapping it re-submits only what failed, and the bar disappears once it succeeds. New copy (\\\"Retry\\\", \\\"Retrying…\\\", \\\"Couldn't save your workout / progress / workout & progress\\\") is available in all supported languages.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Accurate brand-color theming for the loading animation\"},{\"type\":\"text\",\"text\":\": The full-screen loading animation is now tinted to the configured brand color far more accurately, including for dark or highly saturated colors (e.g. deep purple) that previously rendered as a noticeably wrong color. The default green (when no custom color is configured) is unchanged. Side effect: any internal shading in the animation now renders as a single flat tone.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fix\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Fixed assessment audio mismatch: \"},{\"type\":\"text\",\"text\":\"Audio cues for some assessments and in some languages would be mismatched. We re-generated all audios for all languages and bumped cache-version to force reload audios for all consumers. \"}]}]}]}]}","slug":"more-accurate-brand-color-loading-animation-retry-on-failed-workout-saves-assess","publishedAt":"2026-05-12T20:15:39.596Z","status":"published","updatedAt":"2026-05-12T20:15:39.596Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-12T20:15:55.251Z","subscriberCountAtSend":24},{"id":"P6Dye9rcwWuzKZAGYPQ5","title":"New Greeting & Farewell Speech, includePhrases Config Option, Updated SLS Assessment Flow","slug":"new-greeting-farewell-speech-includephrases-config-option-updated-sls-assessment","summary":"Workouts and challenges will play (COMING SOON) a short greeting audio cue at the start and a farewell cue on the completion screen. A new optional embed config field, includePhrases, lets you control which audio phrase categories get downloaded. The single-leg-stance assessment now announces a break between legs. No PostMessage events changed — no integration code changes are required unless you want to opt into includePhrases.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(shared audio/phrase handling and the embed configuration interface changed)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"(COMING SOON) Greeting & farewell audio cues\"},{\"type\":\"text\",\"text\":\": At the start of a workout or challenge, a brief greeting now plays during the first rest period (the first rest countdown is automatically extended by ~3 seconds so it fits). When the workout-completion screen appears, a farewell message plays. Both replay on each workout within the same session.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"includePhrases\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" embed config option\"},{\"type\":\"text\",\"text\":\": New optional parameter in the embed configuration data — an array naming which audio phrase categories to download and use. Allowed values: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"motivational\\\"\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"praisal\\\"\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"greeting\\\"\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"farewell\\\"\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"closing_exercise\\\"\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"auto_skip\\\"\"},{\"type\":\"text\",\"text\":\". Omit the field or pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"null\"},{\"type\":\"text\",\"text\":\" to keep the default (all categories downloaded). Use this to shrink audio download size when certain cues aren't needed. Please use with caution as certain audios are important for proper UX.\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"includePhrases\\\": [\\\"motivational\\\", \\\"praisal\\\", \\\"greeting\\\", \\\"farewell\\\"]\\n}\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Single-leg stance (SLS) assessment\"},{\"type\":\"text\",\"text\":\": After the first leg is finished, the assessment now says \\\"Right side done. Take a break as needed.\\\" (and the mirror phrase for the left side), giving users a moment to rest before switching legs. \"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Audio loading reliability\"},{\"type\":\"text\",\"text\":\": Greeting/farewell audio is now downloaded before the workout loading screen completes, so playback is ready when the workout starts. Includes additional audio playback edge-case fixes.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment spoken instructions\"},{\"type\":\"text\",\"text\":\": Clarified the voice instructions for the single-leg stance and functional reach (FRT) assessments.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — no PostMessage events, payloads, or field names changed in this release. The only integrator-facing change is the new optional \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"includePhrases\"},{\"type\":\"text\",\"text\":\" field in the embed configuration data (see New Features above).\"}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations<br><em>(shared audio/phrase handling and the embed configuration interface changed)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>(COMING SOON) Greeting &amp; farewell audio cues</strong>: At the start of a workout or challenge, a brief greeting now plays during the first rest period (the first rest countdown is automatically extended by ~3 seconds so it fits). When the workout-completion screen appears, a farewell message plays. Both replay on each workout within the same session.</p></li><li><p><code>includePhrases</code><strong>&nbsp;embed config option</strong>: New optional parameter in the embed configuration data — an array naming which audio phrase categories to download and use. Allowed values:&nbsp;<code>\"motivational\"</code>,&nbsp;<code>\"praisal\"</code>,&nbsp;<code>\"greeting\"</code>,&nbsp;<code>\"farewell\"</code>,&nbsp;<code>\"closing_exercise\"</code>,&nbsp;<code>\"auto_skip\"</code>. Omit the field or pass&nbsp;<code>null</code>&nbsp;to keep the default (all categories downloaded). Use this to shrink audio download size when certain cues aren't needed. Please use with caution as certain audios are important for proper UX.</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"includePhrases\": [\"motivational\", \"praisal\", \"greeting\", \"farewell\"]\n}</code></pre></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Single-leg stance (SLS) assessment</strong>: After the first leg is finished, the assessment now says \"Right side done. Take a break as needed.\" (and the mirror phrase for the left side), giving users a moment to rest before switching legs. </p></li><li><p><strong>Audio loading reliability</strong>: Greeting/farewell audio is now downloaded before the workout loading screen completes, so playback is ready when the workout starts. Includes additional audio playback edge-case fixes.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Assessment spoken instructions</strong>: Clarified the voice instructions for the single-leg stance and functional reach (FRT) assessments.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — no PostMessage events, payloads, or field names changed in this release. The only integrator-facing change is the new optional&nbsp;<code>includePhrases</code>&nbsp;field in the embed configuration data (see New Features above).</p></blockquote>","coverImage":null,"category":"new","product":"client","version":"2.31.19","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-11T19:31:00.350Z","publishedAt":"2026-05-11T19:31:05.446Z","status":"published","updatedAt":"2026-05-11T19:31:05.446Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-11T19:31:21.287Z","subscriberCountAtSend":24},{"id":"Kmqu7MbXZH8ZxLs5nGNs","title":"Accurate Workout Session Duration","slug":"accurate-workout-session-duration","summary":"Fixes workout session replay to show the correct total duration including rest periods and transitions. No PostMessage or integration API changes — no code changes required by integrators.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout session replay duration\"},{\"type\":\"text\",\"text\":\": The recorded workout session now correctly reflects the full wall-clock duration (including rest periods, pauses, and exercise transitions). Previously, only active exercise time was recorded, causing the session replay to appear shorter than the workout the user experienced live.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code></p><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Workout session replay duration</strong>: The recorded workout session now correctly reflects the full wall-clock duration (including rest periods, pauses, and exercise transitions). Previously, only active exercise time was recorded, causing the session replay to appear shorter than the workout the user experienced live.</p></li></ul>","coverImage":null,"category":"fixed","product":"client","version":"2.31.18","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-08T17:29:44.073Z","publishedAt":"2026-05-08T17:29:49.258Z","status":"published","updatedAt":"2026-05-08T17:29:49.258Z"},{"id":"UnVIIefSv5i9TkxToksr","title":"New plan_workout_selected Event, Server-Driven Motivational Phrases with Voice Actor Support","slug":"new-plan-workout-selected-event-server-driven-motivational-phrases-with-voice-ac","summary":"This release adds a new plan_workout_selected PostMessage event so integrators can track when users pick a workout from within a plan. It also introduces server-driven audio phrases (motivational, praisal, greeting, farewell, and more) fetched from the API with optional voice actor selection — replacing the previous static audio. No breaking changes; all new capabilities are additive.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(Shared audio infrastructure changed; \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\" additionally receive the new PostMessage event)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Server-driven audio phrases\"},{\"type\":\"text\",\"text\":\": Motivational, praisal, greeting, farewell, closing-exercise, and auto-skip phrases are now delivered by the API with pre-signed audio URLs. This enables per-company phrase customization and faster updates without SDK redeployment. You can create and customize phrases in admin dashboard. Falls back gracefully to the previous audio source if the API response is unavailable.\"}]},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/d0f3439b-87d3-4bb5-980f-235974e2fc3a.png\",\"alt\":null,\"title\":null}}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_workout_selected\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" event\"},{\"type\":\"text\",\"text\":\": A new PostMessage event is emitted when a user selects a workout from within a plan view, giving integrators visibility into plan-to-workout navigation.\"}]}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — all changes are additive. Existing integrations continue to work without modification.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_workout_selected\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Emitted when a user taps a workout card while browsing a plan. This event is only fired when the workout is accessed from a plan context (not from standalone workout views).\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Payload:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"plan_workout_selected\\\",\\n  \\\"workout_id\\\": \\\"workout_abc123\\\",\\n  \\\"workout_title\\\": \\\"Full Body Strength\\\",\\n  \\\"plan_id\\\": \\\"plan_xyz789\\\",\\n  \\\"plan_title\\\": \\\"4-Week Beginner Program\\\"\\n}\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"type:\"},{\"type\":\"text\",\"text\":\" string — always \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"plan_workout_selected\\\"\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"workout_id:\"},{\"type\":\"text\",\"text\":\" string — ID of the selected workout\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"workout_title:\"},{\"type\":\"text\",\"text\":\" string — display title of the selected workout\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"plan_id:\"},{\"type\":\"text\",\"text\":\" string — ID of the parent plan\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"plan_title:\"},{\"type\":\"text\",\"text\":\" string — display title of the parent plan\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required. Subscribe to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_workout_selected\"},{\"type\":\"text\",\"text\":\" if you want to track plan-to-workout navigation in your analytics or trigger custom behavior when a user starts a workout from a plan.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations<br><em>(Shared audio infrastructure changed;&nbsp;</em><code>Plan</code><em>&nbsp;and&nbsp;</em><code>Workout</code><em>&nbsp;additionally receive the new PostMessage event)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Server-driven audio phrases</strong>: Motivational, praisal, greeting, farewell, closing-exercise, and auto-skip phrases are now delivered by the API with pre-signed audio URLs. This enables per-company phrase customization and faster updates without SDK redeployment. You can create and customize phrases in admin dashboard. Falls back gracefully to the previous audio source if the API response is unavailable.</p><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/d0f3439b-87d3-4bb5-980f-235974e2fc3a.png\"></li><li><p><code>plan_workout_selected</code><strong>&nbsp;event</strong>: A new PostMessage event is emitted when a user selects a workout from within a plan view, giving integrators visibility into plan-to-workout navigation.</p></li></ul><hr><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — all changes are additive. Existing integrations continue to work without modification.</p></blockquote><h3><strong>New event:&nbsp;</strong><code>plan_workout_selected</code></h3><p>Emitted when a user taps a workout card while browsing a plan. This event is only fired when the workout is accessed from a plan context (not from standalone workout views).</p><p><strong>Payload:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"plan_workout_selected\",\n  \"workout_id\": \"workout_abc123\",\n  \"workout_title\": \"Full Body Strength\",\n  \"plan_id\": \"plan_xyz789\",\n  \"plan_title\": \"4-Week Beginner Program\"\n}</code></pre><ul><li><p><strong>type:</strong> string — always <code>\"plan_workout_selected\"</code></p></li><li><p><strong>workout_id:</strong> string — ID of the selected workout</p></li><li><p><strong>workout_title:</strong> string — display title of the selected workout</p></li><li><p><strong>plan_id:</strong> string — ID of the parent plan</p></li><li><p><strong>plan_title:</strong> string — display title of the parent plan</p></li></ul><p><strong>Migration</strong>: None required. Subscribe to&nbsp;<code>plan_workout_selected</code>&nbsp;if you want to track plan-to-workout navigation in your analytics or trigger custom behavior when a user starts a workout from a plan.</p>","coverImage":null,"category":"new","product":"client","version":"2.31.17","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-07T14:35:59.402Z","publishedAt":"2026-05-07T14:36:08.254Z","status":"published","updatedAt":"2026-05-07T14:36:08.254Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-07T14:36:28.554Z","subscriberCountAtSend":23},{"id":"f4AGVyGlh6i1DcCP048d","title":"Gait Speed Countdown UX, Improved Model Loading Reliability, Deployment Stability","slug":"gait-speed-countdown-ux-improved-model-loading-reliability-deployment-stability","summary":"This release adds a 3-2-1 countdown with a directional knee arrow to the Gait Speed assessment, making the test start clearer for users. It also fixes a model-loading race condition that could cause exercise tracking to fail on first load, and adds deployment-level asset protection so users with cached pages won't encounter broken screens after a new deploy. No PostMessage API changes — no integration code changes required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(Shared camera hook, model loading service, and build infrastructure changes affect all integration types)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Gait Speed countdown\"},{\"type\":\"text\",\"text\":\": After passing the initial position and space calibration, users now see a 3-2-1 countdown with a \\\"Get Ready!\\\" message on a green panel before the walking phase begins. Previously, the walking phase started immediately after calibration with no preparation time.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Directional knee arrow\"},{\"type\":\"text\",\"text\":\": During the countdown, an animated yellow arrow appears near the user's knee on the camera view, pointing toward the target walking zone. The arrow disappears once the user starts walking, at which point the standard bottom-panel direction indicator takes over.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise model loading reliability\"},{\"type\":\"text\",\"text\":\": Fixed a race condition where the motion analysis model could fail to load if the underlying ML runtime hadn't finished initializing. The system now waits for the runtime to be ready (up to ~10 seconds) before attempting to load models, preventing opaque errors on first use.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Post-deployment asset loading\"},{\"type\":\"text\",\"text\":\": Added deployment-aware asset versioning so that users with a cached page from a previous deployment can still load all screen content after a new deploy goes live. Previously, navigating to a new screen after a deploy could result in missing assets.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — no PostMessage events, payloads, or field names were changed in this release.\"}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations<br><em>(Shared camera hook, model loading service, and build infrastructure changes affect all integration types)</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Gait Speed countdown</strong>: After passing the initial position and space calibration, users now see a 3-2-1 countdown with a \"Get Ready!\" message on a green panel before the walking phase begins. Previously, the walking phase started immediately after calibration with no preparation time.</p></li><li><p><strong>Directional knee arrow</strong>: During the countdown, an animated yellow arrow appears near the user's knee on the camera view, pointing toward the target walking zone. The arrow disappears once the user starts walking, at which point the standard bottom-panel direction indicator takes over.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Exercise model loading reliability</strong>: Fixed a race condition where the motion analysis model could fail to load if the underlying ML runtime hadn't finished initializing. The system now waits for the runtime to be ready (up to ~10 seconds) before attempting to load models, preventing opaque errors on first use.</p></li><li><p><strong>Post-deployment asset loading</strong>: Added deployment-aware asset versioning so that users with a cached page from a previous deployment can still load all screen content after a new deploy goes live. Previously, navigating to a new screen after a deploy could result in missing assets.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — no PostMessage events, payloads, or field names were changed in this release.</p></blockquote>","coverImage":null,"category":"improved","product":"client","version":"2.31.16","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-07T10:00:06.258Z","publishedAt":"2026-05-07T10:00:24.123Z","status":"published","updatedAt":"2026-05-07T10:00:24.123Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-07T10:00:40.076Z","subscriberCountAtSend":23},{"id":"pXAFYFzl7lF52slxWnpq","title":"Workout Rounds — organize workout sequences into grouped rounds","slug":"workout-rounds-organize-workout-sequences-into-grouped-rounds","summary":"Workout sequences can now be organized into rounds — collapsible groups of exercises and rest periods that replace the previous flat list. This affects all KinesteX team members and client company admins who create or edit workouts. No manual migration is required — existing workouts automatically load with all items grouped into Round 1.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout Rounds\"},{\"type\":\"text\",\"text\":\": Workouts can now be organized into multiple rounds. Each round is a collapsible group with its own \\\"Add Exercise,\\\" \\\"Add Rest,\\\" duplicate, and delete controls. You can add new rounds, duplicate existing ones, and reorder them by dragging.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Cross-round drag and drop\"},{\"type\":\"text\",\"text\":\": Exercises and rest periods can be dragged between rounds, with visual insertion lines showing exactly where items will land and ghost previews while dragging.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Drag-and-drop experience\"},{\"type\":\"text\",\"text\":\": Reordering exercises within and across rounds now feels smoother with insertion-line indicators, lower activation distance, and cleaner drag previews.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Multi-exercise insert order\"},{\"type\":\"text\",\"text\":\": When adding multiple exercises from the exercise list, they now insert in the correct sequential order and the list scrolls to the last added item.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Validation feedback\"},{\"type\":\"text\",\"text\":\": Empty rounds are highlighted with a scroll-to and pulse animation, making it easy to locate and fix issues before saving.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Round-aware auto-rest\"},{\"type\":\"text\",\"text\":\": A rest period is automatically inserted between rounds when needed — for example, when the last item of one round is an exercise and the next round starts with one too.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise replacement at first position\"},{\"type\":\"text\",\"text\":\": Replacing the very first exercise in a round (position 0) now works correctly — previously this action silently failed.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Rest period audio preserved on save\"},{\"type\":\"text\",\"text\":\": Rest period audio (custom and auto-generated) was being silently dropped when saving workouts. This is now fixed, so all rest audio persists correctly after save and reload.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Override media priority\"},{\"type\":\"text\",\"text\":\": When a workout exercise has both a default and an overriding video or thumbnail, the override now correctly takes priority. Previously the default media was sent instead of the override.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout duplication and creation\"},{\"type\":\"text\",\"text\":\": Duplicating or creating a workout no longer carries over internal database IDs from the source, preventing accidental overwrites of the original workout's data.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Breaking Changes\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — existing workouts are automatically grouped into Round 1 on load. However, some workouts may need minor edits before re-saving due to stricter validation.\"}]}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Per-round validation is stricter\"},{\"type\":\"text\",\"text\":\": Each round must now contain at least one exercise, and no round can end with a rest period. Previously only the very last item of the entire workout was checked. Existing workouts where a mid-workout section ends with a rest will need that rest moved before the workout can be re-saved.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Outro must be in the last round\"},{\"type\":\"text\",\"text\":\": The outro (if present) must now be the final item in the last round. If you reorder rounds so the outro is no longer last, validation will flag it.\"}]}]}]}]}","contentHtml":"<blockquote><p><strong>Impacted areas</strong>:&nbsp;<code>Workouts</code>&nbsp;·&nbsp;<code>Exercises</code></p></blockquote><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Workout Rounds</strong>: Workouts can now be organized into multiple rounds. Each round is a collapsible group with its own \"Add Exercise,\" \"Add Rest,\" duplicate, and delete controls. You can add new rounds, duplicate existing ones, and reorder them by dragging.</p></li><li><p><strong>Cross-round drag and drop</strong>: Exercises and rest periods can be dragged between rounds, with visual insertion lines showing exactly where items will land and ghost previews while dragging.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Drag-and-drop experience</strong>: Reordering exercises within and across rounds now feels smoother with insertion-line indicators, lower activation distance, and cleaner drag previews.</p></li><li><p><strong>Multi-exercise insert order</strong>: When adding multiple exercises from the exercise list, they now insert in the correct sequential order and the list scrolls to the last added item.</p></li><li><p><strong>Validation feedback</strong>: Empty rounds are highlighted with a scroll-to and pulse animation, making it easy to locate and fix issues before saving.</p></li><li><p><strong>Round-aware auto-rest</strong>: A rest period is automatically inserted between rounds when needed — for example, when the last item of one round is an exercise and the next round starts with one too.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Exercise replacement at first position</strong>: Replacing the very first exercise in a round (position 0) now works correctly — previously this action silently failed.</p></li><li><p><strong>Rest period audio preserved on save</strong>: Rest period audio (custom and auto-generated) was being silently dropped when saving workouts. This is now fixed, so all rest audio persists correctly after save and reload.</p></li><li><p><strong>Override media priority</strong>: When a workout exercise has both a default and an overriding video or thumbnail, the override now correctly takes priority. Previously the default media was sent instead of the override.</p></li><li><p><strong>Workout duplication and creation</strong>: Duplicating or creating a workout no longer carries over internal database IDs from the source, preventing accidental overwrites of the original workout's data.</p></li></ul><h2><strong>Breaking Changes</strong></h2><blockquote><p><strong>Action Required</strong>: No — existing workouts are automatically grouped into Round 1 on load. However, some workouts may need minor edits before re-saving due to stricter validation.</p></blockquote><ul><li><p><strong>Per-round validation is stricter</strong>: Each round must now contain at least one exercise, and no round can end with a rest period. Previously only the very last item of the entire workout was checked. Existing workouts where a mid-workout section ends with a rest will need that rest moved before the workout can be re-saved.</p></li><li><p><strong>Outro must be in the last round</strong>: The outro (if present) must now be the final item in the last round. If you reorder rounds so the outro is no longer last, validation will flag it.</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"2.20.16","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-07T08:54:09.599Z","publishedAt":"2026-05-07T08:54:13.791Z","status":"published","updatedAt":"2026-05-07T08:54:13.791Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-07T08:54:26.746Z","subscriberCountAtSend":23},{"id":"dwoYak2DzD5Ii7oAXFyd","title":"Overhauled Tandem Balance Tests: New Countdown, Visual Foot Guides, Improved Stance Detection & Risk Scoring","slug":"overhauled-tandem-balance-tests-new-countdown-visual-foot-guides-improved-stance","summary":"This release rebuilds the Full Tandem Stand and Semi-Tandem (STSS) balance assessments with significantly more reliable stance detection, a new 3-2-1-Go countdown before each test begins, and real-time visual foot-position guides. No PostMessage or API changes — integrators do not need to update any code. End users (patients) will see a noticeably smoother and more accurate assessment experience.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"3-2-1-Go countdown for tandem balance tests\"},{\"type\":\"text\",\"text\":\": After the patient achieves the correct stance during setup, a visible 3-2-1-Go countdown now plays before the timed test begins, giving the patient a clear moment to stabilize.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Dynamic feet-boundary box overlay\"},{\"type\":\"text\",\"text\":\": Real-time colored guide rectangles are now drawn around each leg during setup. Boxes turn green when feet are correctly positioned and red when adjustment is needed, giving the patient immediate visual feedback on their stance alignment.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Faster and more reliable tandem stance detection\"},{\"type\":\"text\",\"text\":\": The stance detection algorithm was replaced with a simpler geometric overlap model — if the on-screen leg guide boxes overlap, the stance is accepted. This eliminates false negatives that previously left patients standing correctly for 6+ seconds without the test starting, especially at floor camera angles.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Consistent risk classification across all balance tests\"},{\"type\":\"text\",\"text\":\": Full Tandem results now factor in both accumulated valid-stance time and sway magnitude (shoulder + hip shift) when determining low/moderate/high risk, matching the model already used by Semi-Tandem and Side-by-Side assessments. Previously, Full Tandem only used raw elapsed time, which over-credited patients on early termination.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Adaptive status text sizing\"},{\"type\":\"text\",\"text\":\": On-screen instruction and status messages now scale their font size based on message length, ensuring long translations remain fully readable instead of being truncated or overflowing.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Leg guides remain visible during the timed test\"},{\"type\":\"text\",\"text\":\": The colored foot-position rectangles now stay on screen during the 10-second running phase so patients can confirm at a glance they are still holding the correct stance.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Jitter-tolerant stance validation\"},{\"type\":\"text\",\"text\":\": Single-frame landmark noise from MediaPipe no longer resets detection progress. A 10-frame (~333 ms) hysteresis buffer absorbs transient spikes during both the setup and countdown phases without affecting real stance-break detection.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Assessments</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>3-2-1-Go countdown for tandem balance tests</strong>: After the patient achieves the correct stance during setup, a visible 3-2-1-Go countdown now plays before the timed test begins, giving the patient a clear moment to stabilize.</p></li><li><p><strong>Dynamic feet-boundary box overlay</strong>: Real-time colored guide rectangles are now drawn around each leg during setup. Boxes turn green when feet are correctly positioned and red when adjustment is needed, giving the patient immediate visual feedback on their stance alignment.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Faster and more reliable tandem stance detection</strong>: The stance detection algorithm was replaced with a simpler geometric overlap model — if the on-screen leg guide boxes overlap, the stance is accepted. This eliminates false negatives that previously left patients standing correctly for 6+ seconds without the test starting, especially at floor camera angles.</p></li><li><p><strong>Consistent risk classification across all balance tests</strong>: Full Tandem results now factor in both accumulated valid-stance time and sway magnitude (shoulder + hip shift) when determining low/moderate/high risk, matching the model already used by Semi-Tandem and Side-by-Side assessments. Previously, Full Tandem only used raw elapsed time, which over-credited patients on early termination.</p></li><li><p><strong>Adaptive status text sizing</strong>: On-screen instruction and status messages now scale their font size based on message length, ensuring long translations remain fully readable instead of being truncated or overflowing.</p></li><li><p><strong>Leg guides remain visible during the timed test</strong>: The colored foot-position rectangles now stay on screen during the 10-second running phase so patients can confirm at a glance they are still holding the correct stance.</p></li><li><p><strong>Jitter-tolerant stance validation</strong>: Single-frame landmark noise from MediaPipe no longer resets detection progress. A 10-frame (~333 ms) hysteresis buffer absorbs transient spikes during both the setup and countdown phases without affecting real stance-break detection.</p></li></ul>","coverImage":null,"category":"improved","product":"client","version":"2.31.15","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-06T15:12:27.536Z","publishedAt":"2026-05-06T15:14:34.179Z","status":"published","updatedAt":"2026-05-06T15:14:34.179Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-06T15:14:54.918Z","subscriberCountAtSend":23},{"id":"aaciyv65llqUjrAMFblr","title":"Workout Duration Tracking, Improved Completion UX, WebView Crash Fixes","slug":"workout-duration-tracking-improved-completion-ux-webview-crash-fixes","summary":"This release adds a new workout_duration_seconds field to the workout_overview PostMessage event, giving integrators wall-clock session duration (including rest and transitions). Users will see more accurate duration displays on the results screen, and the completion overlay now shows appropriate messaging for minimal-effort sessions. Several WebView crash scenarios on Android and iOS have been resolved.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout duration tracking\"},{\"type\":\"text\",\"text\":\": The results screen now shows total wall-clock time spent in the workout (from start to finish, including rest periods and transitions) instead of only active exercise time. Challenges and assessments continue to show active exercise time only.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Completion overlay messaging\"},{\"type\":\"text\",\"text\":\": When a user scores below 20% efficiency, the completion screen no longer shows a contradictory \\\"Good job\\\" header above the \\\"You Showed Up\\\" title. The supportive subtitle (\\\"Some days that's the whole win…\\\") still appears.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Timer-based completion percentage\"},{\"type\":\"text\",\"text\":\": Completion percentage for timer-based exercises now uses time spent in correct position rather than raw elapsed time, producing a more accurate progress metric.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"WebView crash on navigation\"},{\"type\":\"text\",\"text\":\": Fixed a crash on Android WebView and iOS WKWebView that occurred when navigating away from an exercise during teardown.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — this is an additive change. Existing fields are unchanged.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New field \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_duration_seconds\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" in \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_overview\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"A new field has been added to the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_overview\"},{\"type\":\"text\",\"text\":\" PostMessage payload. It represents the total wall-clock duration of the workout session in seconds, measured from when the user finishes the initial frame-check to when the results screen appears. This includes rest periods, transitions, and pauses. The existing \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"total_time_spent\"},{\"type\":\"text\",\"text\":\" field continues to report active exercise time only (unchanged behavior).\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"For challenges and assessments, \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_duration_seconds\"},{\"type\":\"text\",\"text\":\" equals \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"total_time_spent\"},{\"type\":\"text\",\"text\":\" (no wall-clock concept applies).\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"workout_overview\\\",\\n  \\\"data\\\": {\\n    \\\"workout\\\": \\\"workout-id\\\",\\n    \\\"total_time_spent\\\": 420,\\n    \\\"total_repeats\\\": 50,\\n    \\\"total_calories\\\": 125.5,\\n    \\\"percentage_completed\\\": 85.33,\\n    \\\"completion_percentage\\\": 85.33,\\n    \\\"calories_burned\\\": 125.5,\\n    \\\"completed_reps_count\\\": 50,\\n    \\\"efficiency_score\\\": 72,\\n    \\\"accuracy_score\\\": 88\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"workout_overview\\\",\\n  \\\"data\\\": {\\n    \\\"workout\\\": \\\"workout-id\\\",\\n    \\\"total_time_spent\\\": 420,\\n    \\\"workout_duration_seconds\\\": 635,\\n    \\\"total_repeats\\\": 50,\\n    \\\"total_calories\\\": 125.5,\\n    \\\"percentage_completed\\\": 85.33,\\n    \\\"completion_percentage\\\": 85.33,\\n    \\\"calories_burned\\\": 125.5,\\n    \\\"completed_reps_count\\\": 50,\\n    \\\"efficiency_score\\\": 72,\\n    \\\"accuracy_score\\\": 88\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": No action required. The new \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_duration_seconds\"},{\"type\":\"text\",\"text\":\" field is purely additive. Use it if you want to display or log total session time (including rest). Continue using \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"total_time_spent\"},{\"type\":\"text\",\"text\":\" for active exercise time.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Challenge</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Workout duration tracking</strong>: The results screen now shows total wall-clock time spent in the workout (from start to finish, including rest periods and transitions) instead of only active exercise time. Challenges and assessments continue to show active exercise time only.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Completion overlay messaging</strong>: When a user scores below 20% efficiency, the completion screen no longer shows a contradictory \"Good job\" header above the \"You Showed Up\" title. The supportive subtitle (\"Some days that's the whole win…\") still appears.</p></li><li><p><strong>Timer-based completion percentage</strong>: Completion percentage for timer-based exercises now uses time spent in correct position rather than raw elapsed time, producing a more accurate progress metric.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>WebView crash on navigation</strong>: Fixed a crash on Android WebView and iOS WKWebView that occurred when navigating away from an exercise during teardown.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — this is an additive change. Existing fields are unchanged.</p></blockquote><h3><strong>New field&nbsp;</strong><code>workout_duration_seconds</code><strong>&nbsp;in&nbsp;</strong><code>workout_overview</code></h3><p>A new field has been added to the&nbsp;<code>workout_overview</code>&nbsp;PostMessage payload. It represents the total wall-clock duration of the workout session in seconds, measured from when the user finishes the initial frame-check to when the results screen appears. This includes rest periods, transitions, and pauses. The existing&nbsp;<code>total_time_spent</code>&nbsp;field continues to report active exercise time only (unchanged behavior).</p><p>For challenges and assessments,&nbsp;<code>workout_duration_seconds</code>&nbsp;equals&nbsp;<code>total_time_spent</code>&nbsp;(no wall-clock concept applies).</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"workout_overview\",\n  \"data\": {\n    \"workout\": \"workout-id\",\n    \"total_time_spent\": 420,\n    \"total_repeats\": 50,\n    \"total_calories\": 125.5,\n    \"percentage_completed\": 85.33,\n    \"completion_percentage\": 85.33,\n    \"calories_burned\": 125.5,\n    \"completed_reps_count\": 50,\n    \"efficiency_score\": 72,\n    \"accuracy_score\": 88\n  }\n}</code></pre><p><strong>After:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"workout_overview\",\n  \"data\": {\n    \"workout\": \"workout-id\",\n    \"total_time_spent\": 420,\n    \"workout_duration_seconds\": 635,\n    \"total_repeats\": 50,\n    \"total_calories\": 125.5,\n    \"percentage_completed\": 85.33,\n    \"completion_percentage\": 85.33,\n    \"calories_burned\": 125.5,\n    \"completed_reps_count\": 50,\n    \"efficiency_score\": 72,\n    \"accuracy_score\": 88\n  }\n}</code></pre><p><strong>Migration</strong>: No action required. The new&nbsp;<code>workout_duration_seconds</code>&nbsp;field is purely additive. Use it if you want to display or log total session time (including rest). Continue using&nbsp;<code>total_time_spent</code>&nbsp;for active exercise time.</p>","coverImage":null,"category":"improved","product":"client","version":"2.31.14","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-06T09:04:34.387Z","publishedAt":"2026-05-06T10:37:28.871Z","status":"published","updatedAt":"2026-05-06T10:37:28.871Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-06T10:37:44.102Z","subscriberCountAtSend":23},{"id":"Gw1u57TDgRGnsJuFJeor","title":"Dutch Language Support Added","slug":"dutch-language-support-added","summary":"Dutch (Nederlands) is now a fully supported locale. Integrators passing language: \"nl\" will see the entire UI rendered in Dutch — no code changes required beyond setting the language parameter. All integration options benefit from this addition.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Dutch (nl) locale\"},{\"type\":\"text\",\"text\":\": The SDK now supports Dutch as a display language. All UI strings — workout instructions, assessment prompts, challenge screens, plan details, and home page elements — are available in Nederlands. Integrators can activate it by passing \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"nl\\\"\"},{\"type\":\"text\",\"text\":\" as the language parameter.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations</p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Dutch (nl) locale</strong>: The SDK now supports Dutch as a display language. All UI strings — workout instructions, assessment prompts, challenge screens, plan details, and home page elements — are available in Nederlands. Integrators can activate it by passing&nbsp;<code>\"nl\"</code>&nbsp;as the language parameter.</p></li></ul>","coverImage":null,"category":"new","product":"client","version":"2.31.13","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-03T08:24:25.075Z","publishedAt":"2026-05-03T08:24:31.587Z","status":"published","updatedAt":"2026-05-03T08:24:31.587Z"},{"id":"wu3MpUnpGH64CNmR50iZ","title":"Dutch language support added across the admin dashboard","slug":"dutch-language-support-added-across-the-admin-dashboard","summary":"Dutch (nl) is now available as a content language throughout the admin dashboard. All language selectors — in Exercises, Workouts, Plans, and the Phrase Library — now include Dutch. All translated labels (body parts, starting positions, categories, difficulty levels, proximity options, contraindications, and common terms like \"Rest,\" \"Day,\" and \"Week\") have been translated into Dutch. No action is required — Dutch will appear automatically in all language dropdowns.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plans\"}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Dutch language support\"},{\"type\":\"text\",\"text\":\": Dutch is now available as a content language across the entire dashboard. You can select it when filtering exercises, workouts, or plans by language, when editing content translations, and when bulk-creating phrases. All fitness-related labels — body parts, exercise categories, difficulty levels, starting positions, proximity options, and contraindications — are fully translated into Dutch.\"}]}]}]}]}","contentHtml":"<blockquote><p><strong>Impacted areas</strong>:&nbsp;<code>Exercises</code>&nbsp;·&nbsp;<code>Workouts</code>&nbsp;·&nbsp;<code>Plans</code></p></blockquote><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Dutch language support</strong>: Dutch is now available as a content language across the entire dashboard. You can select it when filtering exercises, workouts, or plans by language, when editing content translations, and when bulk-creating phrases. All fitness-related labels — body parts, exercise categories, difficulty levels, starting positions, proximity options, and contraindications — are fully translated into Dutch.</p></li></ul>","coverImage":null,"category":"new","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-05-02T20:09:15.706Z","publishedAt":"2026-05-02T20:09:24.481Z","status":"published","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-05-02T20:09:40.592Z","subscriberCountAtSend":23,"product":"dashboard","version":"2.20.16","updatedAt":"2026-05-02T20:10:09.335Z"},{"id":"Y9y15nyTcRojG3VDzYfy","title":"Plan Day Completion Fix, Workout-Counted Confirmation Message","slug":"plan-day-completion-fix-workout-counted-confirmation-message","summary":"This release fixes a regression where completing a workout from a non-current day in a plan would fail to register as plan progress. Users now also see a confirmation message when their workout is counted toward their plan. No PostMessage API changes — integrators do not need to update any code.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"\\\"Workout counted toward plan\\\" confirmation\"},{\"type\":\"text\",\"text\":\": After completing a workout that qualifies for plan progression, users now see a confirmation banner on the results screen. For personalized plans the threshold is 80% efficiency; for goal-based plans the threshold is 5+ reps or 10+ seconds of hold time. This message is localized in 15 languages (Arabic, Bengali, Chinese, Danish, English, French, German, Greek, Hebrew, Hindi, Indonesian, Italian, Portuguese, Russian, Spanish).\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan day completion not registering\"},{\"type\":\"text\",\"text\":\": Selecting and completing a workout from any day in the weekly plan view (not just the current day) now correctly records plan progression. Previously, only the current day's workout carried the plan identifier, so completing earlier or later days would silently fail to update progress.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout incorrectly recorded as standalone during plan flow\"},{\"type\":\"text\",\"text\":\": When a workout was launched from within a plan but the plan identifier arrived late (e.g., via a schedule tap or certain host-app launch paths), the system could prematurely record the workout as standalone. This is now deferred until the plan context is fully resolved, ensuring accurate progression tracking.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Plan</code>&nbsp;·&nbsp;<code>Personalized Plan</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>\"Workout counted toward plan\" confirmation</strong>: After completing a workout that qualifies for plan progression, users now see a confirmation banner on the results screen. For personalized plans the threshold is 80% efficiency; for goal-based plans the threshold is 5+ reps or 10+ seconds of hold time. This message is localized in 15 languages (Arabic, Bengali, Chinese, Danish, English, French, German, Greek, Hebrew, Hindi, Indonesian, Italian, Portuguese, Russian, Spanish).</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Plan day completion not registering</strong>: Selecting and completing a workout from any day in the weekly plan view (not just the current day) now correctly records plan progression. Previously, only the current day's workout carried the plan identifier, so completing earlier or later days would silently fail to update progress.</p></li><li><p><strong>Workout incorrectly recorded as standalone during plan flow</strong>: When a workout was launched from within a plan but the plan identifier arrived late (e.g., via a schedule tap or certain host-app launch paths), the system could prematurely record the workout as standalone. This is now deferred until the plan context is fully resolved, ensuring accurate progression tracking.</p></li></ul>","coverImage":null,"category":"fixed","product":"client","version":"2.31.11","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-30T21:27:26.494Z","publishedAt":"2026-04-30T21:27:34.515Z","status":"published","updatedAt":"2026-04-30T21:27:34.515Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-30T21:27:46.655Z","subscriberCountAtSend":23},{"id":"64wwRFZLnCAUpQCZDKqZ","title":"Trainer Overhaul: Post-Workout Check-In, Pre-Workout Readiness, Workout Scheduling, Enhanced Profile & Injury Management","slug":"trainer-overhaul-post-workout-check-in-pre-workout-readiness-workout-scheduling-","summary":"Major upgrade to the AI Personal Trainer integration. Users now complete a post-workout check-in (effort, difficulty, discomfort with per-body-part detail) and a pre-workout readiness assessment (energy, soreness, motivation) that the AI uses to tailor the next workout. A new trainer_schedule_next_workout PostMessage event lets host apps schedule reminder notifications. Equipment selection is now multi-select, injury tracking captures severity and handling preference per body part, and profiles are persisted server-side. No changes to existing PostMessage events — the new event is additive.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Trainer (AI Personal Trainer)\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"No other integrations are affected — all changes are scoped to the Trainer feature.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Post-workout check-in flow\"},{\"type\":\"text\",\"text\":\": After finishing a workout, users rate effort (minimal / solid / max), difficulty (too hard / alright / great), and discomfort (no pain / mild / sharp pain). Selecting \\\"sharp pain\\\" opens a body-part picker followed by per-part severity and handling preference questions. Feedback is sent to the AI so subsequent workouts adapt.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Pre-workout readiness assessment\"},{\"type\":\"text\",\"text\":\": Before generating a workout, users rate energy level, muscle soreness, and motivation. The AI factors these signals into exercise selection, intensity, and duration.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Next-workout scheduling\"},{\"type\":\"text\",\"text\":\": The final step of the post-workout flow lets users pick a date and time for their next session. When the AI generates the next plan, the host app receives a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"trainer_schedule_next_workout\"},{\"type\":\"text\",\"text\":\" PostMessage event so it can schedule a local push notification.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"BMI display in profile\"},{\"type\":\"text\",\"text\":\": The profile setup and summary now calculate and display BMI score and category (Underweight / Healthy / Overweight / Obese) from the user's weight and height.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Multi-select equipment\"},{\"type\":\"text\",\"text\":\": Users can now select multiple equipment types (e.g., dumbbells + resistance bands) instead of a single option. \\\"No equipment\\\" and \\\"Full Gym\\\" remain mutually exclusive shortcuts. A new \\\"Chair\\\" option has been added.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Enhanced injury tracking\"},{\"type\":\"text\",\"text\":\": Each injury now captures severity (light / moderate / severe) and handling preference (avoid exercises entirely, or include lighter/rehabilitative variants). A medical clearance confirmation is required when health conditions are selected.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Imperial/metric unit switching\"},{\"type\":\"text\",\"text\":\": Profile entry supports toggling between kg/cm and lbs/ft with automatic value conversion.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Server-side profile persistence\"},{\"type\":\"text\",\"text\":\": Trainer profiles are now stored on the backend and restored on reload, so users don't lose their profile when clearing browser cache.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Chat retry on failure\"},{\"type\":\"text\",\"text\":\": Failed AI messages now show a retry button instead of a dead-end error, letting users resend without retyping.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Back/exit button\"},{\"type\":\"text\",\"text\":\": The Trainer screen now includes a back button that triggers the standard SDK exit flow, matching other integration screens.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout plan sectioning\"},{\"type\":\"text\",\"text\":\": The workout plan preview now separates warmup, main exercises, and cooldown into visually distinct sections with set counts.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan recovery from history\"},{\"type\":\"text\",\"text\":\": The active workout plan is now reconstructed from server-side chat history on reload, preventing plan loss after cache clears.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Body part label consistency\"},{\"type\":\"text\",\"text\":\": \\\"Shoulder\\\" and \\\"Knee\\\" are now correctly pluralized to \\\"Shoulders\\\" and \\\"Knees\\\" throughout the injury picker and muscle map, matching the canonical vocabulary used by the AI backend.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — this is a new additive event. Existing integrations continue to work without changes. Opt in only if you want to schedule workout reminder notifications.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"trainer_schedule_next_workout\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Dispatched after a post-workout check-in when the user selects a date/time for their next workout \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"and\"},{\"type\":\"text\",\"text\":\" the AI successfully generates the next plan. Not sent if the user skips scheduling or if plan generation fails.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Payload:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"trainer_schedule_next_workout\\\",\\n  \\\"scheduledFor\\\": \\\"2026-04-29T14:30\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"FieldTypeDescription\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"typestring\"},{\"type\":\"text\",\"text\":\"Always \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"trainer_schedule_next_workout\\\"scheduledForstring\"},{\"type\":\"text\",\"text\":\"Local date-time in \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"YYYY-MM-DDTHH:MM\"},{\"type\":\"text\",\"text\":\" format (no timezone). Use this to schedule a local push notification or calendar event in the host app.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": Listen for the new event type in your PostMessage handler if you want to prompt users with a reminder at the scheduled time. No existing events or payloads have changed.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Trainer (AI Personal Trainer)</code><br><em>No other integrations are affected — all changes are scoped to the Trainer feature.</em></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Post-workout check-in flow</strong>: After finishing a workout, users rate effort (minimal / solid / max), difficulty (too hard / alright / great), and discomfort (no pain / mild / sharp pain). Selecting \"sharp pain\" opens a body-part picker followed by per-part severity and handling preference questions. Feedback is sent to the AI so subsequent workouts adapt.</p></li><li><p><strong>Pre-workout readiness assessment</strong>: Before generating a workout, users rate energy level, muscle soreness, and motivation. The AI factors these signals into exercise selection, intensity, and duration.</p></li><li><p><strong>Next-workout scheduling</strong>: The final step of the post-workout flow lets users pick a date and time for their next session. When the AI generates the next plan, the host app receives a&nbsp;<code>trainer_schedule_next_workout</code>&nbsp;PostMessage event so it can schedule a local push notification.</p></li><li><p><strong>BMI display in profile</strong>: The profile setup and summary now calculate and display BMI score and category (Underweight / Healthy / Overweight / Obese) from the user's weight and height.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Multi-select equipment</strong>: Users can now select multiple equipment types (e.g., dumbbells + resistance bands) instead of a single option. \"No equipment\" and \"Full Gym\" remain mutually exclusive shortcuts. A new \"Chair\" option has been added.</p></li><li><p><strong>Enhanced injury tracking</strong>: Each injury now captures severity (light / moderate / severe) and handling preference (avoid exercises entirely, or include lighter/rehabilitative variants). A medical clearance confirmation is required when health conditions are selected.</p></li><li><p><strong>Imperial/metric unit switching</strong>: Profile entry supports toggling between kg/cm and lbs/ft with automatic value conversion.</p></li><li><p><strong>Server-side profile persistence</strong>: Trainer profiles are now stored on the backend and restored on reload, so users don't lose their profile when clearing browser cache.</p></li><li><p><strong>Chat retry on failure</strong>: Failed AI messages now show a retry button instead of a dead-end error, letting users resend without retyping.</p></li><li><p><strong>Back/exit button</strong>: The Trainer screen now includes a back button that triggers the standard SDK exit flow, matching other integration screens.</p></li><li><p><strong>Workout plan sectioning</strong>: The workout plan preview now separates warmup, main exercises, and cooldown into visually distinct sections with set counts.</p></li><li><p><strong>Plan recovery from history</strong>: The active workout plan is now reconstructed from server-side chat history on reload, preventing plan loss after cache clears.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Body part label consistency</strong>: \"Shoulder\" and \"Knee\" are now correctly pluralized to \"Shoulders\" and \"Knees\" throughout the injury picker and muscle map, matching the canonical vocabulary used by the AI backend.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — this is a new additive event. Existing integrations continue to work without changes. Opt in only if you want to schedule workout reminder notifications.</p></blockquote><h3><strong>New event:&nbsp;</strong><code>trainer_schedule_next_workout</code></h3><p>Dispatched after a post-workout check-in when the user selects a date/time for their next workout&nbsp;<strong>and</strong>&nbsp;the AI successfully generates the next plan. Not sent if the user skips scheduling or if plan generation fails.</p><p><strong>Payload:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"trainer_schedule_next_workout\",\n  \"scheduledFor\": \"2026-04-29T14:30\"\n}</code></pre><p><strong>FieldTypeDescription</strong><code>typestring</code>Always&nbsp;<code>\"trainer_schedule_next_workout\"scheduledForstring</code>Local date-time in&nbsp;<code>YYYY-MM-DDTHH:MM</code>&nbsp;format (no timezone). Use this to schedule a local push notification or calendar event in the host app.</p><p><strong>Migration</strong>: Listen for the new event type in your PostMessage handler if you want to prompt users with a reminder at the scheduled time. No existing events or payloads have changed.</p>","coverImage":null,"category":"improved","product":"client","version":"2.31.10","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-28T14:07:53.916Z","publishedAt":"2026-04-28T14:10:06.671Z","status":"published","updatedAt":"2026-04-28T14:10:06.671Z"},{"id":"yWKace8t0tecypFylfYn","title":"iOS Video Playback Fix, STS Assessment Text Correction","slug":"ios-video-playback-fix-sts-assessment-text-correction","summary":"This release fixes laggy exercise video playback on iOS devices, corrects a text mismatch in Sit-to-Stand assessments. No PostMessage API changes — integrators do not need to update any code.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"iOS video playback lag eliminated\"},{\"type\":\"text\",\"text\":\": Exercise videos on iOS devices could play back with significant stuttering and lag. Videos now load and play smoothly on all iOS devices.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"STS assessment instruction text now matches audio\"},{\"type\":\"text\",\"text\":\": The Five Times STS and 30-Second STS assessments previously displayed \\\"Sit sideways in zone\\\" on screen while the audio said \\\"Sit down on the chair!\\\" — the on-screen text now correctly reads \\\"Sit on the chair\\\" across all 15 supported languages.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Workout</code>&nbsp;·&nbsp;<code>Assessments</code></p><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>iOS video playback lag eliminated</strong>: Exercise videos on iOS devices could play back with significant stuttering and lag. Videos now load and play smoothly on all iOS devices.</p></li><li><p><strong>STS assessment instruction text now matches audio</strong>: The Five Times STS and 30-Second STS assessments previously displayed \"Sit sideways in zone\" on screen while the audio said \"Sit down on the chair!\" — the on-screen text now correctly reads \"Sit on the chair\" across all 15 supported languages.</p></li></ul>","coverImage":null,"category":"fixed","product":"client","version":"2.31.9","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-23T11:40:49.053Z","publishedAt":"2026-04-23T15:11:17.726Z","status":"published","updatedAt":"2026-04-23T15:11:17.726Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-23T15:11:33.310Z","subscriberCountAtSend":20},{"id":"TVSb2jpKV15UZ6vZ89wy","title":"Chinese Translation Refresh, Plan Layout Fix","slug":"chinese-translation-refresh-plan-layout-fix","summary":"This release updates Chinese (zh) translations on the model loading and camera permission screens, and fixes a layout issue on the Plan week detail page. No PostMessage API changes — integrators do not need to update any code.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · All Integrations (Chinese locale)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Chinese model loading screen\"},{\"type\":\"text\",\"text\":\": Replaced generic health-fact descriptions with concise setup tips (camera positioning, lighting, attire) so Chinese-speaking users get actionable guidance while the AI coach loads.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Chinese camera permission screen\"},{\"type\":\"text\",\"text\":\": Added translated strings for the camera-enable prompt and a privacy reassurance message explaining that nothing is recorded or shared.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan week detail layout\"},{\"type\":\"text\",\"text\":\": The workout list panel now fills available width correctly on all screen sizes, fixing a case where it could appear narrower than intended on certain viewports.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>:&nbsp;<code>Plan</code>&nbsp;· All Integrations (Chinese locale)</p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>Chinese model loading screen</strong>: Replaced generic health-fact descriptions with concise setup tips (camera positioning, lighting, attire) so Chinese-speaking users get actionable guidance while the AI coach loads.</p></li><li><p><strong>Chinese camera permission screen</strong>: Added translated strings for the camera-enable prompt and a privacy reassurance message explaining that nothing is recorded or shared.</p></li><li><p><strong>Plan week detail layout</strong>: The workout list panel now fills available width correctly on all screen sizes, fixing a case where it could appear narrower than intended on certain viewports.</p></li></ul>","coverImage":null,"category":"fixed","product":"client","version":"2.31.8","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-22T20:23:27.775Z","publishedAt":"2026-04-22T20:23:45.166Z","status":"published","updatedAt":"2026-04-22T20:23:45.166Z"},{"id":"jX6yZMvFw021WcXgybAy","title":"Video compression upgraded with visible savings feedback","slug":"video-compression-upgraded-with-visible-savings-feedback","summary":"Video uploads for exercises and workouts now use stronger compression (smaller files at good quality) and display exactly how much space was saved after each upload. This affects anyone uploading exercise or workout sequence videos through the admin dashboard. No action required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Smaller video files\"},{\"type\":\"text\",\"text\":\": Uploaded videos are now compressed more aggressively — files are resized to 720p and use higher-efficiency encoding. This results in significantly smaller file sizes while maintaining good visual quality.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Compression savings badge (Exercises)\"},{\"type\":\"text\",\"text\":\": After uploading an exercise video (male or female), a green info badge now appears showing the original file size, the compressed file size, and the percentage saved (e.g., \\\"12.40 MB → 3.20 MB (saved 74%)\\\"). The badge disappears when you clear the video or start a new upload.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Compression savings badge (Workouts)\"},{\"type\":\"text\",\"text\":\": The same savings badge now appears on each workout sequence item after its video is uploaded, so you can see compression results per exercise in the sequence.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Upload toast with savings details\"},{\"type\":\"text\",\"text\":\": Workout video upload success notifications now include compression details (e.g., \\\"Video uploaded — 8.50 MB → 2.10 MB (saved 75%)\\\") instead of a generic success message.\"}]}]}]}]}","contentHtml":"<blockquote><p><strong>Impacted areas</strong>:&nbsp;<code>Exercises</code>&nbsp;·&nbsp;<code>Workouts</code></p></blockquote><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>Smaller video files</strong>: Uploaded videos are now compressed more aggressively — files are resized to 720p and use higher-efficiency encoding. This results in significantly smaller file sizes while maintaining good visual quality.</p></li><li><p><strong>Compression savings badge (Exercises)</strong>: After uploading an exercise video (male or female), a green info badge now appears showing the original file size, the compressed file size, and the percentage saved (e.g., \"12.40 MB → 3.20 MB (saved 74%)\"). The badge disappears when you clear the video or start a new upload.</p></li><li><p><strong>Compression savings badge (Workouts)</strong>: The same savings badge now appears on each workout sequence item after its video is uploaded, so you can see compression results per exercise in the sequence.</p></li><li><p><strong>Upload toast with savings details</strong>: Workout video upload success notifications now include compression details (e.g., \"Video uploaded — 8.50 MB → 2.10 MB (saved 75%)\") instead of a generic success message.</p></li></ul>","coverImage":null,"category":"improved","product":"dashboard","version":"2.20.15","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-22T18:58:53.941Z","publishedAt":"2026-04-22T18:59:00.341Z","status":"published","updatedAt":"2026-04-22T18:59:00.341Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-22T18:59:11.805Z","subscriberCountAtSend":20},{"id":"5kRmYUCeYrsrQ3m8Hrjz","title":"Internal Video Diagnostics Logging","slug":"internal-video-diagnostics-logging","summary":"Internal improvements only — no integration changes required. This release adds comprehensive diagnostics logging for iOS video playback failures. No PostMessage events, payloads, or user-facing behavior changed. Integrators do not need to update any code.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations: \"},{\"type\":\"text\",\"text\":\"Workouts (internal observability only)\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"},{\"type\":\"hardBreak\",\"marks\":[{\"type\":\"bold\"}]},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Video fallback diagnostics: \"},{\"type\":\"text\",\"text\":\"Enhanced internal logging when exercise videos fail to play on iOS devices. Captures detailed device, network, and video element state at the moment of failure to accelerate root-cause analysis for video playback issues — particularly on older iOS devices. This is invisible to end users and does not affect any PostMessage events.\"}]}]}","contentHtml":"<p><strong>Impacted integrations: </strong>Workouts (internal observability only)</p><p><strong>Improvements<br>Video fallback diagnostics: </strong>Enhanced internal logging when exercise videos fail to play on iOS devices. Captures detailed device, network, and video element state at the moment of failure to accelerate root-cause analysis for video playback issues — particularly on older iOS devices. This is invisible to end users and does not affect any PostMessage events.</p>","coverImage":null,"category":"improved","product":"client","version":"2.31.7","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-22T10:04:51.591Z","publishedAt":"2026-04-22T10:04:59.553Z","status":"published","updatedAt":"2026-04-22T10:04:59.553Z"},{"id":"qMhX73VkEV6NOB4VRWYZ","title":"Back Button & Navigation Fixes for Home Page Entry Points","slug":"back-button-navigation-fixes-for-home-page-entry-points","summary":"Users who enter Game or Personalized Plans from the Home Page now see correct back-button behavior and are returned to Home upon completion. No PostMessage changes — integrators do not need to update any code.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations: \"},{\"type\":\"text\",\"text\":\"Assessments · Personalized Plan · Home Page\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\",\"marks\":[{\"type\":\"bold\"}]},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Game back button missing when launched from Home: \"},{\"type\":\"text\",\"text\":\"The back button on the game screen was hidden when the user navigated from the Home Page. It is now always visible in that flow.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"hardBreak\"},{\"type\":\"text\",\"text\":\"Game completion navigated to wrong screen: Finishing a game that was launched from the Home Page incorrectly landed on the game page instead of returning to Home. It now navigates back to Home as expected.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"\\nPersonalized Plan back button missing when launched from Home: The back button on the Personalized Plan screen was not shown when the user entered from the Home Page. It is now visible for both direct Home navigation and nested Home flows.\"}]}]}","contentHtml":"<p><strong>Impacted integrations: </strong>Assessments · Personalized Plan · Home Page</p><p><strong>Bug Fixes</strong></p><p><strong><br>Game back button missing when launched from Home: </strong>The back button on the game screen was hidden when the user navigated from the Home Page. It is now always visible in that flow.</p><p><br>Game completion navigated to wrong screen: Finishing a game that was launched from the Home Page incorrectly landed on the game page instead of returning to Home. It now navigates back to Home as expected.</p><p>\nPersonalized Plan back button missing when launched from Home: The back button on the Personalized Plan screen was not shown when the user entered from the Home Page. It is now visible for both direct Home navigation and nested Home flows.</p>","coverImage":null,"category":"fixed","product":"client","version":"2.31.6","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-20T07:39:27.820Z","publishedAt":"2026-04-20T07:39:34.943Z","status":"published","updatedAt":"2026-04-20T07:39:34.943Z"},{"id":"GAN4cZheqEmGwsxQTw7v","title":"New Phrase Library under Configs","slug":"new-phrase-library-under-configs","summary":"A brand-new Phrase Library is now available under the Configs section, letting KinesteX team and company admins curate the spoken lines used throughout workouts and challenges — including greetings, motivational cues, corrections, and farewells. Phrases can be written, translated into multiple languages, and voiced automatically. Configs access has also been reworked so more roles can reach it when they have something meaningful to manage inside.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Phrase Library\"},{\"type\":\"text\",\"text\":\": A new \\\"Phrase Library\\\" tab inside \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Configs\"},{\"type\":\"text\",\"text\":\" lets you browse every type of spoken line used during a session — workout and challenge greetings, introductory cues for hold vs. rep exercises, praise, informational tips, proactive nudges (soft and push), appraisals, mistake feedback, auto-skip messages, closing lines, farewells, and static system phrases. Each type has a friendly title, emoji, and description so you know exactly when it plays.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Create and edit phrases\"},{\"type\":\"text\",\"text\":\": You can open any phrase type to see its current list, add new phrases (in English), rename them inline, and delete the ones you don't want. A visible limit tells you how many phrases a category supports so your sessions stay varied without going overboard.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Multi-language phrases with auto-translate\"},{\"type\":\"text\",\"text\":\": Switch between any of the available languages on the phrase list. A one-click \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Auto Translate\"},{\"type\":\"text\",\"text\":\" action fills in missing translations from the English versions, so you can roll out localized phrasing without writing each line by hand.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Per-phrase and bulk audio generation\"},{\"type\":\"text\",\"text\":\": Generate a voiced version of each phrase using configurable voice settings (voice actor, stability, speed, similarity boost, style, speaker boost). A new \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Generate All Audios\"},{\"type\":\"text\",\"text\":\" button produces voice-overs for every phrase in the current language in one pass, with a progress overlay so you can see how the batch is going.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Deleted phrases, recoverable\"},{\"type\":\"text\",\"text\":\": A dedicated \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Deleted phrases\"},{\"type\":\"text\",\"text\":\" tab lists anything you've removed, with a badge showing the count. You can restore a phrase at any time — helpful when you've excluded a default phrase and later want it back.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2}}]}","contentHtml":"<h2><strong>New Features</strong></h2><ul><li><p><strong>Phrase Library</strong>: A new \"Phrase Library\" tab inside <strong>Configs</strong> lets you browse every type of spoken line used during a session — workout and challenge greetings, introductory cues for hold vs. rep exercises, praise, informational tips, proactive nudges (soft and push), appraisals, mistake feedback, auto-skip messages, closing lines, farewells, and static system phrases. Each type has a friendly title, emoji, and description so you know exactly when it plays.</p></li><li><p><strong>Create and edit phrases</strong>: You can open any phrase type to see its current list, add new phrases (in English), rename them inline, and delete the ones you don't want. A visible limit tells you how many phrases a category supports so your sessions stay varied without going overboard.</p></li><li><p><strong>Multi-language phrases with auto-translate</strong>: Switch between any of the available languages on the phrase list. A one-click <strong>Auto Translate</strong> action fills in missing translations from the English versions, so you can roll out localized phrasing without writing each line by hand.</p></li><li><p><strong>Per-phrase and bulk audio generation</strong>: Generate a voiced version of each phrase using configurable voice settings (voice actor, stability, speed, similarity boost, style, speaker boost). A new <strong>Generate All Audios</strong> button produces voice-overs for every phrase in the current language in one pass, with a progress overlay so you can see how the batch is going.</p></li><li><p><strong>Deleted phrases, recoverable</strong>: A dedicated <strong>Deleted phrases</strong> tab lists anything you've removed, with a badge showing the count. You can restore a phrase at any time — helpful when you've excluded a default phrase and later want it back.</p></li></ul><h2></h2>","coverImage":null,"category":"new","product":"dashboard","version":"2.20.14","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-19T15:48:56.198Z","publishedAt":"2026-04-19T15:49:06.799Z","status":"published","updatedAt":"2026-04-19T15:49:06.799Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-19T15:49:20.561Z","subscriberCountAtSend":20},{"id":"LjCerrhjzMWUkqWzFKWX","title":"iOS Video Stability Overhaul, New workout_ended Exit Types, MotionRecorder Kill-Switch","slug":"ios-video-stability-overhaul-new-workout-ended-exit-types-motionrecorder-kill-sw","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(The \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"motionDataEnabled\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\" custom parameter is available to all integration types via the verification payload)\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Video preloading during camera setup\"},{\"type\":\"text\",\"text\":\": Exercise videos are now decoded in the background while the user positions themselves in the camera frame, eliminating the 1–5 second blank screen delay when the first exercise begins.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"motionDataEnabled\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" custom parameter\"},{\"type\":\"text\",\"text\":\": Host apps can now pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"motionDataEnabled: false\"},{\"type\":\"text\",\"text\":\" in the verification payload to disable per-frame motion recording, reducing memory usage on constrained devices (e.g., iOS with many unique videos). Session replay will be empty for affected sessions, but all other workout functionality is unaffected. Default behavior is unchanged if the parameter is omitted.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"\\\"No AI Feedback\\\" indicator\"},{\"type\":\"text\",\"text\":\": The overlay shown during exercises without AI tracking now has improved contrast (white text on translucent dark background with blur effect) and supports RTL layouts.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Reduced video memory usage\"},{\"type\":\"text\",\"text\":\": Exercise videos now use Blob URLs on iOS instead of base64 data URIs, video data buffers are released from memory after processing, and the video render window has been tightened from 4 to 3 concurrent videos. This reduces peak memory consumption during long workouts with many unique videos.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"iOS video fallback cascade\"},{\"type\":\"text\",\"text\":\": Fixed a critical bug where the iOS stuck-video detector triggered a cascade of \\\"Video not found\\\" errors, producing white screens, missing audio, and unrecoverable UI states. The stuck-video detection timeout has also been increased to accommodate longer rest videos.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Black screen on rapid exercise navigation\"},{\"type\":\"text\",\"text\":\": Fixed a permanent black screen when users quickly swiped between exercises. Videos now auto-play reliably even when navigation outpaces the video element lifecycle.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Rest audio double-play and silence\"},{\"type\":\"text\",\"text\":\": Fixed rest-period speech playing twice on forward navigation and not playing at all on backward navigation on iOS.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"iOS autoplay retry\"},{\"type\":\"text\",\"text\":\": When iOS blocks video autoplay, playback now correctly retries on the next user tap/touch, with guards against stale elements from prior navigations.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Stale rest audio on rapid navigation\"},{\"type\":\"text\",\"text\":\": Fixed incorrect rest-period audio playing when the user navigated away before the previous video finished loading.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"View transition errors\"},{\"type\":\"text\",\"text\":\": Eliminated spurious error reports from view transitions that were aborted or ran while the page was in the background.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": Yes — integrators who switch on \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_ended.exit_type\"},{\"type\":\"text\",\"text\":\" should handle the new \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"outro\\\"\"},{\"type\":\"text\",\"text\":\" value.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"ios_video_fallback_activated\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Emitted when iOS detects a stuck video decoder and switches to image-based playback. This is a degraded-but-functional mode (video plays as animated images, no video controls). Integrators can use this for analytics or to surface a notice in their UI.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Payload:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"ios_video_fallback_activated\\\",\\n  \\\"reason\\\": \\\"video_stuck_at_metadata\\\",\\n  \\\"videoUrl\\\": \\\"https://cdn.example.com/exercise-video.mp4\\\",\\n  \\\"readyState\\\": 1,\\n  \\\"userAgent\\\": \\\"Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 ...)\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": No action required — this is a new additive event. Listen for it only if you want visibility into iOS playback degradation.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Updated event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_ended\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" — new \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exit_type: \\\"outro\\\"\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exit_type\"},{\"type\":\"text\",\"text\":\" field now has three possible values instead of two. When a user taps exit/close while on the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"outro rest screen\"},{\"type\":\"text\",\"text\":\" (the final cooldown after the last exercise), the event sends \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"outro\\\"\"},{\"type\":\"text\",\"text\":\" instead of \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exit\\\"\"},{\"type\":\"text\",\"text\":\".\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"workout_ended\\\",\\n  \\\"id\\\": \\\"workout_abc\\\",\\n  \\\"exit_type\\\": \\\"exit\\\",\\n  \\\"date\\\": \\\"2026-04-18T12:00:00Z\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After (when exiting from outro screen):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"workout_ended\\\",\\n  \\\"id\\\": \\\"workout_abc\\\",\\n  \\\"exit_type\\\": \\\"outro\\\",\\n  \\\"date\\\": \\\"2026-04-18T12:00:00Z\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exit_type\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Meaning\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"complete\\\"\"},{\"type\":\"text\",\"text\":\"User finished the entire workout including outro (unchanged)\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exit\\\"\"},{\"type\":\"text\",\"text\":\"User abandoned the workout mid-session (unchanged — but no longer fires from the outro screen)\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"outro\\\"\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New\"},{\"type\":\"text\",\"text\":\" — User exited from the outro/cooldown screen after completing all exercises\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": If your code switches on \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exit_type\"},{\"type\":\"text\",\"text\":\", add a case for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"outro\\\"\"},{\"type\":\"text\",\"text\":\". If you don't need the distinction, treat it the same as \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"complete\\\"\"},{\"type\":\"text\",\"text\":\" — the user has finished all exercises.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New custom parameter: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"motionDataEnabled\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"motionDataEnabled: false\"},{\"type\":\"text\",\"text\":\" in the verification payload to disable per-frame motion data collection for the session. The recorder state resets on every verification, so this flag does not leak across sessions.\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"motionDataEnabled\\\": false\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": No action required — additive parameter with unchanged defaults. Use it if your iOS users experience memory-related crashes during long workouts with many unique exercise videos.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Camera</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Personalized Plan</code><br><em>(The </em><code>motionDataEnabled</code><em> custom parameter is available to all integration types via the verification payload)</em></p><h2><strong>New Features</strong></h2><ul><li><p><strong>Video preloading during camera setup</strong>: Exercise videos are now decoded in the background while the user positions themselves in the camera frame, eliminating the 1–5 second blank screen delay when the first exercise begins.</p></li><li><p><code>motionDataEnabled</code><strong> custom parameter</strong>: Host apps can now pass <code>motionDataEnabled: false</code> in the verification payload to disable per-frame motion recording, reducing memory usage on constrained devices (e.g., iOS with many unique videos). Session replay will be empty for affected sessions, but all other workout functionality is unaffected. Default behavior is unchanged if the parameter is omitted.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>\"No AI Feedback\" indicator</strong>: The overlay shown during exercises without AI tracking now has improved contrast (white text on translucent dark background with blur effect) and supports RTL layouts.</p></li><li><p><strong>Reduced video memory usage</strong>: Exercise videos now use Blob URLs on iOS instead of base64 data URIs, video data buffers are released from memory after processing, and the video render window has been tightened from 4 to 3 concurrent videos. This reduces peak memory consumption during long workouts with many unique videos.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>iOS video fallback cascade</strong>: Fixed a critical bug where the iOS stuck-video detector triggered a cascade of \"Video not found\" errors, producing white screens, missing audio, and unrecoverable UI states. The stuck-video detection timeout has also been increased to accommodate longer rest videos.</p></li><li><p><strong>Black screen on rapid exercise navigation</strong>: Fixed a permanent black screen when users quickly swiped between exercises. Videos now auto-play reliably even when navigation outpaces the video element lifecycle.</p></li><li><p><strong>Rest audio double-play and silence</strong>: Fixed rest-period speech playing twice on forward navigation and not playing at all on backward navigation on iOS.</p></li><li><p><strong>iOS autoplay retry</strong>: When iOS blocks video autoplay, playback now correctly retries on the next user tap/touch, with guards against stale elements from prior navigations.</p></li><li><p><strong>Stale rest audio on rapid navigation</strong>: Fixed incorrect rest-period audio playing when the user navigated away before the previous video finished loading.</p></li><li><p><strong>View transition errors</strong>: Eliminated spurious error reports from view transitions that were aborted or ran while the page was in the background.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: Yes — integrators who switch on <code>workout_ended.exit_type</code> should handle the new <code>\"outro\"</code> value.</p></blockquote><h3><strong>New event: </strong><code>ios_video_fallback_activated</code></h3><p>Emitted when iOS detects a stuck video decoder and switches to image-based playback. This is a degraded-but-functional mode (video plays as animated images, no video controls). Integrators can use this for analytics or to surface a notice in their UI.</p><p><strong>Payload:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"ios_video_fallback_activated\",\n  \"reason\": \"video_stuck_at_metadata\",\n  \"videoUrl\": \"https://cdn.example.com/exercise-video.mp4\",\n  \"readyState\": 1,\n  \"userAgent\": \"Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 ...)\"\n}</code></pre><p><strong>Migration</strong>: No action required — this is a new additive event. Listen for it only if you want visibility into iOS playback degradation.</p><hr><h3><strong>Updated event: </strong><code>workout_ended</code><strong> — new </strong><code>exit_type: \"outro\"</code></h3><p>The <code>exit_type</code> field now has three possible values instead of two. When a user taps exit/close while on the <strong>outro rest screen</strong> (the final cooldown after the last exercise), the event sends <code>\"outro\"</code> instead of <code>\"exit\"</code>.</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"workout_ended\",\n  \"id\": \"workout_abc\",\n  \"exit_type\": \"exit\",\n  \"date\": \"2026-04-18T12:00:00Z\"\n}</code></pre><p><strong>After (when exiting from outro screen):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"workout_ended\",\n  \"id\": \"workout_abc\",\n  \"exit_type\": \"outro\",\n  \"date\": \"2026-04-18T12:00:00Z\"\n}</code></pre><p><code>exit_type</code><strong>Meaning</strong><code>\"complete\"</code>User finished the entire workout including outro (unchanged)<code>\"exit\"</code>User abandoned the workout mid-session (unchanged — but no longer fires from the outro screen)<code>\"outro\"</code><strong>New</strong> — User exited from the outro/cooldown screen after completing all exercises</p><p><strong>Migration</strong>: If your code switches on <code>exit_type</code>, add a case for <code>\"outro\"</code>. If you don't need the distinction, treat it the same as <code>\"complete\"</code> — the user has finished all exercises.</p><hr><h3><strong>New custom parameter: </strong><code>motionDataEnabled</code></h3><p>Pass <code>motionDataEnabled: false</code> in the verification payload to disable per-frame motion data collection for the session. The recorder state resets on every verification, so this flag does not leak across sessions.</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"motionDataEnabled\": false\n}</code></pre><p><strong>Migration</strong>: No action required — additive parameter with unchanged defaults. Use it if your iOS users experience memory-related crashes during long workouts with many unique exercise videos.</p>","coverImage":null,"category":"new","product":"client","version":"2.31.5","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-18T11:27:16.160Z","summary":"This release fixes critical iOS video playback failures that caused white screens and audio issues during workouts. Integrators should note two PostMessage API additions: a new ios_video_fallback_activated event and a new \"outro\" value for workout_ended.exit_type. A new motionDataEnabled custom parameter lets host apps disable motion recording on memory-constrained devices.","publishedAt":"2026-04-18T11:27:32.904Z","status":"published","updatedAt":"2026-04-18T11:27:32.904Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-18T11:27:45.025Z","subscriberCountAtSend":20},{"id":"WNuW0Cgrnm5GXMXFFRSr","title":"Personalized Plan Result Screen Fixed, Workout Details Added, PostMessage Parity","slug":"personalized-plan-result-screen-fixed-workout-details-added-postmessage-parity","summary":"The personalized plan result screen now loads correctly for both new and returning users, displaying full workout details (image, title, calories, duration) per day. Two PostMessage behavior changes affect host apps consuming plan events: plan_opened now fires for personalized plans, and plan_onboarding_plan_created is suppressed on revisits. Integrators listening to these events should review the API Changes section below.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations: \"},{\"type\":\"text\",\"text\":\"Personalized Plan\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout details on personalized plan result\"},{\"type\":\"text\",\"text\":\": The plan result screen for personalized plans now displays each day's workout image, title, estimated calories, and duration — matching the level of detail already shown for goal-based plans.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Personalized plan result now loads for returning users\"},{\"type\":\"text\",\"text\":\": Previously, returning users who already had a personalized plan would see a broken/empty result screen. The screen now renders correctly on revisit.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": Yes — review if your app listens to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_opened\"},{\"type\":\"text\",\"text\":\" or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_onboarding_plan_created\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_opened\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" now fires for personalized plans\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Previously, the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_opened\"},{\"type\":\"text\",\"text\":\" event was only emitted when a goal-based plan loaded. It now also fires when the personalized plan result screen renders — giving host apps a consistent signal regardless of plan type.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before (personalized plan flow):\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"text\":\"No \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_opened\"},{\"type\":\"text\",\"text\":\" event was emitted.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After (personalized plan flow):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"plan_opened\\\", \\\"id\\\": \\\"12345\\\" }\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": If your app handles \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_opened\"},{\"type\":\"text\",\"text\":\", it will now receive this event in the personalized plan context as well. The payload shape is identical. No code changes required unless you need to distinguish plan types (the event does not include a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_type\"},{\"type\":\"text\",\"text\":\" field).\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_onboarding_plan_created\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" suppressed on personalized plan revisits\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"When a user revisits their existing personalized plan (without going through the assessment flow again), the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_onboarding_plan_created\"},{\"type\":\"text\",\"text\":\" event no longer fires. This prevents spurious \\\"plan created\\\" signals on every page view. The event still fires normally after a fresh assessment completion and for all goal-based plan flows.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New user (assessment → plan generation → result screen):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"plan_onboarding_plan_created\\\", \\\"data\\\": { \\\"plan_id\\\": \\\"12345\\\", \\\"plan_type\\\": \\\"personalized\\\" } }\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"✅ Still fires as before.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Returning user (navigates to existing plan result):\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"text\":\"Previously would have fired (if the page had loaded — it was broken). Now intentionally suppressed.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": If your app relied on receiving \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_onboarding_plan_created\"},{\"type\":\"text\",\"text\":\" every time the personalized plan result page opened, that no longer happens. Only the first creation triggers this event. Use \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_opened\"},{\"type\":\"text\",\"text\":\" (now available for personalized plans) if you need a \\\"plan screen viewed\\\" signal.\"}]}]}","contentHtml":"<p><strong>Impacted integrations: </strong>Personalized Plan</p><h2><strong>New Features</strong></h2><ul><li><p><strong>Workout details on personalized plan result</strong>: The plan result screen for personalized plans now displays each day's workout image, title, estimated calories, and duration — matching the level of detail already shown for goal-based plans.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Personalized plan result now loads for returning users</strong>: Previously, returning users who already had a personalized plan would see a broken/empty result screen. The screen now renders correctly on revisit.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: Yes — review if your app listens to <code>plan_opened</code> or <code>plan_onboarding_plan_created</code></p></blockquote><h3><code>plan_opened</code><strong> now fires for personalized plans</strong></h3><p>Previously, the <code>plan_opened</code> event was only emitted when a goal-based plan loaded. It now also fires when the personalized plan result screen renders — giving host apps a consistent signal regardless of plan type.</p><p><strong>Before (personalized plan flow):</strong><br>No <code>plan_opened</code> event was emitted.</p><p><strong>After (personalized plan flow):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{ \"type\": \"plan_opened\", \"id\": \"12345\" }</code></pre><p><strong>Migration</strong>: If your app handles <code>plan_opened</code>, it will now receive this event in the personalized plan context as well. The payload shape is identical. No code changes required unless you need to distinguish plan types (the event does not include a <code>plan_type</code> field).</p><hr><h3><code>plan_onboarding_plan_created</code><strong> suppressed on personalized plan revisits</strong></h3><p>When a user revisits their existing personalized plan (without going through the assessment flow again), the <code>plan_onboarding_plan_created</code> event no longer fires. This prevents spurious \"plan created\" signals on every page view. The event still fires normally after a fresh assessment completion and for all goal-based plan flows.</p><p><strong>New user (assessment → plan generation → result screen):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{ \"type\": \"plan_onboarding_plan_created\", \"data\": { \"plan_id\": \"12345\", \"plan_type\": \"personalized\" } }</code></pre><p>✅ Still fires as before.</p><p><strong>Returning user (navigates to existing plan result):</strong><br>Previously would have fired (if the page had loaded — it was broken). Now intentionally suppressed.</p><p><strong>Migration</strong>: If your app relied on receiving <code>plan_onboarding_plan_created</code> every time the personalized plan result page opened, that no longer happens. Only the first creation triggers this event. Use <code>plan_opened</code> (now available for personalized plans) if you need a \"plan screen viewed\" signal.</p>","coverImage":null,"category":"fixed","product":"client","version":"2.31.4","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-17T16:28:41.086Z","publishedAt":"2026-04-17T16:29:02.429Z","status":"published","updatedAt":"2026-04-17T16:29:02.429Z"},{"id":"y14mgOdC5nRs0gbaNZlg","title":"Personalized Plan Workout Nav, Viewport & PostMessage Fixes","slug":"personalized-plan-workout-nav-viewport-postmessage-fixes","summary":"This update enables exercise navigation in personalized plan workouts, and fixes several viewport/stacking bugs that affected embedded WebView hosts. Integrators do not need to change code — all changes are additive or internal fixes.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": All Integrations \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(shared viewport handling, feedback modal)\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" — primary impact on \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Home Page\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Next/previous exercise navigation in Personalized Plan workouts\"},{\"type\":\"text\",\"text\":\": Workouts launched from the \\\"Start AI journey\\\" flow or the personalized plan dashboard now show next/previous exercise buttons, allowing users to skip between exercises.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"No confetti on past sessions\"},{\"type\":\"text\",\"text\":\": Viewing a previously completed workout session no longer triggers the confetti celebration animation.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan onboarding preloading\"},{\"type\":\"text\",\"text\":\": Plan onboarding pages are now preloaded as a standalone entry point, preventing stale-chunk errors (blank screen / MIME type error) on long-lived tabs after a deployment.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"More robust viewport height\"},{\"type\":\"text\",\"text\":\": The \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"--real-height\"},{\"type\":\"text\",\"text\":\" CSS variable now uses the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"visualViewport\"},{\"type\":\"text\",\"text\":\" API and listens to orientation changes and viewport resize events, fixing layout misalignment on iOS WebViews when the native host resizes the frame.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Feedback modal submit button state\"},{\"type\":\"text\",\"text\":\": The submit button now correctly enables when the user selects a feedback tag, and stays disabled until a selection is made — fixing two flows where the button state was out of sync with the actual submit gate.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Feedback modal clipped in ignore-safe-area mode\"},{\"type\":\"text\",\"text\":\": The feedback sheet now anchors to the actual visible viewport height instead of CSS \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"100vh\"},{\"type\":\"text\",\"text\":\", preventing the submit button from being clipped off-screen in integrations that position the WebView smaller than the full screen.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_overview\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" event missing workout info\"},{\"type\":\"text\",\"text\":\": In the \\\"Start AI journey\\\" flow, the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_overview\"},{\"type\":\"text\",\"text\":\" PostMessage event could fire before workout details loaded, resulting in missing \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_title\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_id\"},{\"type\":\"text\",\"text\":\". These fields are now reliably populated via a fallback mechanism.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Hidden replay card when no recording exists\"},{\"type\":\"text\",\"text\":\": The \\\"Watch your workout replay\\\" section is now hidden when no motion recording is available, instead of showing an empty non-functional card.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: All Integrations </span><em>(shared viewport handling, feedback modal)</em><span style=\"color: rgb(240, 246, 252);\"> — primary impact on </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Personalized Plan</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Home Page</code></p><h2><strong>New Features</strong></h2><ul><li><p><strong>Next/previous exercise navigation in Personalized Plan workouts</strong>: Workouts launched from the \"Start AI journey\" flow or the personalized plan dashboard now show next/previous exercise buttons, allowing users to skip between exercises.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>No confetti on past sessions</strong>: Viewing a previously completed workout session no longer triggers the confetti celebration animation.</p></li><li><p><strong>Plan onboarding preloading</strong>: Plan onboarding pages are now preloaded as a standalone entry point, preventing stale-chunk errors (blank screen / MIME type error) on long-lived tabs after a deployment.</p></li><li><p><strong>More robust viewport height</strong>: The <code>--real-height</code> CSS variable now uses the <code>visualViewport</code> API and listens to orientation changes and viewport resize events, fixing layout misalignment on iOS WebViews when the native host resizes the frame.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Feedback modal submit button state</strong>: The submit button now correctly enables when the user selects a feedback tag, and stays disabled until a selection is made — fixing two flows where the button state was out of sync with the actual submit gate.</p></li><li><p><strong>Feedback modal clipped in ignore-safe-area mode</strong>: The feedback sheet now anchors to the actual visible viewport height instead of CSS <code>100vh</code>, preventing the submit button from being clipped off-screen in integrations that position the WebView smaller than the full screen.</p></li><li><p><code>workout_overview</code><strong> event missing workout info</strong>: In the \"Start AI journey\" flow, the <code>workout_overview</code> PostMessage event could fire before workout details loaded, resulting in missing <code>workout_title</code> and <code>workout_id</code>. These fields are now reliably populated via a fallback mechanism.</p></li><li><p><strong>Hidden replay card when no recording exists</strong>: The \"Watch your workout replay\" section is now hidden when no motion recording is available, instead of showing an empty non-functional card.</p></li></ul>","coverImage":null,"category":"improved","product":"client","version":"2.31.3","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-17T10:20:29.237Z","publishedAt":"2026-04-17T10:20:33.571Z","status":"published","updatedAt":"2026-04-17T10:20:33.571Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-17T10:20:47.537Z","subscriberCountAtSend":20},{"id":"2PSgiPuhGTHZO3Z1PakV","title":"Silent Leaderboard Submission for Challenges","slug":"silent-leaderboard-submission-for-challenges","summary":"Adds a new opt-in configuration option, autoSubmitLeaderboard, that lets Challenge integrators skip the leaderboard submission modal and automatically post the user's result. No changes to existing PostMessage events — this is purely additive, so integrators who don't set the flag see the same behavior as before.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Auto-submit to leaderboard\"},{\"type\":\"text\",\"text\":\": New boolean configuration option \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"autoSubmitLeaderboard\"},{\"type\":\"text\",\"text\":\". When set to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"true\"},{\"type\":\"text\",\"text\":\", Challenge completions are submitted to the leaderboard silently: the user is not prompted with the submit-to-leaderboard modal, and the entry is marked as public (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"is_leaderboard: true\"},{\"type\":\"text\",\"text\":\") as soon as the result is recorded. The submitted display name is read from the browser's stored \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"leaderboard_username\"},{\"type\":\"text\",\"text\":\" (set during prior manual submissions) and falls back to the user ID if no name is stored. Leaving the flag unset or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"false\"},{\"type\":\"text\",\"text\":\" preserves the current modal-based submission flow.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — opt-in only. Existing integrations require no code changes.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New configuration field: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"autoSubmitLeaderboard\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"A new optional boolean field accepted alongside the other Challenge customization options sent during SDK initialization/verification. When omitted, behavior is unchanged.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"showLeaderboard\\\": true\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After (new field available):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"showLeaderboard\\\": true,\\n  \\\"autoSubmitLeaderboard\\\": true\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required. Only integrators who want silent leaderboard submission for Challenges need to pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"autoSubmitLeaderboard: true\"},{\"type\":\"text\",\"text\":\". The new field is additive and defaults to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"false\"},{\"type\":\"text\",\"text\":\".\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: <code>Challenge</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Auto-submit to leaderboard</strong>: New boolean configuration option <code>autoSubmitLeaderboard</code>. When set to <code>true</code>, Challenge completions are submitted to the leaderboard silently: the user is not prompted with the submit-to-leaderboard modal, and the entry is marked as public (<code>is_leaderboard: true</code>) as soon as the result is recorded. The submitted display name is read from the browser's stored <code>leaderboard_username</code> (set during prior manual submissions) and falls back to the user ID if no name is stored. Leaving the flag unset or <code>false</code> preserves the current modal-based submission flow.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — opt-in only. Existing integrations require no code changes.</p></blockquote><h3><strong>New configuration field: </strong><code>autoSubmitLeaderboard</code></h3><p>A new optional boolean field accepted alongside the other Challenge customization options sent during SDK initialization/verification. When omitted, behavior is unchanged.</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"showLeaderboard\": true\n}</code></pre><p><strong>After (new field available):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"showLeaderboard\": true,\n  \"autoSubmitLeaderboard\": true\n}</code></pre><p><strong>Migration</strong>: None required. Only integrators who want silent leaderboard submission for Challenges need to pass <code>autoSubmitLeaderboard: true</code>. The new field is additive and defaults to <code>false</code>.</p>","coverImage":null,"category":"new","product":"client","version":"2.31.2","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-16T08:45:18.870Z","publishedAt":"2026-04-16T08:45:44.519Z","status":"published","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-16T08:45:56.959Z","subscriberCountAtSend":20,"updatedAt":"2026-04-16T08:46:32.056Z"},{"id":"Mn8AiZbxMa9QjyjciGkf","title":"Streak tracking moved to a managed API, improved pose-model error handling, setup tip refresh","slug":"streak-tracking-moved-to-a-managed-api-improved-pose-model-error-handling-setup-","summary":"Summary: Daily streaks and weekly activity dots on the Home Page are now served by a managed REST API instead of per-device Firestore writes — end users see the same UI, but streak values stay consistent across sessions and devices. This release also adds error reporting around pose-model load failures, fixes a duplicate home_page_opened event, and polishes the AI Trainer onboarding. No PostMessage payload shape changes — no integrator code changes required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations: \"},{\"type\":\"text\",\"text\":\"Home Page · Workout · Personalized Plan · Assessments · Camera · Challenge\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer setup flow\"},{\"type\":\"text\",\"text\":\": The AI Trainer onboarding has been broken into discrete setup steps (profile, fitness level, goals, body parts, duration, equipment, injuries, warmup/cooldown), each with redesigned chips, icons, and muscle-group visuals. Users can now edit their profile inline and switch units directly from the chat.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Streak & weekly activity accuracy\"},{\"type\":\"text\",\"text\":\": Streak count, longest streak, last activity date, and weekly activity dots on the Home Page are now read from a single managed endpoint. This removes drift between devices and makes the same values available to the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"streak_extended\"},{\"type\":\"text\",\"text\":\" analytics event after workout completion.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Instruction-mode transition safety\"},{\"type\":\"text\",\"text\":\": The workout player now catches failures from the browser's view-transition API during instruction-mode switches and falls back to a plain state update, preventing the rare case where the exercise screen could freeze mid-transition.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Setup tips refresh\"},{\"type\":\"text\",\"text\":\": The pre-workout model-loading tips no longer include the \\\"place your phone upright\\\" tip, and remaining tips have been reordered for clarity. End users see five tips instead of six during the initial model load.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Duplicate \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"home_page_opened\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" event\"},{\"type\":\"text\",\"text\":\": The \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"home_page_opened\"},{\"type\":\"text\",\"text\":\" PostMessage event was being dispatched twice on some renders of the Home Page due to effect re-runs. It now fires exactly once per mount. Integrators that previously deduplicated this event on their side can simplify their handlers.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment activity end\"},{\"type\":\"text\",\"text\":\": Completing an assessment now records an activity-end entry against the user's history, matching the behavior of completed workouts, challenges, and plans. This ensures assessments contribute to streak and activity tracking the same way other integrations do.\"}]}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — no PostMessage payload fields were added, removed, or renamed in this release.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Timing change: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"home_page_opened\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The event payload is unchanged, but it is now guaranteed to fire exactly once per Home Page mount (previously it could fire twice).\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Payload (unchanged):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"home_page_opened\\\",\\n  \\\"date\\\": \\\"2026-04-14T12:34:56.789Z\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required. If your handler previously deduplicated consecutive \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"home_page_opened\"},{\"type\":\"text\",\"text\":\" events within the same session, you can remove that workaround.\"}]}]}","contentHtml":"<p><strong>Impacted integrations: </strong>Home Page · Workout · Personalized Plan · Assessments · Camera · Challenge</p><h2><strong>New Features</strong></h2><ul><li><p><strong>AI Trainer setup flow</strong>: The AI Trainer onboarding has been broken into discrete setup steps (profile, fitness level, goals, body parts, duration, equipment, injuries, warmup/cooldown), each with redesigned chips, icons, and muscle-group visuals. Users can now edit their profile inline and switch units directly from the chat.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Streak &amp; weekly activity accuracy</strong>: Streak count, longest streak, last activity date, and weekly activity dots on the Home Page are now read from a single managed endpoint. This removes drift between devices and makes the same values available to the <code>streak_extended</code> analytics event after workout completion.</p></li><li><p><strong>Instruction-mode transition safety</strong>: The workout player now catches failures from the browser's view-transition API during instruction-mode switches and falls back to a plain state update, preventing the rare case where the exercise screen could freeze mid-transition.</p></li><li><p><strong>Setup tips refresh</strong>: The pre-workout model-loading tips no longer include the \"place your phone upright\" tip, and remaining tips have been reordered for clarity. End users see five tips instead of six during the initial model load.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Duplicate </strong><code>home_page_opened</code><strong> event</strong>: The <code>home_page_opened</code> PostMessage event was being dispatched twice on some renders of the Home Page due to effect re-runs. It now fires exactly once per mount. Integrators that previously deduplicated this event on their side can simplify their handlers.</p></li><li><p><strong>Assessment activity end</strong>: Completing an assessment now records an activity-end entry against the user's history, matching the behavior of completed workouts, challenges, and plans. This ensures assessments contribute to streak and activity tracking the same way other integrations do.</p></li></ul><hr><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — no PostMessage payload fields were added, removed, or renamed in this release.</p></blockquote><h3><strong>Timing change: </strong><code>home_page_opened</code></h3><p>The event payload is unchanged, but it is now guaranteed to fire exactly once per Home Page mount (previously it could fire twice).</p><p><strong>Payload (unchanged):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"home_page_opened\",\n  \"date\": \"2026-04-14T12:34:56.789Z\"\n}</code></pre><p><strong>Migration</strong>: None required. If your handler previously deduplicated consecutive <code>home_page_opened</code> events within the same session, you can remove that workaround.</p>","coverImage":null,"category":"improved","product":"client","version":"2.31.1","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-14T07:53:57.702Z","publishedAt":"2026-04-14T07:54:34.074Z","status":"published","updatedAt":"2026-04-14T07:54:34.074Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-14T07:54:46.297Z","subscriberCountAtSend":20},{"id":"DxI1yrVd7G2a1D3N18Zp","title":"Exercise categories redesigned around fitness goals, with translated plan categories","slug":"exercise-categories-redesigned-around-fitness-goals-with-translated-plan-categor","summary":"Exercises now use 8 goal-oriented categories (Weight Loss, Muscle Gain, Strength, Cardio/Endurance, General Fitness, Wellness/Flexibility, Warm Up, Cooldown) in place of the previous 4, fully translated across all supported languages. Plan cards now display category names in the selected language instead of raw identifiers. Admins managing existing exercises should review items saved under legacy categories — see the note below.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plans\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Goal-based exercise categories\"},{\"type\":\"text\",\"text\":\": When creating or editing an exercise, you can now tag it with one or more of 8 categories — Weight Loss, Muscle Gain, Strength, Cardio/Endurance, General Fitness, Wellness/Flexibility, Warm Up, and Cooldown — giving a much clearer picture of what each exercise is for.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Filter exercises by goal\"},{\"type\":\"text\",\"text\":\": The exercises page filter panel now offers the full set of new goal-oriented categories, so you can quickly narrow the library down to, for example, all Weight Loss or Cooldown exercises.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Translated exercise categories\"},{\"type\":\"text\",\"text\":\": The new categories are fully translated into every supported language (English, Spanish, Russian, Ukrainian, Hebrew, French, German, Bengali, Arabic, Greek, Portuguese, Italian, Hindi, Indonesian, and Chinese), so category names appear correctly when you switch the content language.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Translated plan category labels\"},{\"type\":\"text\",\"text\":\": Plan cards now show category names in the current content language instead of raw values, making plan listings easier to scan.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Better layout for long category names\"},{\"type\":\"text\",\"text\":\": On exercise cards, long category labels and language lists now wrap neatly to a new line instead of being cut off or pushing the card layout around.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"More forgiving exercise edit form\"},{\"type\":\"text\",\"text\":\": If a category is missing a translation in the selected language, the edit form now falls back to showing the category name instead of breaking the dropdown.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan category filter highlighting\"},{\"type\":\"text\",\"text\":\": Selecting a category in the plans filter panel now correctly highlights the active choice, so it's clear which filters are applied.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Breaking Changes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Legacy exercise categories no longer selectable in filters\"},{\"type\":\"text\",\"text\":\": Exercises previously tagged with the old categories (Cardio, Strength, Stretching, Warmup) will still open and save correctly, and their data is preserved, but they will not match any of the new filter options on the exercises page until their categories are updated. We recommend re-tagging older exercises with the new goal-oriented categories so they remain easy to discover.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted areas</strong>: <code>Exercises</code> · <code>Plans</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Goal-based exercise categories</strong>: When creating or editing an exercise, you can now tag it with one or more of 8 categories — Weight Loss, Muscle Gain, Strength, Cardio/Endurance, General Fitness, Wellness/Flexibility, Warm Up, and Cooldown — giving a much clearer picture of what each exercise is for.</p></li><li><p><strong>Filter exercises by goal</strong>: The exercises page filter panel now offers the full set of new goal-oriented categories, so you can quickly narrow the library down to, for example, all Weight Loss or Cooldown exercises.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Translated exercise categories</strong>: The new categories are fully translated into every supported language (English, Spanish, Russian, Ukrainian, Hebrew, French, German, Bengali, Arabic, Greek, Portuguese, Italian, Hindi, Indonesian, and Chinese), so category names appear correctly when you switch the content language.</p></li><li><p><strong>Translated plan category labels</strong>: Plan cards now show category names in the current content language instead of raw values, making plan listings easier to scan.</p></li><li><p><strong>Better layout for long category names</strong>: On exercise cards, long category labels and language lists now wrap neatly to a new line instead of being cut off or pushing the card layout around.</p></li><li><p><strong>More forgiving exercise edit form</strong>: If a category is missing a translation in the selected language, the edit form now falls back to showing the category name instead of breaking the dropdown.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Plan category filter highlighting</strong>: Selecting a category in the plans filter panel now correctly highlights the active choice, so it's clear which filters are applied.</p></li></ul><h2><strong>Breaking Changes</strong></h2><ul><li><p><strong>Legacy exercise categories no longer selectable in filters</strong>: Exercises previously tagged with the old categories (Cardio, Strength, Stretching, Warmup) will still open and save correctly, and their data is preserved, but they will not match any of the new filter options on the exercises page until their categories are updated. We recommend re-tagging older exercises with the new goal-oriented categories so they remain easy to discover.</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"2.20.13","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-10T07:03:04.718Z","publishedAt":"2026-04-10T07:03:14.636Z","status":"published","updatedAt":"2026-04-10T07:03:14.636Z"},{"id":"PXyPzYQc7FcWBLAAW6Yo","title":"Assessment-Only Reassessment Flow, AI Trainer Chat Upgrade, Safe-Area Rework Across Screens","slug":"assessment-only-reassessment-flow-ai-trainer-chat-upgrade-safe-area-rework-acros","summary":"This release introduces an assessment-only entry point for reassessment flows, significantly upgrades the AI Trainer chat experience (rich markdown, smart exercise grouping, refreshed goal/duration options), and reworks safe-area handling across challenge, workout, plan, and check-frame screens so content extends edge-to-edge on notched devices. No PostMessage event changes — integrators only need to act if they want to use the new assessmentOnly configuration option.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" — plus AI Trainer (not a standalone integration option but embedded within supported flows)\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment-only reassessment mode\"},{\"type\":\"text\",\"text\":\": Integrators can now set \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessmentOnly: true\"},{\"type\":\"text\",\"text\":\" in the plan onboarding prefill configuration to skip all survey screens (goal, health issues, injuries, duration, lifestyle) and drop the user directly into the fitness assessment. Designed for reassessment flows where user preferences are already known. Any previously stored plan ID is cleared so a fresh plan is generated after the assessment completes.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer: Rich markdown messages\"},{\"type\":\"text\",\"text\":\": Assistant responses now render as formatted markdown — headings, bullet/numbered lists, tables, code blocks, blockquotes, bold/italic, and clickable links (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"target=\\\"_blank\\\"\"},{\"type\":\"text\",\"text\":\"). Messages are also selectable and copyable via long-press.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer: Workout plan grouping\"},{\"type\":\"text\",\"text\":\": Consecutive identical exercises collapse into \\\"Set xN\\\" cards and repeating multi-exercise blocks collapse into \\\"Round xN\\\" groups, making complex workout plans much easier to scan. New/Updated badges bubble up to group headers.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Safe-area / notch handling reworked\"},{\"type\":\"text\",\"text\":\": On notched devices (iPhone Dynamic Island, etc.), headers, exit buttons, floating camera buttons, and overlays now sit correctly below the notch while their container backgrounds extend edge-to-edge underneath it. Affected screens: challenge video, workout layout, exercise detail, check-frame, plan detail, and workout detail (both default and fitpass variants).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Personalized plans respect session duration\"},{\"type\":\"text\",\"text\":\": The preferred workout duration selected during plan onboarding is now sent to the plan-generation API, so generated plans match the user's chosen session length.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer: Updated duration options\"},{\"type\":\"text\",\"text\":\": Available session durations changed from 15 / 30 / 45 / 60 minutes to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"5 / 10 / 20 / 30 / 60 minutes\"},{\"type\":\"text\",\"text\":\".\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer: Refreshed goal list\"},{\"type\":\"text\",\"text\":\": Goals restructured — \\\"Tone Muscle\\\" → \\\"Muscle Gain\\\", \\\"Build Strength\\\" → \\\"Strength\\\", \\\"Cross Training\\\" → \\\"Cardio/Endurance\\\"; \\\"Gain Mass\\\" and \\\"Bodybuilding\\\" removed; new \\\"Wellness/Flexibility\\\" added; emoji prefixes removed from all labels.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer: Exclusive \\\"Full Body\\\" selection\"},{\"type\":\"text\",\"text\":\": Selecting \\\"Full Body\\\" in the body-parts picker now clears any individual body part selections, and vice versa.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer: Auto-expand plan preview\"},{\"type\":\"text\",\"text\":\": When the assistant returns a new or modified workout plan, the plan preview automatically expands — unless the assistant's turn is a clarifying question.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Guide tooltip positioning on desktop\"},{\"type\":\"text\",\"text\":\": Tooltips now correctly target the visible element when desktop and mobile variants share the same guide ID, preventing tooltips from anchoring to hidden elements.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Guide tooltip step transitions\"},{\"type\":\"text\",\"text\":\": Eliminated visual \\\"jumping\\\" where the tooltip briefly appeared at the previous step's position before moving to the new target.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout guide on desktop\"},{\"type\":\"text\",\"text\":\": Fixed exercise list guide tooltip display and muscles section highlight/text visibility during the guided tour.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Challenge guide\"},{\"type\":\"text\",\"text\":\": Fixed guide tooltip targeting on the challenge screen.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise detail on desktop\"},{\"type\":\"text\",\"text\":\": Fixed display issues with the exercise detail page on wider viewports.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (Configuration)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — this is a purely additive change. Existing integrations are unaffected.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessmentOnly\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" option on plan onboarding prefill\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"A new optional boolean field allows integrators to trigger a reassessment flow that skips the full onboarding survey.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"planOnboardingPrefill\\\": {\\n    \\\"goal\\\": \\\"weight_loss\\\",\\n    \\\"healthIssues\\\": [],\\n    \\\"injuries\\\": [],\\n    \\\"duration\\\": 30,\\n    \\\"lifestyle\\\": \\\"active\\\"\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After (new field available):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"planOnboardingPrefill\\\": {\\n    \\\"assessmentOnly\\\": true\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"When \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessmentOnly\"},{\"type\":\"text\",\"text\":\" is \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"true\"},{\"type\":\"text\",\"text\":\", all other prefill fields are ignored and the user lands directly on the fitness assessment screen. A fresh personalized plan is generated after the assessment completes.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": No action required. The field is optional and defaults to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"false\"},{\"type\":\"text\",\"text\":\" (existing behavior unchanged).\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: <code>Challenge</code> · <code>Workout</code> · <code>Plan</code> · <code>Personalized Plan</code> · <code>Camera</code> · <code>Assessments</code> — plus AI Trainer (not a standalone integration option but embedded within supported flows)</p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Assessment-only reassessment mode</strong>: Integrators can now set <code>assessmentOnly: true</code> in the plan onboarding prefill configuration to skip all survey screens (goal, health issues, injuries, duration, lifestyle) and drop the user directly into the fitness assessment. Designed for reassessment flows where user preferences are already known. Any previously stored plan ID is cleared so a fresh plan is generated after the assessment completes.</p></li><li><p><strong>AI Trainer: Rich markdown messages</strong>: Assistant responses now render as formatted markdown — headings, bullet/numbered lists, tables, code blocks, blockquotes, bold/italic, and clickable links (<code>target=\"_blank\"</code>). Messages are also selectable and copyable via long-press.</p></li><li><p><strong>AI Trainer: Workout plan grouping</strong>: Consecutive identical exercises collapse into \"Set xN\" cards and repeating multi-exercise blocks collapse into \"Round xN\" groups, making complex workout plans much easier to scan. New/Updated badges bubble up to group headers.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Safe-area / notch handling reworked</strong>: On notched devices (iPhone Dynamic Island, etc.), headers, exit buttons, floating camera buttons, and overlays now sit correctly below the notch while their container backgrounds extend edge-to-edge underneath it. Affected screens: challenge video, workout layout, exercise detail, check-frame, plan detail, and workout detail (both default and fitpass variants).</p></li><li><p><strong>Personalized plans respect session duration</strong>: The preferred workout duration selected during plan onboarding is now sent to the plan-generation API, so generated plans match the user's chosen session length.</p></li><li><p><strong>AI Trainer: Updated duration options</strong>: Available session durations changed from 15 / 30 / 45 / 60 minutes to <strong>5 / 10 / 20 / 30 / 60 minutes</strong>.</p></li><li><p><strong>AI Trainer: Refreshed goal list</strong>: Goals restructured — \"Tone Muscle\" → \"Muscle Gain\", \"Build Strength\" → \"Strength\", \"Cross Training\" → \"Cardio/Endurance\"; \"Gain Mass\" and \"Bodybuilding\" removed; new \"Wellness/Flexibility\" added; emoji prefixes removed from all labels.</p></li><li><p><strong>AI Trainer: Exclusive \"Full Body\" selection</strong>: Selecting \"Full Body\" in the body-parts picker now clears any individual body part selections, and vice versa.</p></li><li><p><strong>AI Trainer: Auto-expand plan preview</strong>: When the assistant returns a new or modified workout plan, the plan preview automatically expands — unless the assistant's turn is a clarifying question.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Guide tooltip positioning on desktop</strong>: Tooltips now correctly target the visible element when desktop and mobile variants share the same guide ID, preventing tooltips from anchoring to hidden elements.</p></li><li><p><strong>Guide tooltip step transitions</strong>: Eliminated visual \"jumping\" where the tooltip briefly appeared at the previous step's position before moving to the new target.</p></li><li><p><strong>Workout guide on desktop</strong>: Fixed exercise list guide tooltip display and muscles section highlight/text visibility during the guided tour.</p></li><li><p><strong>Challenge guide</strong>: Fixed guide tooltip targeting on the challenge screen.</p></li><li><p><strong>Exercise detail on desktop</strong>: Fixed display issues with the exercise detail page on wider viewports.</p></li></ul><h2><strong>API Changes (Configuration)</strong></h2><blockquote><p><strong>Action Required</strong>: No — this is a purely additive change. Existing integrations are unaffected.</p></blockquote><h3><strong>New </strong><code>assessmentOnly</code><strong> option on plan onboarding prefill</strong></h3><p>A new optional boolean field allows integrators to trigger a reassessment flow that skips the full onboarding survey.</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"planOnboardingPrefill\": {\n    \"goal\": \"weight_loss\",\n    \"healthIssues\": [],\n    \"injuries\": [],\n    \"duration\": 30,\n    \"lifestyle\": \"active\"\n  }\n}</code></pre><p><strong>After (new field available):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"planOnboardingPrefill\": {\n    \"assessmentOnly\": true\n  }\n}</code></pre><p>When <code>assessmentOnly</code> is <code>true</code>, all other prefill fields are ignored and the user lands directly on the fitness assessment screen. A fresh personalized plan is generated after the assessment completes.</p><p><strong>Migration</strong>: No action required. The field is optional and defaults to <code>false</code> (existing behavior unchanged).</p>","coverImage":null,"category":"new","product":"client","version":"2.31.0","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-10T06:30:07.727Z","publishedAt":"2026-04-10T06:30:59.756Z","status":"published","updatedAt":"2026-04-10T06:30:59.756Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-10T06:31:16.303Z","subscriberCountAtSend":19},{"id":"WsFzCXJ4aWGbZuzT0JjE","title":"Languages filter added to content listing endpoints","slug":"languages-filter-added-to-content-listing-endpoints","summary":"All three content listing API endpoints (exercises, workouts, and plans) now support an optional translation_languages parameter, allowing callers to request content filtered by available translations. This is an additive, non-breaking change — no action is required for existing integrations.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas: \"},{\"type\":\"text\",\"text\":\"Exercises · Workouts · Plans\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Filter content by translation languages\"},{\"type\":\"text\",\"text\":\": You can now pass a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"translation_languages\"},{\"type\":\"text\",\"text\":\" query parameter when listing exercises, workouts, or plans to retrieve only content available in specific languages.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (Exercises / Workouts / Plans)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — this is an additive change. Existing requests continue to work as before.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"GET /api/v1/exercises\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"New optional query parameter \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"translation_languages\"},{\"type\":\"text\",\"text\":\" to filter exercises by available translations. Accepts a comma-separated string or repeated query parameters.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"GET /api/v1/exercises?lang=en&limit=10\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"GET /api/v1/exercises?lang=en&limit=10&translation_languages=es,fr\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"or\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"GET /api/v1/exercises?lang=en&limit=10&translation_languages=es&translation_languages=fr\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required — the parameter is optional.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"GET /api/v1/workouts\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"New optional query parameter \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"translation_languages\"},{\"type\":\"text\",\"text\":\" to filter workouts by available translations. Accepts a comma-separated string or repeated query parameters.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"GET /api/v1/workouts?lang=en&limit=10\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"GET /api/v1/workouts?lang=en&limit=10&translation_languages=es,fr\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"or\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"GET /api/v1/workouts?lang=en&limit=10&translation_languages=es&translation_languages=fr\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required — the parameter is optional.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"GET /api/v1/plans\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"New optional query parameter \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"translation_languages\"},{\"type\":\"text\",\"text\":\" to filter plans by available translations. Accepts a comma-separated string or repeated query parameters.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"GET /api/v1/plans?lang=en&limit=10\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"GET /api/v1/plans?lang=en&limit=10&translation_languages=es,fr\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"or\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"GET /api/v1/plans?lang=en&limit=10&translation_languages=es&translation_languages=fr\\n\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": None required — the parameter is optional.\"}]}]}","contentHtml":"<p><strong>Impacted areas: </strong>Exercises · Workouts · Plans</p><h2><strong>New Features</strong></h2><ul><li><p><strong>Filter content by translation languages</strong>: You can now pass a <code>translation_languages</code> query parameter when listing exercises, workouts, or plans to retrieve only content available in specific languages.</p></li></ul><h2><strong>API Changes (Exercises / Workouts / Plans)</strong></h2><blockquote><p><strong>Action Required</strong>: No — this is an additive change. Existing requests continue to work as before.</p></blockquote><h3><code>GET /api/v1/exercises</code></h3><p>New optional query parameter <code>translation_languages</code> to filter exercises by available translations. Accepts a comma-separated string or repeated query parameters.</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>GET /api/v1/exercises?lang=en&amp;limit=10\n</code></pre><p><strong>After:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>GET /api/v1/exercises?lang=en&amp;limit=10&amp;translation_languages=es,fr\n</code></pre><p>or</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>GET /api/v1/exercises?lang=en&amp;limit=10&amp;translation_languages=es&amp;translation_languages=fr\n</code></pre><p><strong>Migration</strong>: None required — the parameter is optional.</p><hr><h3><code>GET /api/v1/workouts</code></h3><p>New optional query parameter <code>translation_languages</code> to filter workouts by available translations. Accepts a comma-separated string or repeated query parameters.</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>GET /api/v1/workouts?lang=en&amp;limit=10\n</code></pre><p><strong>After:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>GET /api/v1/workouts?lang=en&amp;limit=10&amp;translation_languages=es,fr\n</code></pre><p>or</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>GET /api/v1/workouts?lang=en&amp;limit=10&amp;translation_languages=es&amp;translation_languages=fr\n</code></pre><p><strong>Migration</strong>: None required — the parameter is optional.</p><hr><h3><code>GET /api/v1/plans</code></h3><p>New optional query parameter <code>translation_languages</code> to filter plans by available translations. Accepts a comma-separated string or repeated query parameters.</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>GET /api/v1/plans?lang=en&amp;limit=10\n</code></pre><p><strong>After:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>GET /api/v1/plans?lang=en&amp;limit=10&amp;translation_languages=es,fr\n</code></pre><p>or</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>GET /api/v1/plans?lang=en&amp;limit=10&amp;translation_languages=es&amp;translation_languages=fr\n</code></pre><p><strong>Migration</strong>: None required — the parameter is optional.</p>","coverImage":null,"category":"new","version":"2.20.13","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-07T07:28:28.731Z","product":"client","publishedAt":"2026-04-07T07:28:54.296Z","status":"published","updatedAt":"2026-04-07T07:28:54.296Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-07T07:29:10.214Z","subscriberCountAtSend":19},{"id":"LkxEp1hqeXgannCvgEnP","title":"Fix: Exercise duplication no longer fails due to invalid ID values","slug":"fix-exercise-duplication-no-longer-fails-due-to-invalid-id-values","summary":"Fixed a bug where duplicating an exercise could fail or produce errors because certain ID values were stored as text instead of numbers. This affected all admin users who duplicate exercises. No action is required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas: \"},{\"type\":\"text\",\"text\":\"Exercises\"}]}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\\nExercise duplication error: \"},{\"type\":\"text\",\"text\":\"Duplicating an exercise could fail silently or produce backend errors when the original exercise's AI model or parent exercise IDs were in an unexpected format. These values are now properly validated and converted before being sent to the server, so exercise duplication works reliably.\\nExercise AI model reassignment: Changing the AI model on an existing exercise could also fail if the model ID was in an unexpected format. This is now handled correctly.\"}]}]}]}]}","contentHtml":"<blockquote><p><strong>Impacted areas: </strong>Exercises</p></blockquote><ul><li><p><strong>Bug Fixes\nExercise duplication error: </strong>Duplicating an exercise could fail silently or produce backend errors when the original exercise's AI model or parent exercise IDs were in an unexpected format. These values are now properly validated and converted before being sent to the server, so exercise duplication works reliably.\nExercise AI model reassignment: Changing the AI model on an existing exercise could also fail if the model ID was in an unexpected format. This is now handled correctly.</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"2.20.12","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-06T09:15:07.244Z","publishedAt":"2026-04-06T09:15:20.018Z","status":"published","updatedAt":"2026-04-06T09:15:20.018Z"},{"id":"aJFw50WTV5m7CQT9jyre","title":"Clearer, more helpful notification messages across the dashboard","slug":"clearer-more-helpful-notification-messages-across-the-dashboard","summary":"All notification messages (success confirmations, error alerts, warnings, and informational tips) throughout the admin dashboard have been rewritten to be clearer, more specific, and more actionable. This affects all dashboard users across all companies. No action is required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas: \"},{\"type\":\"text\",\"text\":\"Exercises · Workouts · Plans · Theme Editor · Company Management · API Keys · Authentication · Design Customization · Dashboard\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\\nError messages now explain what to do next: \"},{\"type\":\"text\",\"text\":\"Instead of generic messages like \\\"Something went wrong\\\" or \\\"Failed to upload image,\\\" error messages now describe the problem and suggest a fix — for example, \\\"Cover image upload failed. Please check the file format and size, then try again.\\\"\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Success confirmations are more specific: \"},{\"type\":\"text\",\"text\":\"Messages like \\\"Image uploaded successfully!\\\" now tell you exactly what succeeded — \\\"Cover image uploaded successfully\\\" or \\\"Plan image uploaded successfully\\\" — so you always know what just happened.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Sign-in and account errors are friendlier: \"},{\"type\":\"text\",\"text\":\"Login failures now say \\\"Could not sign in. Please check your credentials and try again\\\" instead of the vague \\\"Error logging in.\\\" Password reset, email verification, and sign-up messages are similarly improved.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Permission errors include next steps: \"},{\"type\":\"text\",\"text\":\"If you try to duplicate content you don't have access to, the message now tells you to \\\"Contact the account owner for access\\\" rather than simply stating the restriction.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"2FA and verification messages are clearer: \"},{\"type\":\"text\",\"text\":\"\\\"OTP sent\\\" is now \\\"Verification code sent,\\\" and error messages specify whether the code was incorrect or couldn't be sent.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"File upload and validation messages are more descriptive: \"},{\"type\":\"text\",\"text\":\"\\\"Invalid file type\\\" is now \\\"Unsupported file type,\\\" \\\"Invalid file size\\\" is now \\\"File is too large. Please upload a smaller file,\\\" and image dimension requirements explain exactly what's needed.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Session expiry is clearly communicated: \"},{\"type\":\"text\",\"text\":\"The generic \\\"Missing or invalid authorization header\\\" message is now \\\"Your session has expired. Please sign in again.\\\"\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Theme editor messages are more natural: \"},{\"type\":\"text\",\"text\":\"Save, rename, reset, and error messages in the theme editor now use plain language — for example, \\\"Colors reset to defaults\\\" instead of \\\"Reset to default colors.\\\"\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Team management messages are more helpful: \"},{\"type\":\"text\",\"text\":\"Duplicate invitation attempts now say \\\"An invitation has already been sent to this email address,\\\" and removal errors specify what went wrong and what to try.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Support ticket and equipment messages improved: \"},{\"type\":\"text\",\"text\":\"File attachment limits, translation results, and equipment save/delete confirmations all use clearer, friendlier wording.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\\nPassword reset error now shows the correct message: \"},{\"type\":\"text\",\"text\":\"The password reset flow previously could show a generic fallback error instead of the actual error returned by the server. This is now fixed so you see the real reason if a reset fails.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted areas: </strong>Exercises · Workouts · Plans · Theme Editor · Company Management · API Keys · Authentication · Design Customization · Dashboard</p><ul><li><p><strong>Improvements\nError messages now explain what to do next: </strong>Instead of generic messages like \"Something went wrong\" or \"Failed to upload image,\" error messages now describe the problem and suggest a fix — for example, \"Cover image upload failed. Please check the file format and size, then try again.\"</p></li><li><p><strong>Success confirmations are more specific: </strong>Messages like \"Image uploaded successfully!\" now tell you exactly what succeeded — \"Cover image uploaded successfully\" or \"Plan image uploaded successfully\" — so you always know what just happened.</p></li><li><p><strong>Sign-in and account errors are friendlier: </strong>Login failures now say \"Could not sign in. Please check your credentials and try again\" instead of the vague \"Error logging in.\" Password reset, email verification, and sign-up messages are similarly improved.</p></li><li><p><strong>Permission errors include next steps: </strong>If you try to duplicate content you don't have access to, the message now tells you to \"Contact the account owner for access\" rather than simply stating the restriction.</p></li><li><p><strong>2FA and verification messages are clearer: </strong>\"OTP sent\" is now \"Verification code sent,\" and error messages specify whether the code was incorrect or couldn't be sent.</p></li><li><p><strong>File upload and validation messages are more descriptive: </strong>\"Invalid file type\" is now \"Unsupported file type,\" \"Invalid file size\" is now \"File is too large. Please upload a smaller file,\" and image dimension requirements explain exactly what's needed.</p></li><li><p><strong>Session expiry is clearly communicated: </strong>The generic \"Missing or invalid authorization header\" message is now \"Your session has expired. Please sign in again.\"</p></li><li><p><strong>Theme editor messages are more natural: </strong>Save, rename, reset, and error messages in the theme editor now use plain language — for example, \"Colors reset to defaults\" instead of \"Reset to default colors.\"</p></li><li><p><strong>Team management messages are more helpful: </strong>Duplicate invitation attempts now say \"An invitation has already been sent to this email address,\" and removal errors specify what went wrong and what to try.</p></li><li><p><strong>Support ticket and equipment messages improved: </strong>File attachment limits, translation results, and equipment save/delete confirmations all use clearer, friendlier wording.</p></li><li><p><strong>Bug Fixes\nPassword reset error now shows the correct message: </strong>The password reset flow previously could show a generic fallback error instead of the actual error returned by the server. This is now fixed so you see the real reason if a reset fails.</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"2.20.11","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-04T08:28:54.715Z","publishedAt":"2026-04-04T08:30:59.448Z","status":"published","updatedAt":"2026-04-04T08:30:59.448Z"},{"id":"FphKA2G0gYFiKOU6z3D4","title":"AI Trainer Integration, Redesigned Onboarding Flow, Color Chase Game Improvements","slug":"ai-trainer-integration-redesigned-onboarding-flow-color-chase-game-improvements","summary":"This release introduces our new (beta) AI Trainer integration option — a chat-based workout planning experience powered by LLM. The onboarding/model-loading flow has been fully redesigned with a swipeable tips carousel. The workout_ended PostMessage event now includes an exit_type field distinguishing user-initiated exits from completed workouts. A new gameTotalRounds configuration option lets integrators customize Color Chase game length. Integrators consuming workout_ended should note the new additive field.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · All Integrations (shared onboarding/model-loading flow)\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI Trainer (Please contact KinesteX for details)\"},{\"type\":\"text\",\"text\":\": New chat-based workout planning experience where users interact with an AI trainer to generate personalized workouts based on their goals, equipment, and preferences. Includes profile settings (age, weight, height, gender), workout history, and exercise preview with the ability to launch workouts directly from chat.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Configurable Color Chase rounds\"},{\"type\":\"text\",\"text\":\": Integrators can now pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"gameTotalRounds\"},{\"type\":\"text\",\"text\":\" in the customization data to control how many rounds the Color Chase game lasts (default: 10). Rounds beyond 10 are procedurally generated with increasing difficulty.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Redesigned model loading screen\"},{\"type\":\"text\",\"text\":\": The loading screen now features a full-screen swipeable image carousel with setup tips, a linear progress bar at the bottom, and a \\\"Continue\\\" button that appears when loading completes (previously auto-redirected for non-assessment flows). Images are cached in IndexedDB for faster subsequent loads.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Redesigned onboarding camera access\"},{\"type\":\"text\",\"text\":\": The camera permission screen now includes an instructional video and clickable cookie/terms links that open detail modals.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Color Chase game visual overhaul\"},{\"type\":\"text\",\"text\":\": Balloons now spawn in an arch formation above the user's shoulders (adapts to player position) instead of a fixed horizontal row. Balloons display numbered labels for easier sequence recall. Score display moved to bottom-center with a card-style background. The check-frame step now uses the standard full-body detection overlay for consistency.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Color Chase wide-screen fix\"},{\"type\":\"text\",\"text\":\": Balloon placement now correctly adapts to wide viewport ratios, preventing balloons from rendering outside the visible camera area.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improved assessment workout scoring\"},{\"type\":\"text\",\"text\":\": Assessment results now use a detailed per-exercise scoring algorithm that accounts for hold-position accuracy, rep-by-rep accuracy, and volume — replacing the previous generic efficiency score.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Hold-position drift tolerance\"},{\"type\":\"text\",\"text\":\": Hold exercises (e.g., plank) now include drift tolerance logic — brief departures from the target position during a hold no longer immediately reset the hold timer, reducing false negatives.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Skeleton tip color\"},{\"type\":\"text\",\"text\":\": When the AI provides form tips, the skeleton overlay now highlights in orange instead of the default color, making tips visually distinct from correct/incorrect feedback.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"\\\"No AI\\\" mode restricted for Challenges and Assessments\"},{\"type\":\"text\",\"text\":\": The \\\"Continue without AI\\\" option is no longer available for challenge and assessment flows, ensuring AI tracking is always active for these modes.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Instruction mode display fix\"},{\"type\":\"text\",\"text\":\": Instruction mode (video-only exercise preview) now renders at full width consistently.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Text direction support\"},{\"type\":\"text\",\"text\":\": Assessment and challenge action buttons now respect RTL/LTR text direction.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Camera access modal shown twice\"},{\"type\":\"text\",\"text\":\": Fixed an issue where the camera permission modal could appear a second time on the accelerometer screen.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"First-open back navigation\"},{\"type\":\"text\",\"text\":\": Fixed incorrect back navigation on the very first app open.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"iOS chat scrolling\"},{\"type\":\"text\",\"text\":\": Fixed an issue where tapping empty space in the chat input area triggered unwanted scrolling on iOS.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Safe area rendering\"},{\"type\":\"text\",\"text\":\": Fixed safe-area inset handling on the model loading and accelerometer screens to prevent content from being obscured by device notches.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No (additive change) — but recommended to start consuming \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exit_type\"},{\"type\":\"text\",\"text\":\" if you differentiate between user exits and completed workouts.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exit_type\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" field on \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_ended\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_ended\"},{\"type\":\"text\",\"text\":\" event now includes an \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exit_type\"},{\"type\":\"text\",\"text\":\" field indicating whether the workout was completed normally or the user exited early.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"workout_ended\\\",\\n  \\\"id\\\": \\\"workout_abc\\\",\\n  \\\"date\\\": \\\"2026-04-03T12:00:00\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"workout_ended\\\",\\n  \\\"id\\\": \\\"workout_abc\\\",\\n  \\\"exit_type\\\": \\\"complete\\\",\\n  \\\"date\\\": \\\"2026-04-03T12:00:00\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exit_type\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" valueMeaning\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"complete\\\"\"},{\"type\":\"text\",\"text\":\"User finished all exercises in the workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exit\\\"\"},{\"type\":\"text\",\"text\":\"User manually exited before completing the workout\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": This is an additive change — no existing fields were removed or renamed. If you want to distinguish completed workouts from early exits, read the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exit_type\"},{\"type\":\"text\",\"text\":\" field. The field defaults to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"exit\\\"\"},{\"type\":\"text\",\"text\":\" when the user leaves via the exit button.\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"gameTotalRounds\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" configuration option\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Integrators can now pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"gameTotalRounds\"},{\"type\":\"text\",\"text\":\" (number) in the customization payload to control the number of rounds in the Color Chase game.\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"gameTotalRounds\\\": 15\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": Optional. If not provided, the Color Chase game defaults to 10 rounds (unchanged behavior).\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Challenge</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Personalized Plan</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Camera</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Assessments</code><span style=\"color: rgb(240, 246, 252);\"> · All Integrations (shared onboarding/model-loading flow)</span></p><h2><strong>New Features</strong></h2><ul><li><p><strong>AI Trainer (Please contact KinesteX for details)</strong>: New chat-based workout planning experience where users interact with an AI trainer to generate personalized workouts based on their goals, equipment, and preferences. Includes profile settings (age, weight, height, gender), workout history, and exercise preview with the ability to launch workouts directly from chat.</p></li><li><p><strong>Configurable Color Chase rounds</strong>: Integrators can now pass <code>gameTotalRounds</code> in the customization data to control how many rounds the Color Chase game lasts (default: 10). Rounds beyond 10 are procedurally generated with increasing difficulty.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Redesigned model loading screen</strong>: The loading screen now features a full-screen swipeable image carousel with setup tips, a linear progress bar at the bottom, and a \"Continue\" button that appears when loading completes (previously auto-redirected for non-assessment flows). Images are cached in IndexedDB for faster subsequent loads.</p></li><li><p><strong>Redesigned onboarding camera access</strong>: The camera permission screen now includes an instructional video and clickable cookie/terms links that open detail modals.</p></li><li><p><strong>Color Chase game visual overhaul</strong>: Balloons now spawn in an arch formation above the user's shoulders (adapts to player position) instead of a fixed horizontal row. Balloons display numbered labels for easier sequence recall. Score display moved to bottom-center with a card-style background. The check-frame step now uses the standard full-body detection overlay for consistency.</p></li><li><p><strong>Color Chase wide-screen fix</strong>: Balloon placement now correctly adapts to wide viewport ratios, preventing balloons from rendering outside the visible camera area.</p></li><li><p><strong>Improved assessment workout scoring</strong>: Assessment results now use a detailed per-exercise scoring algorithm that accounts for hold-position accuracy, rep-by-rep accuracy, and volume — replacing the previous generic efficiency score.</p></li><li><p><strong>Hold-position drift tolerance</strong>: Hold exercises (e.g., plank) now include drift tolerance logic — brief departures from the target position during a hold no longer immediately reset the hold timer, reducing false negatives.</p></li><li><p><strong>Skeleton tip color</strong>: When the AI provides form tips, the skeleton overlay now highlights in orange instead of the default color, making tips visually distinct from correct/incorrect feedback.</p></li><li><p><strong>\"No AI\" mode restricted for Challenges and Assessments</strong>: The \"Continue without AI\" option is no longer available for challenge and assessment flows, ensuring AI tracking is always active for these modes.</p></li><li><p><strong>Instruction mode display fix</strong>: Instruction mode (video-only exercise preview) now renders at full width consistently.</p></li><li><p><strong>Text direction support</strong>: Assessment and challenge action buttons now respect RTL/LTR text direction.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Camera access modal shown twice</strong>: Fixed an issue where the camera permission modal could appear a second time on the accelerometer screen.</p></li><li><p><strong>First-open back navigation</strong>: Fixed incorrect back navigation on the very first app open.</p></li><li><p><strong>iOS chat scrolling</strong>: Fixed an issue where tapping empty space in the chat input area triggered unwanted scrolling on iOS.</p></li><li><p><strong>Safe area rendering</strong>: Fixed safe-area inset handling on the model loading and accelerometer screens to prevent content from being obscured by device notches.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No (additive change) — but recommended to start consuming <code>exit_type</code> if you differentiate between user exits and completed workouts.</p></blockquote><h3><strong>New </strong><code>exit_type</code><strong> field on </strong><code>workout_ended</code></h3><p>The <code>workout_ended</code> event now includes an <code>exit_type</code> field indicating whether the workout was completed normally or the user exited early.</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"workout_ended\",\n  \"id\": \"workout_abc\",\n  \"date\": \"2026-04-03T12:00:00\"\n}</code></pre><p><strong>After:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"workout_ended\",\n  \"id\": \"workout_abc\",\n  \"exit_type\": \"complete\",\n  \"date\": \"2026-04-03T12:00:00\"\n}</code></pre><p><code>exit_type</code><strong> valueMeaning</strong><code>\"complete\"</code>User finished all exercises in the workout<code>\"exit\"</code>User manually exited before completing the workout</p><p><strong>Migration</strong>: This is an additive change — no existing fields were removed or renamed. If you want to distinguish completed workouts from early exits, read the <code>exit_type</code> field. The field defaults to <code>\"exit\"</code> when the user leaves via the exit button.</p><h3><strong>New </strong><code>gameTotalRounds</code><strong> configuration option</strong></h3><p>Integrators can now pass <code>gameTotalRounds</code> (number) in the customization payload to control the number of rounds in the Color Chase game.</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"gameTotalRounds\": 15\n}</code></pre><p><strong>Migration</strong>: Optional. If not provided, the Color Chase game defaults to 10 rounds (unchanged behavior).</p>","coverImage":null,"category":"new","product":"client","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-03T20:48:04.653Z","version":"2.30.0","publishedAt":"2026-04-03T20:48:36.642Z","status":"published","updatedAt":"2026-04-03T20:48:36.642Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-03T20:48:51.621Z","subscriberCountAtSend":19},{"id":"pjDS6OJtAGN9XocK2LZ4","title":"Workout editor overhaul: smarter rest periods, improved audio & video uploads, and bug fixes","slug":"workout-editor-overhaul-smarter-rest-periods-improved-audio-video-uploads-and-bu","summary":"This update significantly improves the workout editing experience — rest periods are now auto-inserted with audio inherited from exercises, video and audio uploads are more reliable, and several bugs (including infinite loading on new items) are fixed. Affects all users who create or edit workouts, exercises, and plans in the admin dashboard. No manual action required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plans\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Shared/Infrastructure\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Auto-inserted rest periods with inherited audio\"},{\"type\":\"text\",\"text\":\": When you add an exercise to a workout, a rest period is now automatically placed before it (except at the start or when an intro rest already exists). The auto-inserted rest inherits the exercise's rest speech text, voice settings, and audio URLs — no need to configure rest audio manually.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Rest audio source badges\"},{\"type\":\"text\",\"text\":\": Rest and outro items in the workout sequence now display a badge showing where their audio comes from — \\\"Custom audio\\\", \\\"From exercise\\\", \\\"Saved audio\\\", or \\\"No audio\\\" — so you can tell at a glance which rests need attention.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Smart rest audio on drag-and-drop reorder\"},{\"type\":\"text\",\"text\":\": When you reorder exercises by dragging, rest items automatically update their audio to match their new neighboring exercise. Custom audio you uploaded yourself is never overwritten.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Video uploads now use direct cloud upload\"},{\"type\":\"text\",\"text\":\": Rest and outro video uploads no longer compress on your device. Files are uploaded directly to the cloud and processed server-side, making uploads faster and more predictable. A 10 MB file size limit now applies.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Audio upload retry on network errors\"},{\"type\":\"text\",\"text\":\": If a custom audio upload fails due to a network hiccup or a brief server issue, the system now automatically retries up to 3 times before showing an error.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Broader audio file support\"},{\"type\":\"text\",\"text\":\": M4A files that were previously rejected due to strict file type checks are now accepted. The system is more lenient with audio formats while still blocking actual video files.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Outro layout cleaned up\"},{\"type\":\"text\",\"text\":\": Outro items no longer display the repeats/countdown input fields, and the remaining fields use a wider layout for better readability.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Better validation error messages\"},{\"type\":\"text\",\"text\":\": Validation errors for outros now include the position in the sequence. The \\\"consecutive rest\\\" error now points to the second rest period instead of the first, making it easier to find and fix the issue.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Voice actor names displayed consistently\"},{\"type\":\"text\",\"text\":\": Internal voice IDs (e.g., for Glinda) are now automatically converted to human-readable names everywhere in the workout and exercise editors.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Male video/thumbnail fallback\"},{\"type\":\"text\",\"text\":\": Exercises and workout sequence items now automatically fall back to the male video and thumbnail when the primary media is missing.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Upload paths scoped to your company\"},{\"type\":\"text\",\"text\":\": Audio uploads are now stored in company-specific folders for better organization and security.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Infinite loading on new items fixed\"},{\"type\":\"text\",\"text\":\": Creating a new workout, exercise, or plan no longer causes a permanent loading spinner. The page now loads correctly and lets you start editing immediately.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout rest audio bug fixed\"},{\"type\":\"text\",\"text\":\": Rest audio settings are now properly preserved and linked when adding, copying, or reordering exercises in a workout.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Audio file re-selection after failure\"},{\"type\":\"text\",\"text\":\": If an audio upload fails, you can now select the same file again without needing to pick a different one first.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted areas</strong>: <code>Exercises</code> · <code>Workouts</code> · <code>Plans</code> · <code>Shared/Infrastructure</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Auto-inserted rest periods with inherited audio</strong>: When you add an exercise to a workout, a rest period is now automatically placed before it (except at the start or when an intro rest already exists). The auto-inserted rest inherits the exercise's rest speech text, voice settings, and audio URLs — no need to configure rest audio manually.</p></li><li><p><strong>Rest audio source badges</strong>: Rest and outro items in the workout sequence now display a badge showing where their audio comes from — \"Custom audio\", \"From exercise\", \"Saved audio\", or \"No audio\" — so you can tell at a glance which rests need attention.</p></li><li><p><strong>Smart rest audio on drag-and-drop reorder</strong>: When you reorder exercises by dragging, rest items automatically update their audio to match their new neighboring exercise. Custom audio you uploaded yourself is never overwritten.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Video uploads now use direct cloud upload</strong>: Rest and outro video uploads no longer compress on your device. Files are uploaded directly to the cloud and processed server-side, making uploads faster and more predictable. A 10 MB file size limit now applies.</p></li><li><p><strong>Audio upload retry on network errors</strong>: If a custom audio upload fails due to a network hiccup or a brief server issue, the system now automatically retries up to 3 times before showing an error.</p></li><li><p><strong>Broader audio file support</strong>: M4A files that were previously rejected due to strict file type checks are now accepted. The system is more lenient with audio formats while still blocking actual video files.</p></li><li><p><strong>Outro layout cleaned up</strong>: Outro items no longer display the repeats/countdown input fields, and the remaining fields use a wider layout for better readability.</p></li><li><p><strong>Better validation error messages</strong>: Validation errors for outros now include the position in the sequence. The \"consecutive rest\" error now points to the second rest period instead of the first, making it easier to find and fix the issue.</p></li><li><p><strong>Voice actor names displayed consistently</strong>: Internal voice IDs (e.g., for Glinda) are now automatically converted to human-readable names everywhere in the workout and exercise editors.</p></li><li><p><strong>Male video/thumbnail fallback</strong>: Exercises and workout sequence items now automatically fall back to the male video and thumbnail when the primary media is missing.</p></li><li><p><strong>Upload paths scoped to your company</strong>: Audio uploads are now stored in company-specific folders for better organization and security.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Infinite loading on new items fixed</strong>: Creating a new workout, exercise, or plan no longer causes a permanent loading spinner. The page now loads correctly and lets you start editing immediately.</p></li><li><p><strong>Workout rest audio bug fixed</strong>: Rest audio settings are now properly preserved and linked when adding, copying, or reordering exercises in a workout.</p></li><li><p><strong>Audio file re-selection after failure</strong>: If an audio upload fails, you can now select the same file again without needing to pick a different one first.</p></li></ul>","coverImage":null,"category":"improved","product":"dashboard","version":"2.20.10","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-03T07:13:52.237Z","publishedAt":"2026-04-03T07:13:57.745Z","status":"published","updatedAt":"2026-04-03T07:13:57.745Z"},{"id":"AX9yLhXp20OYkheX0NQv","title":"New disableCookies Configuration Option","slug":"new-disablecookies-configuration-option","summary":"A new disableCookies option is available in the customization settings. When enabled, the SDK will automatically decline all cookies on behalf of the user and hide cookie-related UI elements. No changes to PostMessage events — integrators only need to pass the new option if they want to opt out of cookies.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": All Integrations\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Disable Cookies Option\"},{\"type\":\"text\",\"text\":\": Integrators can now pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"disableCookies: true\"},{\"type\":\"text\",\"text\":\" in the customization options to prevent the SDK from storing any cookies or persistent tracking data. When enabled, cookies are automatically declined before any analytics initialization, the cookie management button is hidden from the settings screen, and the cookies consent link is removed from the onboarding screen.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — this is an additive, opt-in configuration option\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"disableCookies\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" customization parameter\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"disableCookies: true\"},{\"type\":\"text\",\"text\":\" in your customization options to disable all cookie storage. Default is \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"false\"},{\"type\":\"text\",\"text\":\" (existing behavior unchanged).\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before (no change needed if you don't use this feature):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"disableGuide\\\": false,\\n  \\\"hideStatisticsHeader\\\": false\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After (opt-in):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"disableCookies\\\": true,\\n  \\\"disableGuide\\\": false,\\n  \\\"hideStatisticsHeader\\\": false\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": No action required. This is fully backward-compatible. Only pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"disableCookies: true\"},{\"type\":\"text\",\"text\":\" if your application requires cookie-free operation (e.g., for GDPR compliance or privacy-sensitive environments).\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: All Integrations</p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Disable Cookies Option</strong>: Integrators can now pass <code>disableCookies: true</code> in the customization options to prevent the SDK from storing any cookies or persistent tracking data. When enabled, cookies are automatically declined before any analytics initialization, the cookie management button is hidden from the settings screen, and the cookies consent link is removed from the onboarding screen.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — this is an additive, opt-in configuration option</p></blockquote><h3><strong>New </strong><code>disableCookies</code><strong> customization parameter</strong></h3><p>Pass <code>disableCookies: true</code> in your customization options to disable all cookie storage. Default is <code>false</code> (existing behavior unchanged).</p><p><strong>Before (no change needed if you don't use this feature):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"disableGuide\": false,\n  \"hideStatisticsHeader\": false\n}</code></pre><p><strong>After (opt-in):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"disableCookies\": true,\n  \"disableGuide\": false,\n  \"hideStatisticsHeader\": false\n}</code></pre><p><strong>Migration</strong>: No action required. This is fully backward-compatible. Only pass <code>disableCookies: true</code> if your application requires cookie-free operation (e.g., for GDPR compliance or privacy-sensitive environments).</p>","coverImage":null,"category":"new","product":"client","version":"2.20.11","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-01T12:02:59.410Z","publishedAt":"2026-04-01T12:05:12.712Z","status":"published","updatedAt":"2026-04-01T12:05:12.712Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-04-01T12:05:29.359Z","subscriberCountAtSend":18},{"id":"MtLfMs6lASwicxzRLrfv","title":"Balloon Pop Rounds Mode, Improved Balloon Boundaries","slug":"balloon-pop-rounds-mode-improved-balloon-boundaries","summary":"This release adds a new rounds mode to the Balloon Pop game, allowing clients to configure a fixed number of rounds and balloons per round instead of the default timer-based gameplay. Balloon spawn boundaries have also been improved to stay within the visible play area. No breaking API changes — existing integrations continue to work without modification.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Games\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Balloon Pop Rounds Mode\"},{\"type\":\"text\",\"text\":\": The Balloon Pop game now supports a rounds-based configuration. When \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"reps\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"countdown\"},{\"type\":\"text\",\"text\":\" are provided in customParams, the game switches from a timed mode to a rounds mode where each round spawns a fixed number of balloons. The game ends after all rounds are completed. The in-game HUD displays the current round counter instead of a countdown timer, and the difficulty stage indicator is hidden.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"\"}}],\"text\":\"Quick Examples\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"4 rounds of 2 balloons\"},{\"type\":\"text\",\"text\":\" (8 total pops) → \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"reps: 4\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"countdown: 2\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"3 rounds of 3 balloons\"},{\"type\":\"text\",\"text\":\" (9 total pops) → \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"reps: 3\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"countdown: 3\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"6 rounds of 1 balloon\"},{\"type\":\"text\",\"text\":\" (6 total pops, very easy for small kids) → \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"reps: 6\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"countdown: 1\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Default timer mode\"},{\"type\":\"text\",\"text\":\" (30s, escalating) → \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"reps: null\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"countdown: any\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Balloon spawn boundaries\"},{\"type\":\"text\",\"text\":\": Balloons now respect the player's foot position as a lower boundary, preventing balloons from appearing below the player's feet. This also clamps balloon movement and spawn zones to the visible camera area, improving the experience in cover/fullscreen layouts.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: <code>Games</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Balloon Pop Rounds Mode</strong>: The Balloon Pop game now supports a rounds-based configuration. When <code>reps</code> and <code>countdown</code> are provided in customParams, the game switches from a timed mode to a rounds mode where each round spawns a fixed number of balloons. The game ends after all rounds are completed. The in-game HUD displays the current round counter instead of a countdown timer, and the difficulty stage indicator is hidden.</p></li></ul><h3><span>Quick Examples</span></h3><ul><li><p><strong>4 rounds of 2 balloons</strong> (8 total pops) → <code>reps: 4</code>, <code>countdown: 2</code></p></li><li><p><strong>3 rounds of 3 balloons</strong> (9 total pops) → <code>reps: 3</code>, <code>countdown: 3</code></p></li><li><p><strong>6 rounds of 1 balloon</strong> (6 total pops, very easy for small kids) → <code>reps: 6</code>, <code>countdown: 1</code></p></li><li><p><strong>Default timer mode</strong> (30s, escalating) → <code>reps: null</code>, <code>countdown: any</code></p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Balloon spawn boundaries</strong>: Balloons now respect the player's foot position as a lower boundary, preventing balloons from appearing below the player's feet. This also clamps balloon movement and spawn zones to the visible camera area, improving the experience in cover/fullscreen layouts.</p></li></ul>","coverImage":null,"product":"client","version":"2.20.10","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-04-01T10:06:27.617Z","category":"improved","publishedAt":"2026-04-01T10:06:50.115Z","status":"published","updatedAt":"2026-04-01T10:06:50.115Z"},{"id":"gBPHD5iUJiRi2exrGSBB","title":"New Hold Position Data in Exercise Events, Fixed Audio Overlap in Instruction Mode","slug":"new-hold-position-data-in-exercise-events-fixed-audio-overlap-in-instruction-mod","summary":"This release adds a new perfect_hold_position field to the exercise_completed PostMessage event, giving integrators access to how long a user held the target position during hold-based exercises. It also fixes an audio overlap issue where reset and instruction mode voice prompts could play simultaneously. No breaking changes — integrators do not need to update existing code, but can optionally consume the new field.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Perfect hold position tracking in exercise events\"},{\"type\":\"text\",\"text\":\": The \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercise_completed\"},{\"type\":\"text\",\"text\":\" PostMessage event now includes a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"perfect_hold_position\"},{\"type\":\"text\",\"text\":\" field (in seconds), reporting how long the user maintained the ideal hold position during an exercise. Defaults to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"0\"},{\"type\":\"text\",\"text\":\" for non-hold exercises.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Fixed audio overlap during instruction mode reset\"},{\"type\":\"text\",\"text\":\": When an exercise resets into instruction mode, the reset audio and the instruction mode audio no longer play on top of each other. The instruction prompt is correctly skipped when a reset speech is already playing.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — this is an additive change. Existing integrations are unaffected.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"perfect_hold_position\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" field on \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercise_completed\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"A new numeric field \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"perfect_hold_position\"},{\"type\":\"text\",\"text\":\" has been added to the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercise_completed\"},{\"type\":\"text\",\"text\":\" event payload. It represents the time (in seconds) the user spent in the perfect hold position during the exercise.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"exercise_completed\\\",\\n  \\\"data\\\": {\\n    \\\"exercise_title\\\": \\\"Plank\\\",\\n    \\\"time_spent\\\": 45,\\n    \\\"repeats\\\": 1,\\n    \\\"total_reps\\\": 1,\\n    \\\"total_duration\\\": 60,\\n    \\\"calories\\\": 5.2,\\n    \\\"exercise_id\\\": \\\"plank_01\\\",\\n    \\\"exercise\\\": \\\"plank_01\\\",\\n    \\\"exercise_index\\\": 1,\\n    \\\"total_exercises\\\": 5,\\n    \\\"mistakes\\\": []\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"exercise_completed\\\",\\n  \\\"data\\\": {\\n    \\\"exercise_title\\\": \\\"Plank\\\",\\n    \\\"time_spent\\\": 45,\\n    \\\"repeats\\\": 1,\\n    \\\"total_reps\\\": 1,\\n    \\\"total_duration\\\": 60,\\n    \\\"perfect_hold_position\\\": 32,\\n    \\\"calories\\\": 5.2,\\n    \\\"exercise_id\\\": \\\"plank_01\\\",\\n    \\\"exercise\\\": \\\"plank_01\\\",\\n    \\\"exercise_index\\\": 1,\\n    \\\"total_exercises\\\": 5,\\n    \\\"mistakes\\\": []\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": No action required. The field is additive and defaults to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"0\"},{\"type\":\"text\",\"text\":\" for exercises without hold tracking. Integrators can optionally read \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"perfect_hold_position\"},{\"type\":\"text\",\"text\":\" to display hold duration stats or use it for scoring.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: <code>Workout</code> · <code>Challenge</code> · <code>Plan</code> · <code>Personalized Plan</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Perfect hold position tracking in exercise events</strong>: The <code>exercise_completed</code> PostMessage event now includes a <code>perfect_hold_position</code> field (in seconds), reporting how long the user maintained the ideal hold position during an exercise. Defaults to <code>0</code> for non-hold exercises.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Fixed audio overlap during instruction mode reset</strong>: When an exercise resets into instruction mode, the reset audio and the instruction mode audio no longer play on top of each other. The instruction prompt is correctly skipped when a reset speech is already playing.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — this is an additive change. Existing integrations are unaffected.</p></blockquote><h3><strong>New </strong><code>perfect_hold_position</code><strong> field on </strong><code>exercise_completed</code></h3><p>A new numeric field <code>perfect_hold_position</code> has been added to the <code>exercise_completed</code> event payload. It represents the time (in seconds) the user spent in the perfect hold position during the exercise.</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"exercise_completed\",\n  \"data\": {\n    \"exercise_title\": \"Plank\",\n    \"time_spent\": 45,\n    \"repeats\": 1,\n    \"total_reps\": 1,\n    \"total_duration\": 60,\n    \"calories\": 5.2,\n    \"exercise_id\": \"plank_01\",\n    \"exercise\": \"plank_01\",\n    \"exercise_index\": 1,\n    \"total_exercises\": 5,\n    \"mistakes\": []\n  }\n}</code></pre><p><strong>After:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"exercise_completed\",\n  \"data\": {\n    \"exercise_title\": \"Plank\",\n    \"time_spent\": 45,\n    \"repeats\": 1,\n    \"total_reps\": 1,\n    \"total_duration\": 60,\n    \"perfect_hold_position\": 32,\n    \"calories\": 5.2,\n    \"exercise_id\": \"plank_01\",\n    \"exercise\": \"plank_01\",\n    \"exercise_index\": 1,\n    \"total_exercises\": 5,\n    \"mistakes\": []\n  }\n}</code></pre><p><strong>Migration</strong>: No action required. The field is additive and defaults to <code>0</code> for exercises without hold tracking. Integrators can optionally read <code>perfect_hold_position</code> to display hold duration stats or use it for scoring.</p>","coverImage":null,"category":"improved","product":"client","version":"2.20.9","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-31T20:14:18.170Z","status":"published","publishedAt":"2026-03-31T20:14:33.015Z","updatedAt":"2026-03-31T20:14:33.015Z"},{"id":"idbIqBYSKgAVXIRZcSLW","title":"Exercise model type badges now visible on exercise and workout pages","slug":"exercise-model-type-badges-now-visible-on-exercise-and-workout-pages","summary":"Exercises and workout exercises now display colored badges indicating their model type properties — Timer/Reps, Hold, Break, and Reset. This helps content managers quickly identify how each exercise behaves without opening individual exercise details. No action required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise model type badges\"},{\"type\":\"text\",\"text\":\": Each exercise now shows color-coded badges for its model type — \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Timer\"},{\"type\":\"text\",\"text\":\" or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Reps\"},{\"type\":\"text\",\"text\":\" (always shown), plus \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Hold\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Break\"},{\"type\":\"text\",\"text\":\", and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Reset\"},{\"type\":\"text\",\"text\":\" when applicable. Badges appear on exercise cards in the exercise list and on exercise items within the workout editor.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout exercise list\"},{\"type\":\"text\",\"text\":\": Exercise entries in the workout editor now show model type badges beneath the exercise name, making it easier to review the structure of a workout at a glance without navigating to each exercise individually.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted areas</strong>: <code>Exercises</code> · <code>Workouts</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Exercise model type badges</strong>: Each exercise now shows color-coded badges for its model type — <strong>Timer</strong> or <strong>Reps</strong> (always shown), plus <strong>Hold</strong>, <strong>Break</strong>, and <strong>Reset</strong> when applicable. Badges appear on exercise cards in the exercise list and on exercise items within the workout editor.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Workout exercise list</strong>: Exercise entries in the workout editor now show model type badges beneath the exercise name, making it easier to review the structure of a workout at a glance without navigating to each exercise individually.</p></li></ul>","coverImage":null,"category":"improved","product":"dashboard","version":"2.20.9","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-30T14:57:40.700Z","publishedAt":"2026-03-30T14:57:50.507Z","status":"published","updatedAt":"2026-03-30T14:57:50.507Z"},{"id":"I4sWrlmeleVl5Qsywj4W","title":"Improved FRT Assessment Accuracy, Outro Video Behavior, AI Tracking Locked for Challenges","slug":"improved-frt-assessment-accuracy-outro-video-behavior-ai-tracking-locked-for-cha","summary":"This release improves the Functional Reach Test (FRT) assessment by reducing false arm-drop terminations, fixes the outro exercise screen to play its video once before advancing, and prevents users from disabling AI tracking mid-session during challenges or experiences.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"hardBreak\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"FRT assessment arm-drop detection\"},{\"type\":\"text\",\"text\":\": The test now requires 15 consecutive frames below the arm threshold before ending, instead of terminating on a single frame. The detection angle threshold was also adjusted for better accuracy when the camera is at desk height. This reduces false early terminations caused by brief detection noise or forward-leaning posture.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"FRT \\\"perfect form\\\" status text\"},{\"type\":\"text\",\"text\":\": Updated from \\\"Perfect form\\\" to \\\"Good form — no serious violations detected\\\" to better reflect assessment results (all supported languages).\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Outro video now plays fully before advancing\"},{\"type\":\"text\",\"text\":\": Previously, the outro rest screen auto-advanced on a timer like regular rest periods. Now the outro video plays once to completion, then automatically moves to the next step. The countdown timer is also hidden during the outro.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"AI tracking cannot be disabled during challenges or experiences\"},{\"type\":\"text\",\"text\":\": Users can no longer turn off AI motion tracking mid-session in a challenge or experience. The \\\"Continue without AI\\\" option is hidden in camera permission prompts, and the settings toggle shows an error message if toggled during these sessions. This prevents incomplete tracking data during scored sessions.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Challenge</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Assessments</code><br></p><h2><strong>Improvements</strong></h2><ul><li><p><strong>FRT assessment arm-drop detection</strong>: The test now requires 15 consecutive frames below the arm threshold before ending, instead of terminating on a single frame. The detection angle threshold was also adjusted for better accuracy when the camera is at desk height. This reduces false early terminations caused by brief detection noise or forward-leaning posture.</p></li><li><p><strong>FRT \"perfect form\" status text</strong>: Updated from \"Perfect form\" to \"Good form — no serious violations detected\" to better reflect assessment results (all supported languages).</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Outro video now plays fully before advancing</strong>: Previously, the outro rest screen auto-advanced on a timer like regular rest periods. Now the outro video plays once to completion, then automatically moves to the next step. The countdown timer is also hidden during the outro.</p></li><li><p><strong>AI tracking cannot be disabled during challenges or experiences</strong>: Users can no longer turn off AI motion tracking mid-session in a challenge or experience. The \"Continue without AI\" option is hidden in camera permission prompts, and the settings toggle shows an error message if toggled during these sessions. This prevents incomplete tracking data during scored sessions.</p></li></ul>","coverImage":null,"product":"client","version":"2.20.8","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-27T15:48:31.052Z","category":"improved","publishedAt":"2026-03-27T15:48:44.444Z","status":"published","updatedAt":"2026-03-27T15:48:44.444Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-27T15:48:56.589Z","subscriberCountAtSend":19},{"id":"wNJQBUbzicKTsVJq8em0","title":"Timer-based exercises are now handled automatically in workout sequences","slug":"timer-based-exercises-are-now-handled-automatically-in-workout-sequences","summary":"When adding or replacing a timer-based exercise in a workout sequence, the Reps field is now automatically disabled and the exercise defaults to countdown mode. This prevents configuration errors for exercises that only support timed durations. Affects anyone building or editing workouts in the admin dashboard.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Auto-detect timer-based exercises in workouts\"},{\"type\":\"text\",\"text\":\": When you add a timer-based exercise to a workout sequence, the Reps field is now automatically disabled and grayed out with a tooltip explaining why. The exercise will default to a 30-second countdown if no duration is set, ensuring timer-based exercises are always configured correctly.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Clearer exercise configuration in workouts\"},{\"type\":\"text\",\"text\":\": Timer-based exercises now show a \\\"Reps (disabled)\\\" label with a helpful tooltip (\\\"Timer-based exercises use countdown only\\\"), making it immediately obvious why the Reps field can't be edited for certain exercises.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Tooltip display fix\"},{\"type\":\"text\",\"text\":\": Resolved an issue where tooltips were not displaying correctly.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Timer-based exercise validation\"},{\"type\":\"text\",\"text\":\": Previously, you could accidentally set reps on a timer-based exercise, which would cause errors. The system now validates this and shows a clear message: \\\"Exercise X is timer-based and must use countdown, not reps.\\\"\"}]}]}]}]}","contentHtml":"<p><strong>Impacted areas</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Exercises</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Workouts</code></p><h2><strong>New Features</strong></h2><ul><li><p><strong>Auto-detect timer-based exercises in workouts</strong>: When you add a timer-based exercise to a workout sequence, the Reps field is now automatically disabled and grayed out with a tooltip explaining why. The exercise will default to a 30-second countdown if no duration is set, ensuring timer-based exercises are always configured correctly.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Clearer exercise configuration in workouts</strong>: Timer-based exercises now show a \"Reps (disabled)\" label with a helpful tooltip (\"Timer-based exercises use countdown only\"), making it immediately obvious why the Reps field can't be edited for certain exercises.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Tooltip display fix</strong>: Resolved an issue where tooltips were not displaying correctly.</p></li><li><p><strong>Timer-based exercise validation</strong>: Previously, you could accidentally set reps on a timer-based exercise, which would cause errors. The system now validates this and shows a clear message: \"Exercise X is timer-based and must use countdown, not reps.\"</p></li></ul>","coverImage":null,"category":"fixed","product":"dashboard","version":"2.20.8","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-27T10:47:42.212Z","publishedAt":"2026-03-27T10:47:49.330Z","status":"published","updatedAt":"2026-03-27T10:47:49.330Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-27T10:48:04.202Z","subscriberCountAtSend":19},{"id":"UciWFdmFbC1nOTO6n0WD","title":"Native Scroll Support, Improved FRT Assessment Accuracy, New Session Save Event","slug":"native-scroll-support-improved-frt-assessment-accuracy-new-session-save-event","summary":"This release improves FRT (Functional Reach Test) assessment accuracy by switching to 3D angle calculations, adds a new nativeParentScroll configuration option for native apps that manage their own scrolling, and introduces a new session_save_complete PostMessage event. Integrators who track session upload status will want to listen for the new event. No breaking changes.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Native Parent Scroll support\"},{\"type\":\"text\",\"text\":\": New \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"nativeParentScroll\"},{\"type\":\"text\",\"text\":\" configuration option allows native host apps to manage scrolling of the statistics screen natively, instead of relying on the SDK's internal scroll container. Pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"nativeParentScroll: true\"},{\"type\":\"text\",\"text\":\" in your configuration to enable this behavior.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Session save completion event\"},{\"type\":\"text\",\"text\":\": A new \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"session_save_complete\"},{\"type\":\"text\",\"text\":\" PostMessage event is fired when motion recording uploads finish successfully, letting host apps react to upload completion (e.g., dismiss loading indicators or navigate away).\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"FRT assessment arm detection\"},{\"type\":\"text\",\"text\":\": Arm-drop detection during the Functional Reach Test now uses 3D angle calculations instead of 2D. This prevents false \\\"arm dropped\\\" terminations when users reach forward (depth movement was previously misread as the arm lowering in the 2D projection).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"FRT heel lift tolerance\"},{\"type\":\"text\",\"text\":\": The heel lift threshold during Functional Reach Test is now more forgiving, accommodating natural weight shifts during forward reaching without prematurely flagging a violation.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Motivational phrases refreshed\"},{\"type\":\"text\",\"text\":\": Updated and expanded the in-workout motivational audio phrases with friendlier, more encouraging language across all workout sections.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — all changes are additive\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"session_save_complete\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Fired when motion recording session data has been successfully uploaded after a workout.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Payload:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"session_save_complete\\\" }\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": No action required. Listen for this event if you want to know when session recording uploads have finished.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New configuration option: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"nativeParentScroll\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"A new boolean option that can be passed in the SDK configuration. When enabled, the statistics screen delegates scroll behavior to the native parent container instead of using its own internal scroll.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Usage:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"nativeParentScroll\\\": true }\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": No action required. Only enable this if your native app needs to control the scroll container for the statistics view.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: <code>Workout</code> · <code>Personalized Plan</code> · <code>Assessments</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Native Parent Scroll support</strong>: New <code>nativeParentScroll</code> configuration option allows native host apps to manage scrolling of the statistics screen natively, instead of relying on the SDK's internal scroll container. Pass <code>nativeParentScroll: true</code> in your configuration to enable this behavior.</p></li><li><p><strong>Session save completion event</strong>: A new <code>session_save_complete</code> PostMessage event is fired when motion recording uploads finish successfully, letting host apps react to upload completion (e.g., dismiss loading indicators or navigate away).</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>FRT assessment arm detection</strong>: Arm-drop detection during the Functional Reach Test now uses 3D angle calculations instead of 2D. This prevents false \"arm dropped\" terminations when users reach forward (depth movement was previously misread as the arm lowering in the 2D projection).</p></li><li><p><strong>FRT heel lift tolerance</strong>: The heel lift threshold during Functional Reach Test is now more forgiving, accommodating natural weight shifts during forward reaching without prematurely flagging a violation.</p></li><li><p><strong>Motivational phrases refreshed</strong>: Updated and expanded the in-workout motivational audio phrases with friendlier, more encouraging language across all workout sections.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — all changes are additive</p></blockquote><h3><strong>New event: </strong><code>session_save_complete</code></h3><p>Fired when motion recording session data has been successfully uploaded after a workout.</p><p><strong>Payload:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{ \"type\": \"session_save_complete\" }</code></pre><p><strong>Migration</strong>: No action required. Listen for this event if you want to know when session recording uploads have finished.</p><hr><h3><strong>New configuration option: </strong><code>nativeParentScroll</code></h3><p>A new boolean option that can be passed in the SDK configuration. When enabled, the statistics screen delegates scroll behavior to the native parent container instead of using its own internal scroll.</p><p><strong>Usage:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{ \"nativeParentScroll\": true }</code></pre><p><strong>Migration</strong>: No action required. Only enable this if your native app needs to control the scroll container for the statistics view.</p>","coverImage":null,"product":"client","version":"2.20.7","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-24T20:10:22.879Z","category":"improved","publishedAt":"2026-03-24T20:15:39.368Z","status":"published","updatedAt":"2026-03-24T20:15:39.368Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-24T20:15:52.113Z","subscriberCountAtSend":19},{"id":"5MNd04w0QifNgPE8ygxL","title":"Gemini AI translation option added across all content types","slug":"gemini-ai-translation-option-added-across-all-content-types","summary":"You can now choose between Google Translate and Gemini AI when auto-translating exercises, workouts, plans, equipment, and AI models. Gemini AI produces more natural, gender-inclusive fitness translations optimized for voice playback. Additionally, translation progress is now shown step-by-step, exercise sequence saving is more reliable, and content lists load more efficiently. Affects all dashboard users who manage multilingual content.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"heading\",\"attrs\":{\"level\":1},\"content\":[{\"type\":\"hardBreak\",\"marks\":[{\"type\":\"bold\"}]},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Gemini AI translation option added across all content types\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Summary\"},{\"type\":\"text\",\"text\":\": You can now choose between Google Translate and Gemini AI when auto-translating exercises, workouts, plans, equipment, and AI models. Gemini AI produces more natural, gender-inclusive fitness translations optimized for voice playback. Additionally, translation progress is now shown step-by-step, exercise sequence saving is more reliable, and content lists load more efficiently. Affects all dashboard users who manage multilingual content.\"}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plans\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Gemini AI translation\"},{\"type\":\"text\",\"text\":\": When clicking \\\"Auto Translate\\\" on any exercise, workout, plan, equipment, or AI model page, you can now choose between \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Google Translate\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Gemini AI\"},{\"type\":\"text\",\"text\":\" from a dropdown next to the button. Gemini AI is specifically tuned for fitness content — it produces more natural-sounding translations that are gender-inclusive and optimized for being spoken aloud during workouts. Your chosen translation method is remembered for next time.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Translation progress indicators\"},{\"type\":\"text\",\"text\":\": Auto-translating an exercise now shows detailed step-by-step progress (e.g., \\\"Step 1/4: Translating exercise fields…\\\", \\\"Generating audio 3–4 of 8…\\\") instead of a generic loading spinner. The same applies to workouts and AI models.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout rest speech translation\"},{\"type\":\"text\",\"text\":\": When auto-translating a workout, rest period speech text is now automatically translated and audio is generated for each rest segment — previously this had to be done manually.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise list loading\"},{\"type\":\"text\",\"text\":\": The exercise list page now waits for saved filters to load before fetching data and debounces your search input, preventing unnecessary duplicate requests and delivering results faster.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Content freshness after saving\"},{\"type\":\"text\",\"text\":\": After saving an exercise, workout, or plan, the cached data is automatically refreshed so you immediately see your latest changes without needing to manually reload the page.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise sequence ordering\"},{\"type\":\"text\",\"text\":\": Fixed an issue where exercise sequences could end up with duplicate or missing order numbers, which caused sequences to save incorrectly or appear out of order.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Sequence save using wrong order value\"},{\"type\":\"text\",\"text\":\": Fixed a bug where the saved sequence order did not match the displayed order, potentially causing sequences to shift positions after saving.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout translation success message\"},{\"type\":\"text\",\"text\":\": The success message after translating a workout now correctly says \\\"Workout translated successfully\\\" instead of \\\"Exercise translated successfully.\\\"\"}]}]}]}]}","contentHtml":"<h1><strong><br>Gemini AI translation option added across all content types</strong></h1><blockquote><p><strong>Summary</strong>: You can now choose between Google Translate and Gemini AI when auto-translating exercises, workouts, plans, equipment, and AI models. Gemini AI produces more natural, gender-inclusive fitness translations optimized for voice playback. Additionally, translation progress is now shown step-by-step, exercise sequence saving is more reliable, and content lists load more efficiently. Affects all dashboard users who manage multilingual content.</p></blockquote><p><strong>Impacted areas</strong>: <code>Exercises</code> · <code>Workouts</code> · <code>Plans</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Gemini AI translation</strong>: When clicking \"Auto Translate\" on any exercise, workout, plan, equipment, or AI model page, you can now choose between <strong>Google Translate</strong> and <strong>Gemini AI</strong> from a dropdown next to the button. Gemini AI is specifically tuned for fitness content — it produces more natural-sounding translations that are gender-inclusive and optimized for being spoken aloud during workouts. Your chosen translation method is remembered for next time.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Translation progress indicators</strong>: Auto-translating an exercise now shows detailed step-by-step progress (e.g., \"Step 1/4: Translating exercise fields…\", \"Generating audio 3–4 of 8…\") instead of a generic loading spinner. The same applies to workouts and AI models.</p></li><li><p><strong>Workout rest speech translation</strong>: When auto-translating a workout, rest period speech text is now automatically translated and audio is generated for each rest segment — previously this had to be done manually.</p></li><li><p><strong>Exercise list loading</strong>: The exercise list page now waits for saved filters to load before fetching data and debounces your search input, preventing unnecessary duplicate requests and delivering results faster.</p></li><li><p><strong>Content freshness after saving</strong>: After saving an exercise, workout, or plan, the cached data is automatically refreshed so you immediately see your latest changes without needing to manually reload the page.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Exercise sequence ordering</strong>: Fixed an issue where exercise sequences could end up with duplicate or missing order numbers, which caused sequences to save incorrectly or appear out of order.</p></li><li><p><strong>Sequence save using wrong order value</strong>: Fixed a bug where the saved sequence order did not match the displayed order, potentially causing sequences to shift positions after saving.</p></li><li><p><strong>Workout translation success message</strong>: The success message after translating a workout now correctly says \"Workout translated successfully\" instead of \"Exercise translated successfully.\"</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"2.20.7","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-24T19:39:54.335Z","publishedAt":"2026-03-24T19:40:05.811Z","status":"published","updatedAt":"2026-03-24T19:40:05.811Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-24T19:40:15.982Z","subscriberCountAtSend":19},{"id":"q7WyNdCsGwUa5moZGa5P","title":"Landscape Mode for TUG & Gait Speed, Improved FRT Completion Tracking, Orange Skeleton Tips","slug":"landscape-mode-for-tug-gait-speed-improved-frt-completion-tracking-orange-skelet","summary":"This release adds landscape orientation support for TUG and Gait Speed assessments on mobile devices, improves device detection reliability, and introduces a distinct orange skeleton color for exercise tips. Integrators do not need to change any code — no PostMessage events were modified.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Landscape mode for TUG & Gait Speed assessments\"},{\"type\":\"text\",\"text\":\": Mobile devices in landscape orientation now use dedicated zone thresholds and relaxed height-coverage limits, enabling these assessments to run correctly when the phone is rotated sideways.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Orange skeleton color for tips\"},{\"type\":\"text\",\"text\":\": A new \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"TIP\"},{\"type\":\"text\",\"text\":\" feedback state renders the skeleton overlay in orange (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"#FF7600\"},{\"type\":\"text\",\"text\":\") when showing form tips, visually distinguishing tips from errors (which remain red).\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Reduced required walking distance on portrait mobile\"},{\"type\":\"text\",\"text\":\": Optimal height-coverage target raised from 65% to 85% on mobile portrait, effectively reducing the physical space users need from their device from ~4 m to ~2.6 m for TUG and Gait Speed assessments.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Reliable mobile device detection\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"isMobileDevice()\"},{\"type\":\"text\",\"text\":\" now checks the User-Agent string and screen dimensions instead of comparing viewport width/height, preventing false positives on resized desktop windows and false negatives on landscape phones.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"FRT assessment completion tracking\"},{\"type\":\"text\",\"text\":\": Test-completion status (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"testCompleted\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"endReason\"},{\"type\":\"text\",\"text\":\") is now tracked via refs and funnelled through a single \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"FINISHED\"},{\"type\":\"text\",\"text\":\" state transition, eliminating duplicated navigation logic for arm-drop and position-loss scenarios.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"FeedbackFeelingModal overflow on small screens\"},{\"type\":\"text\",\"text\":\": Changed emoji row alignment so feedback labels no longer clip or overflow on narrow viewports.\"}]},{\"type\":\"paragraph\"}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: <code>Assessments</code> · <code>Camera</code> · <code>Workout</code></p><hr><h2><strong>New Features</strong></h2><ul><li><p><strong>Landscape mode for TUG &amp; Gait Speed assessments</strong>: Mobile devices in landscape orientation now use dedicated zone thresholds and relaxed height-coverage limits, enabling these assessments to run correctly when the phone is rotated sideways.</p></li><li><p><strong>Orange skeleton color for tips</strong>: A new <code>TIP</code> feedback state renders the skeleton overlay in orange (<code>#FF7600</code>) when showing form tips, visually distinguishing tips from errors (which remain red).</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Reduced required walking distance on portrait mobile</strong>: Optimal height-coverage target raised from 65% to 85% on mobile portrait, effectively reducing the physical space users need from their device from ~4 m to ~2.6 m for TUG and Gait Speed assessments.</p></li><li><p><strong>Reliable mobile device detection</strong>: <code>isMobileDevice()</code> now checks the User-Agent string and screen dimensions instead of comparing viewport width/height, preventing false positives on resized desktop windows and false negatives on landscape phones.</p></li><li><p><strong>FRT assessment completion tracking</strong>: Test-completion status (<code>testCompleted</code>, <code>endReason</code>) is now tracked via refs and funnelled through a single <code>FINISHED</code> state transition, eliminating duplicated navigation logic for arm-drop and position-loss scenarios.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>FeedbackFeelingModal overflow on small screens</strong>: Changed emoji row alignment so feedback labels no longer clip or overflow on narrow viewports.</p><p></p></li></ul>","coverImage":null,"category":"new","product":"client","version":"2.20.6","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-23T19:17:14.985Z","publishedAt":"2026-03-23T19:17:26.664Z","status":"published","updatedAt":"2026-03-23T19:17:26.664Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-23T19:17:38.886Z","subscriberCountAtSend":19},{"id":"HuNdb3x3grsPsDu9FxwW","title":"Smoother dashboard experience and internal service improvements","slug":"smoother-dashboard-experience-and-internal-service-improvements","summary":"This update improves the dashboard experience with smoother page and filter transitions, and resolves a potential issue where company data could briefly appear stale after logging in. The majority of changes are internal restructuring with no impact on how the admin dashboard looks or behaves","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plans\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Dashboard\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Authentication\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Dashboard transitions\"},{\"type\":\"text\",\"text\":\": When changing pages, sorting, or filtering on the dashboard, the previous data now stays visible until new results load — instead of briefly showing a loading state. This makes navigation feel faster and less jarring.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Dashboard loading\"},{\"type\":\"text\",\"text\":\": The dashboard now unblocks and becomes interactive sooner, even when some data is still loading in the background.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Company data on login\"},{\"type\":\"text\",\"text\":\": Fixed a timing issue where company information (such as access status or role) could briefly show outdated values right after signing in, before snapping to the correct state.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Login session timing\"},{\"type\":\"text\",\"text\":\": The session identifier is now saved earlier during sign-in, ensuring it's available even if a redirect happens immediately after authentication.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted areas</strong>: <code>Exercises</code> · <code>Workouts</code> · <code>Plans</code> · <code>Dashboard</code> · <code>Authentication</code></p><hr><h2><strong>Improvements</strong></h2><ul><li><p><strong>Dashboard transitions</strong>: When changing pages, sorting, or filtering on the dashboard, the previous data now stays visible until new results load — instead of briefly showing a loading state. This makes navigation feel faster and less jarring.</p></li><li><p><strong>Dashboard loading</strong>: The dashboard now unblocks and becomes interactive sooner, even when some data is still loading in the background.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Company data on login</strong>: Fixed a timing issue where company information (such as access status or role) could briefly show outdated values right after signing in, before snapping to the correct state.</p></li><li><p><strong>Login session timing</strong>: The session identifier is now saved earlier during sign-in, ensuring it's available even if a redirect happens immediately after authentication.</p></li></ul>","coverImage":null,"version":"2.20.6","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-22T13:54:01.379Z","product":"dashboard","category":"improved","publishedAt":"2026-03-22T14:01:22.253Z","status":"published","updatedAt":"2026-03-22T14:01:22.253Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-22T14:01:38.741Z","subscriberCountAtSend":19},{"id":"atBWwWIVU3910hRh9vYx","title":"Chinese Language Support, RTL Button Fixes, Updated Assessment Button Text","slug":"chinese-language-support-rtl-button-fixes-updated-assessment-button-text","summary":"This release adds full Chinese (Simplified) language support across the SDK, fixes button layout issues in RTL languages (Arabic, Hebrew), and corrects the assessment start button label. No PostMessage API changes — integrators do not need to update their code.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"All Integrations (Chinese language support)\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Chinese (Simplified) language support\"},{\"type\":\"text\",\"text\":\": The SDK now fully supports \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zh\"},{\"type\":\"text\",\"text\":\" as a locale. When the host app passes Chinese as the language, all UI strings — workout instructions, assessment prompts, challenge labels, onboarding flows — render in Chinese.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"RTL button layout fix\"},{\"type\":\"text\",\"text\":\": Assessment and challenge start buttons now explicitly set \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"dir=\\\"ltr\\\"\"},{\"type\":\"text\",\"text\":\", preventing broken button layouts when the SDK is embedded in RTL language contexts (Arabic, Hebrew).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment start button text\"},{\"type\":\"text\",\"text\":\": The button now displays the localized \\\"Start Assessment\\\" string instead of the raw exercise title, which could appear untranslated or mismatched for certain assessment types.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Challenge exercise detail modal spacing\"},{\"type\":\"text\",\"text\":\": Added top padding to the exercise title in the detail modal to prevent it from being clipped by the modal header.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: <code>Challenge</code> · <code>Assessments</code> · <em>All Integrations (Chinese language support)</em></p><h2><strong>New Features</strong></h2><ul><li><p><strong>Chinese (Simplified) language support</strong>: The SDK now fully supports <code>zh</code> as a locale. When the host app passes Chinese as the language, all UI strings — workout instructions, assessment prompts, challenge labels, onboarding flows — render in Chinese.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>RTL button layout fix</strong>: Assessment and challenge start buttons now explicitly set <code>dir=\"ltr\"</code>, preventing broken button layouts when the SDK is embedded in RTL language contexts (Arabic, Hebrew).</p></li><li><p><strong>Assessment start button text</strong>: The button now displays the localized \"Start Assessment\" string instead of the raw exercise title, which could appear untranslated or mismatched for certain assessment types.</p></li><li><p><strong>Challenge exercise detail modal spacing</strong>: Added top padding to the exercise title in the detail modal to prevent it from being clipped by the modal header.</p></li></ul>","coverImage":null,"category":"new","product":"client","version":"2.20.5","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-20T12:20:12.778Z","publishedAt":"2026-03-20T12:21:44.828Z","status":"published","updatedAt":"2026-03-20T12:21:44.828Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-20T12:21:58.358Z","subscriberCountAtSend":18},{"id":"YkjDVw57ftXNH7bTWOEc","title":"Chinese language support, plan category fixes, and improved form validation scrolling","slug":"chinese-language-support-plan-category-fixes-and-improved-form-validation-scroll","summary":"This update adds Chinese (zh) as a new content language across exercises, workouts, and plans. It also fixes how plan category levels are stored so they remain consistent across all languages, and resolves issues where the form would not reliably scroll to validation errors after saving.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Exercises\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workouts\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plans\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Chinese language support\"},{\"type\":\"text\",\"text\":\": Chinese is now available as a content language. You can create and manage exercise, workout, and plan translations in Chinese, and filter content by Chinese in the list views.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan category levels now consistent across languages\"},{\"type\":\"text\",\"text\":\": Previously, category levels (e.g., Cardio, Strength) on a plan could get out of sync when switching between languages. Category levels are now shared across all translations of a plan, so changing them in one language applies everywhere.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Validation scroll reliability\"},{\"type\":\"text\",\"text\":\": When you click Save with missing required fields, the page now reliably scrolls to and highlights the first error — even if it's on a different tab. Previously, the scroll could fail to reach the correct field when switching tabs, leaving you searching for the error manually.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan validation for rest days and deleted items\"},{\"type\":\"text\",\"text\":\": Rest days no longer incorrectly trigger \\\"title required\\\" or \\\"workout required\\\" errors. Deleted weeks and days are now properly skipped during validation, so you won't see false errors for content you've already removed.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted areas</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Exercises</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Workouts</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Plans</code></p><h2><strong>New Features</strong></h2><ul><li><p><strong>Chinese language support</strong>: Chinese is now available as a content language. You can create and manage exercise, workout, and plan translations in Chinese, and filter content by Chinese in the list views.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Plan category levels now consistent across languages</strong>: Previously, category levels (e.g., Cardio, Strength) on a plan could get out of sync when switching between languages. Category levels are now shared across all translations of a plan, so changing them in one language applies everywhere.</p></li><li><p><strong>Validation scroll reliability</strong>: When you click Save with missing required fields, the page now reliably scrolls to and highlights the first error — even if it's on a different tab. Previously, the scroll could fail to reach the correct field when switching tabs, leaving you searching for the error manually.</p></li><li><p><strong>Plan validation for rest days and deleted items</strong>: Rest days no longer incorrectly trigger \"title required\" or \"workout required\" errors. Deleted weeks and days are now properly skipped during validation, so you won't see false errors for content you've already removed.</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"1.0.7","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-19T19:37:48.990Z","publishedAt":"2026-03-19T19:38:14.601Z","status":"published","updatedAt":"2026-03-19T19:38:14.601Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-19T19:38:29.238Z","subscriberCountAtSend":18},{"id":"xLP5yOVbGTCY2RGVtPLu","title":"Theme Editor: New color inspector and refreshed screen previews","slug":"theme-editor-new-color-inspector-and-refreshed-screen-previews","summary":"The Theme Editor now lets you click any element in the screen previews to instantly see and jump to its color settings in the sidebar. All preview screens have been redesigned to match the latest app look, including a brand-new Model Loading screen. These changes affect anyone using the Design Customization page.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Theme Editor\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Design Customization\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Color Inspector\"},{\"type\":\"text\",\"text\":\": Click any element in the screen previews to highlight it with a green border. The sidebar automatically scrolls to the matching color settings, and the selected colors are displayed at the top-right of the preview area — making it much faster to find and tweak the right color.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Model Loading screen preview\"},{\"type\":\"text\",\"text\":\": A new screen preview has been added showing the model loading experience with tip slides, a progress bar, and loading indicators. It replaces the previous Audio Check preview.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Home screen preview\"},{\"type\":\"text\",\"text\":\": Completely redesigned to show the current app layout — now includes streak counter, fitness score with tooltip, weekly activity calendar, personalized plan card, and challenge/game cards.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Accelerometer screen preview\"},{\"type\":\"text\",\"text\":\": Redesigned to show the updated two-step flow (audio check + device position) with refreshed visuals and layout.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Camera Access screen preview\"},{\"type\":\"text\",\"text\":\": Updated to show the new design with an onboarding video, clearer permission text, and cookie consent notice. The preview now respects your current light/dark mode selection.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Statistics screen preview\"},{\"type\":\"text\",\"text\":\": Refreshed layout with updated chart visuals, time-based stat cards, and a workout history section.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan Detail screen preview\"},{\"type\":\"text\",\"text\":\": Redesigned with a header image, weekly progress tracker, workout cards with duration and calorie info, and a \\\"Start plan\\\" action.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise Detail screen preview\"},{\"type\":\"text\",\"text\":\": Cleaned up layout — \\\"Involved Muscles\\\" section repositioned, unnecessary duplicate content removed.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise screen preview\"},{\"type\":\"text\",\"text\":\": Improved progress bar and rep counter display during the exercise view.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Challenge screen preview\"},{\"type\":\"text\",\"text\":\": Added settings button, updated text labels, and minor layout refinements.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Sidebar scroll on inspect\"},{\"type\":\"text\",\"text\":\": When you click an element in the preview, the sidebar smoothly scrolls to the first matching color setting so you don't have to search manually.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Typography styles updated\"},{\"type\":\"text\",\"text\":\": Screen previews now use the latest typography scale for more consistent text rendering across all screens.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted areas</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Theme Editor</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Design Customization</code></p><h2><strong>New Features</strong></h2><ul><li><p><strong>Color Inspector</strong>: Click any element in the screen previews to highlight it with a green border. The sidebar automatically scrolls to the matching color settings, and the selected colors are displayed at the top-right of the preview area — making it much faster to find and tweak the right color.</p></li><li><p><strong>Model Loading screen preview</strong>: A new screen preview has been added showing the model loading experience with tip slides, a progress bar, and loading indicators. It replaces the previous Audio Check preview.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Home screen preview</strong>: Completely redesigned to show the current app layout — now includes streak counter, fitness score with tooltip, weekly activity calendar, personalized plan card, and challenge/game cards.</p></li><li><p><strong>Accelerometer screen preview</strong>: Redesigned to show the updated two-step flow (audio check + device position) with refreshed visuals and layout.</p></li><li><p><strong>Camera Access screen preview</strong>: Updated to show the new design with an onboarding video, clearer permission text, and cookie consent notice. The preview now respects your current light/dark mode selection.</p></li><li><p><strong>Statistics screen preview</strong>: Refreshed layout with updated chart visuals, time-based stat cards, and a workout history section.</p></li><li><p><strong>Plan Detail screen preview</strong>: Redesigned with a header image, weekly progress tracker, workout cards with duration and calorie info, and a \"Start plan\" action.</p></li><li><p><strong>Exercise Detail screen preview</strong>: Cleaned up layout — \"Involved Muscles\" section repositioned, unnecessary duplicate content removed.</p></li><li><p><strong>Exercise screen preview</strong>: Improved progress bar and rep counter display during the exercise view.</p></li><li><p><strong>Challenge screen preview</strong>: Added settings button, updated text labels, and minor layout refinements.</p></li><li><p><strong>Sidebar scroll on inspect</strong>: When you click an element in the preview, the sidebar smoothly scrolls to the first matching color setting so you don't have to search manually.</p></li><li><p><strong>Typography styles updated</strong>: Screen previews now use the latest typography scale for more consistent text rendering across all screens.</p></li></ul>","coverImage":null,"category":"improved","product":"dashboard","version":"1.0.6","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-19T19:34:55.790Z","publishedAt":"2026-03-19T19:35:01.363Z","status":"published","updatedAt":"2026-03-19T19:35:01.363Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-19T19:35:13.335Z","subscriberCountAtSend":18},{"id":"SsZCRkOHKWSpSmlthanP","title":"Full Tandem Stand Assessment: Fixed Elapsed Time Accuracy","slug":"full-tandem-stand-assessment-fixed-elapsed-time-accuracy","summary":"This release fixes a timing bug in the Full Tandem Stand balance assessment where elapsed time could be calculated incorrectly due to a stale closure. No integration code changes are required — the fix is internal to the assessment logic.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Full Tandem Stand elapsed time accuracy\"},{\"type\":\"text\",\"text\":\": Fixed an issue where the elapsed time recorded on assessment failure (stance break or timeout) could be inaccurate. The previous implementation relied on \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"getRemainingTime()\"},{\"type\":\"text\",\"text\":\" inside a callback, which could capture a stale value. Elapsed time is now computed directly from \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Date.now()\"},{\"type\":\"text\",\"text\":\" relative to the test start time, ensuring the reported duration is always correct.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: <code>Assessments</code></p><hr><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Full Tandem Stand elapsed time accuracy</strong>: Fixed an issue where the elapsed time recorded on assessment failure (stance break or timeout) could be inaccurate. The previous implementation relied on <code>getRemainingTime()</code> inside a callback, which could capture a stale value. Elapsed time is now computed directly from <code>Date.now()</code> relative to the test start time, ensuring the reported duration is always correct.</p></li></ul>","coverImage":null,"product":"client","version":"2.20.4","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-18T16:41:57.157Z","category":"fixed","publishedAt":"2026-03-18T16:42:07.851Z","status":"published","updatedAt":"2026-03-18T16:42:07.851Z","emailMode":"groups","notifiedGroupIds":["0IkpaHDJ3Sf13IxiIsCm"],"notificationSentAt":"2026-03-18T16:42:09.476Z","subscriberCountAtSend":2},{"id":"mIRcTZeGu8qe70OoKE36","title":"Improved sign-up flow with step navigation, unsaved changes protection, and onboarding tour cleanup","slug":"improved-sign-up-flow-with-step-navigation-unsaved-changes-protection-and-onboar","summary":"The sign-up experience has been redesigned with better step-by-step navigation, a \"Back\" button, and protection against accidentally losing your progress. The onboarding tour checklist can no longer be permanently dismissed — it stays visible until all steps are completed. These changes affect all new users signing up and all users who see the onboarding tour.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted areas\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Authentication\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Unsaved changes protection on sign-up\"},{\"type\":\"text\",\"text\":\": If you start filling out the sign-up form and try to leave the page or close the browser tab, you'll now see a confirmation dialog so you don't accidentally lose your progress.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Back button on sign-up\"},{\"type\":\"text\",\"text\":\": You can now go back to previous steps during sign-up instead of having to start over.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Clickable step indicators\"},{\"type\":\"text\",\"text\":\": The progress dots at the top of the sign-up form are now clickable — you can jump back to any previously completed step.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Step 4 now visible in progress indicator\"},{\"type\":\"text\",\"text\":\": The sign-up progress bar now shows all 4 steps (previously only showed 3), giving a clearer picture of where you are in the process.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Inline field validation on sign-up\"},{\"type\":\"text\",\"text\":\": Validation errors now appear directly below each field as you fill out the form, rather than only showing as pop-up notifications when you try to continue.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Better error feedback on sign-up submission\"},{\"type\":\"text\",\"text\":\": If account creation fails, you'll now see a clear error message and remain on the form so you can fix the issue and retry.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Onboarding tour always visible\"},{\"type\":\"text\",\"text\":\": The onboarding checklist can no longer be dismissed with the close button — it stays visible until you complete all steps, ensuring new users don't accidentally skip important setup tasks.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Verify email page navigation guard\"},{\"type\":\"text\",\"text\":\": The confirmation dialog when leaving the email verification page now works more reliably across all types of navigation (back button, links, page refresh).\"}]}]}]}]}","contentHtml":"<p><strong>Impacted areas</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Authentication</code></p><h2><strong>New Features</strong></h2><ul><li><p><strong>Unsaved changes protection on sign-up</strong>: If you start filling out the sign-up form and try to leave the page or close the browser tab, you'll now see a confirmation dialog so you don't accidentally lose your progress.</p></li><li><p><strong>Back button on sign-up</strong>: You can now go back to previous steps during sign-up instead of having to start over.</p></li><li><p><strong>Clickable step indicators</strong>: The progress dots at the top of the sign-up form are now clickable — you can jump back to any previously completed step.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Step 4 now visible in progress indicator</strong>: The sign-up progress bar now shows all 4 steps (previously only showed 3), giving a clearer picture of where you are in the process.</p></li><li><p><strong>Inline field validation on sign-up</strong>: Validation errors now appear directly below each field as you fill out the form, rather than only showing as pop-up notifications when you try to continue.</p></li><li><p><strong>Better error feedback on sign-up submission</strong>: If account creation fails, you'll now see a clear error message and remain on the form so you can fix the issue and retry.</p></li><li><p><strong>Onboarding tour always visible</strong>: The onboarding checklist can no longer be dismissed with the close button — it stays visible until you complete all steps, ensuring new users don't accidentally skip important setup tasks.</p></li><li><p><strong>Verify email page navigation guard</strong>: The confirmation dialog when leaving the email verification page now works more reliably across all types of navigation (back button, links, page refresh).</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"1.0.5","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-18T11:07:10.358Z","publishedAt":"2026-03-18T11:30:19.623Z","status":"published","updatedAt":"2026-03-18T11:30:19.623Z"},{"id":"37GjsFBCtXGx72SQn3l6","title":"Plan Onboarding Prefill","slug":"plan-onboarding-prefill","summary":"This release introduces a new planOnboardingPrefill configuration option that lets integrators pre-fill (or skip) onboarding survey questions. No breaking changes — all additions are backward-compatible.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations:\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan Onboarding Prefill:\"},{\"type\":\"text\",\"text\":\" Integrators can now pass a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"planOnboardingPrefill\"},{\"type\":\"text\",\"text\":\" object in the configuration to pre-fill onboarding survey answers (goal, healthIssues, injuries, duration, lifestyle). Answered screens are skipped automatically; if all answers are provided for a goal-based plan, the user goes straight to results.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Slide Animations on Plan Onboarding:\"},{\"type\":\"text\",\"text\":\" Onboarding screens now animate with directional slide transitions (left/right, RTL-aware) for a smoother navigation feel.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required:\"},{\"type\":\"text\",\"text\":\" No — all changes are additive\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"text\":\"New \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"planOnboardingPrefill\"},{\"type\":\"text\",\"text\":\" configuration option\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Integrators can pass prefill data in the initial configuration message to skip already-answered onboarding questions:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"planOnboardingPrefill\\\": {\\n    \\\"goal\\\": \\\"weight_loss\\\",\\n    \\\"healthIssues\\\": [\\\"back_pain\\\"],\\n    \\\"injuries\\\": [],\\n    \\\"duration\\\": 30,\\n    \\\"lifestyle\\\": \\\"sedentary\\\"\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"All fields are optional — only provided fields will be pre-filled. Omitted fields prompt the user as before.\"}]}]}","contentHtml":"<p><strong>Impacted integrations:</strong> <code>Personalized Plan</code> </p><h2>New Features</h2><ul><li><p><strong>Plan Onboarding Prefill:</strong> Integrators can now pass a <code>planOnboardingPrefill</code> object in the configuration to pre-fill onboarding survey answers (goal, healthIssues, injuries, duration, lifestyle). Answered screens are skipped automatically; if all answers are provided for a goal-based plan, the user goes straight to results.</p></li></ul><h2>Improvements</h2><ul><li><p><strong>Slide Animations on Plan Onboarding:</strong> Onboarding screens now animate with directional slide transitions (left/right, RTL-aware) for a smoother navigation feel.</p></li></ul><h2>API Changes (PostMessage)</h2><p><strong>Action Required:</strong> No — all changes are additive</p><h3>New <code>planOnboardingPrefill</code> configuration option</h3><p>Integrators can pass prefill data in the initial configuration message to skip already-answered onboarding questions:</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"planOnboardingPrefill\": {\n    \"goal\": \"weight_loss\",\n    \"healthIssues\": [\"back_pain\"],\n    \"injuries\": [],\n    \"duration\": 30,\n    \"lifestyle\": \"sedentary\"\n  }\n}</code></pre><p>All fields are optional — only provided fields will be pre-filled. Omitted fields prompt the user as before.</p>","coverImage":null,"category":"new","product":"client","version":"2.20.3","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-17T12:26:47.117Z","publishedAt":"2026-03-17T12:26:55.677Z","status":"published","updatedAt":"2026-03-17T12:26:55.677Z"},{"id":"sJF2Jpl2yBZ01jK77DkH","title":"Session Saving with Motion Recording Upload, Session Replay Page","slug":"session-saving-with-motion-recording-upload-session-replay-page","summary":"This release adds optional, automatic workout session saving to the backend (opt-in via shouldSendStats), including motion recording upload with progress tracking. A new /session/:sessionId route lets integrators navigate users to view historical workout statistics. Integrators who want session saving must pass shouldSendStats: true in the configuration; otherwise, no data will be saved.","coverImage":null,"category":"new","product":"client","version":"2.20.2","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-16T18:55:00.886Z","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Workout</code> <code>Challenge</code> <code>Plan</code> </p><h2><strong>New Features</strong></h2><ul><li><p><strong>Workout Session Saving</strong>: When <code>shouldSendStats: true</code> is passed in the SDK configuration, workout sessions are automatically saved to the backend upon completion — including per-exercise stats, accuracy scores, calories, and motion recording data. The \"Done\" button is temporarily disabled (shows a spinner with \"Saving...\") until the save and any motion uploads complete (15s timeout).</p></li><li><p><strong>Session Replay Page</strong>: New route <code>/session/:sessionId</code> fetches a previously saved workout session from the backend and renders the full statistics view, including motion recording playback. Integrators can navigate users to this route to review past sessions.</p></li><li><p><strong>Motion Recording Upload</strong>: After a session is saved, motion recordings (pose landmark data) are compressed with gzip, encoded in KMRF format, and uploaded to pre-signed URLs returned by the backend. Upload progress is tracked and communicated via PostMessage events.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Toast positioning</strong>: The global toaster now uses proper top offset, preventing toasts from being clipped on devices with no safe-area inset.</p></li><li><p><strong>Exercise list in statistics</strong>: Exercises with motion recordings are now shown in the expanded results even if they have zero reps and zero time spent.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: No — all new events are additive. Existing integrations are unaffected.</p></blockquote><h3><strong>New configuration option: </strong><code>shouldSendStats</code></h3><p>Pass <code>shouldSendStats: true</code> in your SDK configuration to enable automatic session saving after workout completion.</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"apiKey\": \"...\",\n  \"company\": \"...\",\n  \"userId\": \"...\"\n}</code></pre><p><strong>After:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"apiKey\": \"...\",\n  \"company\": \"...\",\n  \"userId\": \"...\",\n  \"shouldSendStats\": true\n}</code></pre><p><strong>Migration</strong>: Opt-in only. No action required if you don't need session saving.</p><hr><h3><strong>New event: </strong><code>workout_session_saved</code></h3><p>Emitted when a workout session is successfully saved to the backend. Contains the backend-assigned <code>session_id</code> and summary stats.</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"workout_session_saved\",\n  \"data\": {\n    \"session_id\": 12345,\n    \"workout_title\": \"Full Body Burn\",\n    \"accuracy_score\": 85.5,\n    \"efficiency_score\": 78.2,\n    \"completion_percentage\": 92.0,\n    \"completed_reps_count\": 47,\n    \"calories_burned\": 156.32\n  }\n}</code></pre><hr><h3><strong>New event: </strong><code>motion_upload_progress</code></h3><p>Emitted during motion recording upload to report progress. <code>completed</code> increments as each exercise's recording finishes uploading.</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"motion_upload_progress\",\n  \"data\": {\n    \"completed\": 2,\n    \"total\": 5\n  }\n}</code></pre><hr><h3><strong>New event: </strong><code>motion_upload_error</code></h3><p>Emitted if motion recording upload fails or times out (15s timeout).</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"motion_upload_error\",\n  \"data\": {\n    \"error\": \"Failed to upload motion recordings\"\n  }\n}</code></pre><p><strong>Migration</strong>: These are new additive events. Listen for them only if you need to track session save status or motion upload progress in your host app</p><h3><strong>New event: </strong><code>workout_completion_overlay_dismissed</code></h3><p>Emitted when the user dismisses the workout completion celebration overlay.</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"workout_completion_overlay_dismissed\"\n}</code></pre><p><strong>Migration</strong>: Optional — listen for this if you want to trigger host-app actions (e.g., show a review prompt) after the user closes the completion screen.</p>","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout Session Saving\"},{\"type\":\"text\",\"text\":\": When \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"shouldSendStats: true\"},{\"type\":\"text\",\"text\":\" is passed in the SDK configuration, workout sessions are automatically saved to the backend upon completion — including per-exercise stats, accuracy scores, calories, and motion recording data. The \\\"Done\\\" button is temporarily disabled (shows a spinner with \\\"Saving...\\\") until the save and any motion uploads complete (15s timeout).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Session Replay Page\"},{\"type\":\"text\",\"text\":\": New route \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"/session/:sessionId\"},{\"type\":\"text\",\"text\":\" fetches a previously saved workout session from the backend and renders the full statistics view, including motion recording playback. Integrators can navigate users to this route to review past sessions.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Motion Recording Upload\"},{\"type\":\"text\",\"text\":\": After a session is saved, motion recordings (pose landmark data) are compressed with gzip, encoded in KMRF format, and uploaded to pre-signed URLs returned by the backend. Upload progress is tracked and communicated via PostMessage events.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Toast positioning\"},{\"type\":\"text\",\"text\":\": The global toaster now uses proper top offset, preventing toasts from being clipped on devices with no safe-area inset.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Exercise list in statistics\"},{\"type\":\"text\",\"text\":\": Exercises with motion recordings are now shown in the expanded results even if they have zero reps and zero time spent.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": No — all new events are additive. Existing integrations are unaffected.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New configuration option: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"shouldSendStats\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"shouldSendStats: true\"},{\"type\":\"text\",\"text\":\" in your SDK configuration to enable automatic session saving after workout completion.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"apiKey\\\": \\\"...\\\",\\n  \\\"company\\\": \\\"...\\\",\\n  \\\"userId\\\": \\\"...\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"apiKey\\\": \\\"...\\\",\\n  \\\"company\\\": \\\"...\\\",\\n  \\\"userId\\\": \\\"...\\\",\\n  \\\"shouldSendStats\\\": true\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": Opt-in only. No action required if you don't need session saving.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_session_saved\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Emitted when a workout session is successfully saved to the backend. Contains the backend-assigned \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"session_id\"},{\"type\":\"text\",\"text\":\" and summary stats.\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"workout_session_saved\\\",\\n  \\\"data\\\": {\\n    \\\"session_id\\\": 12345,\\n    \\\"workout_title\\\": \\\"Full Body Burn\\\",\\n    \\\"accuracy_score\\\": 85.5,\\n    \\\"efficiency_score\\\": 78.2,\\n    \\\"completion_percentage\\\": 92.0,\\n    \\\"completed_reps_count\\\": 47,\\n    \\\"calories_burned\\\": 156.32\\n  }\\n}\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"motion_upload_progress\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Emitted during motion recording upload to report progress. \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"completed\"},{\"type\":\"text\",\"text\":\" increments as each exercise's recording finishes uploading.\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"motion_upload_progress\\\",\\n  \\\"data\\\": {\\n    \\\"completed\\\": 2,\\n    \\\"total\\\": 5\\n  }\\n}\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"motion_upload_error\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Emitted if motion recording upload fails or times out (15s timeout).\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"motion_upload_error\\\",\\n  \\\"data\\\": {\\n    \\\"error\\\": \\\"Failed to upload motion recordings\\\"\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": These are new additive events. Listen for them only if you need to track session save status or motion upload progress in your host app\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"workout_completion_overlay_dismissed\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Emitted when the user dismisses the workout completion celebration overlay.\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"workout_completion_overlay_dismissed\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": Optional — listen for this if you want to trigger host-app actions (e.g., show a review prompt) after the user closes the completion screen.\"}]}]}","publishedAt":"2026-03-16T21:09:13.457Z","status":"published","updatedAt":"2026-03-16T21:09:13.457Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-16T21:09:35.109Z","subscriberCountAtSend":18},{"id":"DYPPQZ1KUGMYWLLQF1Mj","title":"Onboarding tour can now be dismissed","slug":"onboarding-tour-can-now-be-dismissed","summary":"Summary: Admins can now close the onboarding checklist by clicking a dismiss (X) button. The choice is persisted per user so the popup won't reappear. Legacy users who signed up before the onboarding feature was introduced will have it auto-dismissed on first load.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Dismiss onboarding tour\"},{\"type\":\"text\",\"text\":\": A close button has been added to the onboarding popup, allowing admins to permanently hide the onboarding checklist if they don't need it.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Smarter step completion\"},{\"type\":\"text\",\"text\":\": Onboarding steps now auto-complete when you navigate to the relevant page, even without clicking the tooltip — making the flow feel more natural.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Legacy user handling\"},{\"type\":\"text\",\"text\":\": Users who existed before the onboarding feature was introduced are automatically opted out, so they won't see an unexpected checklist on their next login.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"More robust state persistence\"},{\"type\":\"text\",\"text\":\": Local storage writes now safely merge with existing data instead of overwriting, preventing potential loss of onboarding progress.\"}]}]}]}]}","contentHtml":"<h2><strong>New Features</strong></h2><ul><li><p><strong>Dismiss onboarding tour</strong>: A close button has been added to the onboarding popup, allowing admins to permanently hide the onboarding checklist if they don't need it.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Smarter step completion</strong>: Onboarding steps now auto-complete when you navigate to the relevant page, even without clicking the tooltip — making the flow feel more natural.</p></li><li><p><strong>Legacy user handling</strong>: Users who existed before the onboarding feature was introduced are automatically opted out, so they won't see an unexpected checklist on their next login.</p></li><li><p><strong>More robust state persistence</strong>: Local storage writes now safely merge with existing data instead of overwriting, preventing potential loss of onboarding progress.</p></li></ul>","coverImage":null,"category":"improved","product":"dashboard","version":"1.0.4","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-15T13:33:49.622Z","publishedAt":"2026-03-15T13:33:57.540Z","status":"published","updatedAt":"2026-03-15T13:33:57.540Z"},{"id":"SrktLFVlWzHynJJ9iWEC","title":"Tips/Mistake Feedback System, Assessment Zone Scaling, Tandem Stand Timing Fix","slug":"tipsmistake-feedback-system-assessment-zone-scaling-tandem-stand-timing-fix","summary":"This release introduces a new \"tip\" feedback type that gives users helpful guidance without penalizing their accuracy score, improves assessment start-zone sizing to scale based on required walking space, and fixes a timing display bug in the Full Tandem Stand assessment. No PostMessage API changes — integrators do not need to update their code.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Tip feedback type\"},{\"type\":\"text\",\"text\":\": Exercises can now include \\\"tip\\\" sequences that display guidance with an orange visual indicator (border + badge) instead of the red mistake indicator. Tips do not count as mistakes and do not affect the user's accuracy score.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Dynamic assessment start-zone width\"},{\"type\":\"text\",\"text\":\": The green start zone in TUG and Gait Speed Test assessments now scales based on the required walking space (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"tugRequiredSpace\"},{\"type\":\"text\",\"text\":\"). When less walking distance is needed, the start zone expands proportionally, making it easier for users to position themselves correctly. The check-frame silhouette also repositions to stay centered within the zone.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Configurable green zone width\"},{\"type\":\"text\",\"text\":\": Assessment components now accept a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"zoneWidthPercent\"},{\"type\":\"text\",\"text\":\" prop, allowing the check-frame overlay to match the actual start zone drawn during the assessment instead of using a hardcoded 20% width.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Full Tandem Stand timing display\"},{\"type\":\"text\",\"text\":\": Fixed an issue where \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"timeInProperPosition\"},{\"type\":\"text\",\"text\":\" showed ~9.9s instead of 10.0s on successful completion. A sub-frame gap between when the timer started and when the first pose frame was recorded caused the accumulated time to fall just short of the full duration.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Camera</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Assessments</code></p><h2><strong>New Features</strong></h2><ul><li><p><strong>Tip feedback type</strong>: Exercises can now include \"tip\" sequences that display guidance with an orange visual indicator (border + badge) instead of the red mistake indicator. Tips do not count as mistakes and do not affect the user's accuracy score.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Dynamic assessment start-zone width</strong>: The green start zone in TUG and Gait Speed Test assessments now scales based on the required walking space (<code>tugRequiredSpace</code>). When less walking distance is needed, the start zone expands proportionally, making it easier for users to position themselves correctly. The check-frame silhouette also repositions to stay centered within the zone.</p></li><li><p><strong>Configurable green zone width</strong>: Assessment components now accept a <code>zoneWidthPercent</code> prop, allowing the check-frame overlay to match the actual start zone drawn during the assessment instead of using a hardcoded 20% width.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Full Tandem Stand timing display</strong>: Fixed an issue where <code>timeInProperPosition</code> showed ~9.9s instead of 10.0s on successful completion. A sub-frame gap between when the timer started and when the first pose frame was recorded caused the accumulated time to fall just short of the full duration.</p></li></ul>","coverImage":null,"category":"improved","product":"client","version":"2.20.1","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-13T19:21:41.427Z","publishedAt":"2026-03-13T19:23:02.273Z","status":"published","updatedAt":"2026-03-13T19:23:02.273Z","emailMode":"groups","notifiedGroupIds":["0IkpaHDJ3Sf13IxiIsCm"],"notificationSentAt":"2026-03-13T19:23:04.142Z","subscriberCountAtSend":2},{"id":"fRbEgfQp2HiHnvCqzyCe","title":"Add guided onboarding tour and simplify dashboard & content filters","slug":"add-guided-onboarding-tour-and-simplify-dashboard-content-filters","summary":"This release introduces a step-by-step onboarding tour that guides new admin users through key platform areas (team management, design, API configuration, content creation, AI chat, and changelog). The dashboard has been redesigned to a single-page layout, and content filters across exercises, workouts, and plans have been simplified. These changes affect all companies using the admin dashboard. No action is required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Onboarding Tour\"},{\"type\":\"text\",\"text\":\": New users now see a floating onboarding checklist that tracks progress across six steps — invite team, customize design, connect API, create a workout, try AI chat, and view the changelog. Steps auto-complete when the user visits the corresponding page, and steps inaccessible to the user's role are automatically marked done.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Sidebar Tooltip Guidance\"},{\"type\":\"text\",\"text\":\": Clicking an onboarding step highlights the matching sidebar item with a green border and displays a tooltip explaining what the section does, helping new users navigate the dashboard.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Dashboard Simplified\"},{\"type\":\"text\",\"text\":\": The dashboard now loads as a single unified view with statistic cards (total users, active users, exercises done) and a user table with sorting and filtering — replacing the previous tabbed layout with separate \\\"General Statistics\\\" and \\\"User Activities\\\" tabs.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Content Filters Streamlined\"},{\"type\":\"text\",\"text\":\": Exercise, workout, and plan list filters have been simplified — advanced multi-select filters (difficulty, gender, exercise type, language, company multi-select, week count range, exercise count range) have been removed in favor of a cleaner single-select approach.\"}]}]}]}]}","contentHtml":"<h2><strong>New Features</strong></h2><ul><li><p><strong>Onboarding Tour</strong>: New users now see a floating onboarding checklist that tracks progress across six steps — invite team, customize design, connect API, create a workout, try AI chat, and view the changelog. Steps auto-complete when the user visits the corresponding page, and steps inaccessible to the user's role are automatically marked done.</p></li><li><p><strong>Sidebar Tooltip Guidance</strong>: Clicking an onboarding step highlights the matching sidebar item with a green border and displays a tooltip explaining what the section does, helping new users navigate the dashboard.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Dashboard Simplified</strong>: The dashboard now loads as a single unified view with statistic cards (total users, active users, exercises done) and a user table with sorting and filtering — replacing the previous tabbed layout with separate \"General Statistics\" and \"User Activities\" tabs.</p></li><li><p><strong>Content Filters Streamlined</strong>: Exercise, workout, and plan list filters have been simplified — advanced multi-select filters (difficulty, gender, exercise type, language, company multi-select, week count range, exercise count range) have been removed in favor of a cleaner single-select approach.</p></li></ul>","coverImage":null,"category":"new","product":"dashboard","version":"1.0.3","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-12T20:47:05.787Z","publishedAt":"2026-03-12T20:47:13.811Z","status":"published","updatedAt":"2026-03-12T20:47:13.811Z"},{"id":"EvNJMbYL4TtuFDVhaJ9i","title":"Redesigned Dashboard with new statistics views and simplified content filters","slug":"redesigned-dashboard-with-new-statistics-views-and-simplified-content-filters","summary":"The dashboard has been completely rebuilt with two dedicated tabs — General Statistics (engagement charts, device/OS breakdowns, calorie tracking, activity distribution) and User Core Activities & Details (per-user activity breakdown with collapsible details). Affects all admin users. No action required","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"General Statistics tab\"},{\"type\":\"text\",\"text\":\": New dashboard tab showing engagement line charts (total visitors, repeated visitors, DAU/MAU %), device distribution doughnut, OS distribution doughnut, total calorie burn, and activity starts/ends breakdown — all filterable by time period (last 7/14/30 days or custom date range).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"User Core Activities & Details tab\"},{\"type\":\"text\",\"text\":\": New dashboard tab replacing the old user table, now showing per-user activity with collapsible rows that reveal integration option breakdowns (workout, plan, challenge, assessment, etc.) and associated content IDs.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Company selector on dashboard\"},{\"type\":\"text\",\"text\":\": KinesteX team members can now select a specific child company to view its statistics in the General Statistics tab.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Persistent dashboard filters\"},{\"type\":\"text\",\"text\":\": Selected time period, custom date range, and hidden chart datasets are saved to localStorage and restored on return.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Sort by total opens\"},{\"type\":\"text\",\"text\":\": Users table now supports sorting by total opens in addition to exercises done and last open.\"}]}]}]}]}","contentHtml":"<h2><strong>New Features</strong></h2><ul><li><p><strong>General Statistics tab</strong>: New dashboard tab showing engagement line charts (total visitors, repeated visitors, DAU/MAU %), device distribution doughnut, OS distribution doughnut, total calorie burn, and activity starts/ends breakdown — all filterable by time period (last 7/14/30 days or custom date range).</p></li><li><p><strong>User Core Activities &amp; Details tab</strong>: New dashboard tab replacing the old user table, now showing per-user activity with collapsible rows that reveal integration option breakdowns (workout, plan, challenge, assessment, etc.) and associated content IDs.</p></li><li><p><strong>Company selector on dashboard</strong>: KinesteX team members can now select a specific child company to view its statistics in the General Statistics tab.</p></li><li><p><strong>Persistent dashboard filters</strong>: Selected time period, custom date range, and hidden chart datasets are saved to localStorage and restored on return.</p></li><li><p><strong>Sort by total opens</strong>: Users table now supports sorting by total opens in addition to exercises done and last open.</p></li></ul>","coverImage":null,"category":"improved","product":"dashboard","version":"1.0.2","exerciseId":null,"exerciseTitle":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-12T20:40:34.766Z","publishedAt":"2026-03-12T20:40:57.107Z","status":"published","updatedAt":"2026-03-12T20:40:57.107Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-12T20:41:07.400Z","subscriberCountAtSend":18},{"id":"ZDxuuSxA6NIaJIP5fX83","title":"Fullscreen mode - support for ignore safe area","slug":"fullscreen-mode---support-for-ignore-safe-area","coverImage":null,"category":"improved","product":"client","version":"2.20.0","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-10T12:10:23.430Z","summary":"This release adds comprehensive safe-area inset handling for mobile devices with notches/home indicators (iPhone, modern Android), fixes assessment zone drawing to respect camera cover-mode cropping, and corrects accuracy percentage calculations for non-AI and time-based exercises. No PostMessage API changes — integrators do not need to update their code.","exerciseId":null,"contentHtml":"<p>Impacted integrations: <code>Workout</code> •<code>Personalized Plan</code> •<code>Challenge</code> •<code>Assessments</code> • <code>Custom workout</code> </p><h2><strong>New Features</strong></h2><ul><li><p><strong>Mobile safe-area inset support</strong>: The SDK now respects <code>env(safe-area-inset-*)</code> on devices with notches and home indicators. Layout, buttons, and overlays automatically avoid system UI areas — no integrator configuration needed.</p></li></ul><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/11f695bf-ab8f-44e3-a93f-5573a3d54cf9.PNG\"><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/21d09ade-1510-498f-b931-589c71e35713.PNG\"><p>   </p><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/c9bcb6fd-6c04-4e08-a681-86220775f77d.PNG\"><h2><strong>Improvements</strong></h2><ul><li><p><strong>Assessment zone drawing aligned to visible viewport</strong>: TUG and Gait Speed assessment canvas overlays (zones, silhouettes, guide lines, paused text) now draw within the actual visible region, preventing zones from rendering in cropped/invisible areas.</p></li><li><p><strong>Workout layout uses dedicated safe-area-aware height</strong></p></li><li><p><strong>Challenge page mobile button placement</strong>: The \"Start Challenge\" button on mobile is now a fixed overlay outside the scrollable area with safe-area bottom inset, making it consistently reachable on all devices.</p></li><li><p><strong>Toast notifications respect notch</strong>: Toast messages now offset from the top by the safe-area inset so they don't overlap the status bar.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Accuracy display for non-AI and time-based exercises</strong>: Fixed incorrect 0% accuracy shown for exercises with AI tracking disabled or time-based exercises. The formula now correctly calculates percentage</p></li></ul>","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Impacted integrations: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"text\":\" •\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" •\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"text\":\" •\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" • \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Custom workout\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Mobile safe-area inset support\"},{\"type\":\"text\",\"text\":\": The SDK now respects \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"env(safe-area-inset-*)\"},{\"type\":\"text\",\"text\":\" on devices with notches and home indicators. Layout, buttons, and overlays automatically avoid system UI areas — no integrator configuration needed.\"}]}]}]},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/11f695bf-ab8f-44e3-a93f-5573a3d54cf9.PNG\",\"alt\":null,\"title\":null}},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/21d09ade-1510-498f-b931-589c71e35713.PNG\",\"alt\":null,\"title\":null}},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"   \"}]},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/c9bcb6fd-6c04-4e08-a681-86220775f77d.PNG\",\"alt\":null,\"title\":null}},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment zone drawing aligned to visible viewport\"},{\"type\":\"text\",\"text\":\": TUG and Gait Speed assessment canvas overlays (zones, silhouettes, guide lines, paused text) now draw within the actual visible region, preventing zones from rendering in cropped/invisible areas.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout layout uses dedicated safe-area-aware height\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Challenge page mobile button placement\"},{\"type\":\"text\",\"text\":\": The \\\"Start Challenge\\\" button on mobile is now a fixed overlay outside the scrollable area with safe-area bottom inset, making it consistently reachable on all devices.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Toast notifications respect notch\"},{\"type\":\"text\",\"text\":\": Toast messages now offset from the top by the safe-area inset so they don't overlap the status bar.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Accuracy display for non-AI and time-based exercises\"},{\"type\":\"text\",\"text\":\": Fixed incorrect 0% accuracy shown for exercises with AI tracking disabled or time-based exercises. The formula now correctly calculates percentage\"}]}]}]}]}","exerciseTitle":null,"publishedAt":"2026-03-11T14:09:02.857Z","status":"published","updatedAt":"2026-03-11T14:09:02.857Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-11T14:09:21.102Z","subscriberCountAtSend":18},{"id":"Z9uq53BgLGePx0RGrYzt","title":"Filters & Library UX","slug":"filters-library-ux","summary":"This release is all about making it easier to find the right exercises, workouts, and plans and to understand what you’re selecting at a glance.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Smarter, richer filters\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Across Exercises, Workouts, and Plans you can now narrow content much more precisely:\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Filter by \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"multiple companies\"},{\"type\":\"text\",\"text\":\" at once instead of just one.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Filter by \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"difficulty\"},{\"type\":\"text\",\"text\":\" (easy / medium / hard).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Filter by \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"gender\"},{\"type\":\"text\",\"text\":\" where relevant (male / female variations).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Filter by \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"exercise / workout type\"},{\"type\":\"text\",\"text\":\" (rep-based, timer-based, holds, breaks, etc.).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Filter by \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"languages\"},{\"type\":\"text\",\"text\":\" the content is available in.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Filter by \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"target muscles / body parts\"},{\"type\":\"text\",\"text\":\" with clearer labels.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Filter by \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"equipment\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"contraindications\"},{\"type\":\"text\",\"text\":\" for exercises.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Filter workouts by \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"number of exercises\"},{\"type\":\"text\",\"text\":\" (min/max).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Filter plans by \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"number of weeks\"},{\"type\":\"text\",\"text\":\" (min/max).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Toggle \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"“Active only”\"},{\"type\":\"text\",\"text\":\" to hide inactive content.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The small red “dot” on the filter button now lights up whenever \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"any\"},{\"type\":\"text\",\"text\":\" filter is active, so it’s obvious when you’re looking at a narrowed view.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Better lists and overviews\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"In the lists for plans and workouts, cards now show \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"more meaningful summary info\"},{\"type\":\"text\",\"text\":\" directly in the catalog:\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Plans show \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"total workouts\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"duration in weeks\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"muscles covered\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"languages\"},{\"type\":\"text\",\"text\":\", and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"category\"},{\"type\":\"text\",\"text\":\".\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Workouts show \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"number of exercises\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"category\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"difficulty\"},{\"type\":\"text\",\"text\":\", and other key stats.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Exercise list items now correctly reflect whether they’re \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"timer-based\"},{\"type\":\"text\",\"text\":\" or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"rep-based\"},{\"type\":\"text\",\"text\":\", improving consistency with what users see in the apps.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"You’ll also see clearer \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"“Created by”\"},{\"type\":\"text\",\"text\":\" labels and improved card layouts that make scanning and comparing content faster.\"}]}]}","contentHtml":"<p><strong>Smarter, richer filters</strong>&nbsp;</p><p>Across Exercises, Workouts, and Plans you can now narrow content much more precisely:</p><ul><li><p>Filter by <strong>multiple companies</strong> at once instead of just one.</p></li><li><p>Filter by <strong>difficulty</strong> (easy / medium / hard).</p></li><li><p>Filter by <strong>gender</strong> where relevant (male / female variations).</p></li><li><p>Filter by <strong>exercise / workout type</strong> (rep-based, timer-based, holds, breaks, etc.).</p></li><li><p>Filter by <strong>languages</strong> the content is available in.</p></li><li><p>Filter by <strong>target muscles / body parts</strong> with clearer labels.</p></li><li><p>Filter by <strong>equipment</strong> and <strong>contraindications</strong> for exercises.</p></li><li><p>Filter workouts by <strong>number of exercises</strong> (min/max).</p></li><li><p>Filter plans by <strong>number of weeks</strong> (min/max).</p></li><li><p>Toggle <strong>“Active only”</strong> to hide inactive content.</p></li></ul><p>The small red “dot” on the filter button now lights up whenever <em>any</em> filter is active, so it’s obvious when you’re looking at a narrowed view.</p><p><strong>Better lists and overviews</strong>&nbsp;</p><p>In the lists for plans and workouts, cards now show <strong>more meaningful summary info</strong> directly in the catalog:</p><ul><li><p>Plans show <strong>total workouts</strong>, <strong>duration in weeks</strong>, <strong>muscles covered</strong>, <strong>languages</strong>, and <strong>category</strong>.</p></li><li><p>Workouts show <strong>number of exercises</strong>, <strong>category</strong>, <strong>difficulty</strong>, and other key stats.</p></li><li><p>Exercise list items now correctly reflect whether they’re <strong>timer-based</strong> or <strong>rep-based</strong>, improving consistency with what users see in the apps.</p></li></ul><p>You’ll also see clearer <strong>“Created by”</strong> labels and improved card layouts that make scanning and comparing content faster.</p>","coverImage":null,"category":"improved","product":"dashboard","version":"1.0.1","exerciseId":null,"exerciseTitle":null,"notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-11T13:40:58.798Z","publishedAt":"2026-03-11T13:41:06.955Z","status":"published","updatedAt":"2026-03-11T13:41:06.955Z"},{"id":"nnKf9tAwFrhuTFH5WZ4r","title":"Exercise Progress Tracking in PostMessage Events","slug":"exercise-progress-tracking-in-postmessage-events","summary":"Summary: This release adds exercise progress fields (exercise_index, total_exercises) to the exercise_completed PostMessage event and introduces a new navigation_back event fired when users navigate to a previous exercise during a workout. Integrators who want to track workout progress in real time can now use these new fields — no breaking changes, no migration required.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"navigation_back\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" PostMessage event\"},{\"type\":\"text\",\"text\":\": A new event is dispatched when a user navigates back to a previous exercise during a workout, providing the current exercise index and total exercise count.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New fields on \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercise_completed\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Two new fields have been added to the \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercise_completed\"},{\"type\":\"text\",\"text\":\" event payload: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"exercise_index\"},{\"type\":\"text\",\"text\":\" (1-based position of the completed exercise) and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"total_exercises\"},{\"type\":\"text\",\"text\":\" (total number of exercises in the workout).\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"exercise_completed\\\",\\n  \\\"data\\\": {\\n    \\\"exercise_title\\\": \\\"Squats\\\",\\n    \\\"time_spent\\\": 45,\\n    \\\"repeats\\\": 12,\\n    \\\"total_reps\\\": 15,\\n    \\\"total_duration\\\": 60,\\n    \\\"calories\\\": 8.5,\\n    \\\"exercise_id\\\": \\\"squats_01\\\",\\n    \\\"exercise\\\": \\\"squats_01\\\",\\n    \\\"mistakes\\\": [],\\n    \\\"average_accuracy\\\": 0.85\\n  }\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"exercise_completed\\\",\\n  \\\"data\\\": {\\n    \\\"exercise_title\\\": \\\"Squats\\\",\\n    \\\"time_spent\\\": 45,\\n    \\\"repeats\\\": 12,\\n    \\\"total_reps\\\": 15,\\n    \\\"total_duration\\\": 60,\\n    \\\"calories\\\": 8.5,\\n    \\\"exercise_id\\\": \\\"squats_01\\\",\\n    \\\"exercise\\\": \\\"squats_01\\\",\\n    \\\"exercise_index\\\": 3,\\n    \\\"total_exercises\\\": 8,\\n    \\\"mistakes\\\": [],\\n    \\\"average_accuracy\\\": 0.85\\n  }\\n}\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"navigation_back\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Fired when the user taps back to return to a previous exercise during a workout.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Payload:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"navigation_back\\\",\\n  \\\"data\\\": {\\n    \\\"exercise_index\\\": 2,\\n    \\\"total_exercises\\\": 8\\n  }\\n}\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Workout</code></p><h2><strong>New Features</strong></h2><ul><li><p><code>navigation_back</code><strong> PostMessage event</strong>: A new event is dispatched when a user navigates back to a previous exercise during a workout, providing the current exercise index and total exercise count.</p></li></ul><h3><strong>New fields on </strong><code>exercise_completed</code></h3><p>Two new fields have been added to the <code>exercise_completed</code> event payload: <code>exercise_index</code> (1-based position of the completed exercise) and <code>total_exercises</code> (total number of exercises in the workout).</p><p><strong>Before:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"exercise_completed\",\n  \"data\": {\n    \"exercise_title\": \"Squats\",\n    \"time_spent\": 45,\n    \"repeats\": 12,\n    \"total_reps\": 15,\n    \"total_duration\": 60,\n    \"calories\": 8.5,\n    \"exercise_id\": \"squats_01\",\n    \"exercise\": \"squats_01\",\n    \"mistakes\": [],\n    \"average_accuracy\": 0.85\n  }\n}</code></pre><p><strong>After:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"exercise_completed\",\n  \"data\": {\n    \"exercise_title\": \"Squats\",\n    \"time_spent\": 45,\n    \"repeats\": 12,\n    \"total_reps\": 15,\n    \"total_duration\": 60,\n    \"calories\": 8.5,\n    \"exercise_id\": \"squats_01\",\n    \"exercise\": \"squats_01\",\n    \"exercise_index\": 3,\n    \"total_exercises\": 8,\n    \"mistakes\": [],\n    \"average_accuracy\": 0.85\n  }\n}</code></pre><h3><strong>New event: </strong><code>navigation_back</code></h3><p>Fired when the user taps back to return to a previous exercise during a workout.</p><p><strong>Payload:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"navigation_back\",\n  \"data\": {\n    \"exercise_index\": 2,\n    \"total_exercises\": 8\n  }\n}</code></pre>","coverImage":null,"category":"improved","product":"client","version":"2.20.1","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-10T13:21:37.507Z","publishedAt":"2026-03-10T13:23:20.845Z","status":"published","updatedAt":"2026-03-10T13:23:20.845Z","emailMode":"groups","notifiedGroupIds":["PLpyYxtg1dplh0UYw5cx"],"notificationSentAt":"2026-03-10T13:23:22.247Z","subscriberCountAtSend":1},{"id":"CSt91IZ60PmSKNhzXArK","summary":"This release introduces a new interactive guide/tooltip system that walks first-time users through all major screens (Home, Workout, Exercise, Plan, Challenge, Leaderboard, Settings). You can disable this via the disableGuide configuration option — no other integration changes are required.","coverImage":null,"category":"new","product":"client","version":"2.19.0","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-07T10:25:14.496Z","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Challenge</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Plan</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Personalized Plan</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Home Page</code> <span style=\"color: rgb(240, 246, 252);\">· </span><code>Assessments</code> </p><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/0468d4de-315e-4592-992e-32210565afed.png\"><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/3ed17551-15fd-46bb-ad2a-3795209d68fd.png\"><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/7d7f3148-f7a8-4eb3-b9df-20d62f61a6e8.png\"><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/0dc887d4-dd08-45fc-bedd-dc0f978c2ac5.png\"><h2><strong>New Features</strong></h2><ul><li><p><strong>Interactive Guide System</strong>: A step-by-step tooltip overlay guides new users through key UI areas — workouts, exercises, plans, challenges, leaderboard, and settings. Each screen has contextual guide steps with highlighted elements, scroll-to behavior, and localized text (15 languages).</p></li><li><p><code>disableGuide</code><strong> Configuration Option</strong>: Integrators can pass <code>disableGuide: true</code> in the customization config to suppress the guide entirely for embedded contexts where onboarding isn't needed.</p></li><li><p><strong>New Event: </strong><code>feedback_submitted</code></p><p>A new PostMessage event is dispatched to the host app whenever a user submits feedback (training rating or per-exercise feedback).</p><p><strong>Training feedback payload:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"feedback_submitted\",\n  \"source\": \"training_feedback\",\n  \"rating\": 4,\n  \"is_like\": true,\n  \"description\": \"Great workout!\",\n  \"workout_id\": \"abc123\",\n  \"workout_title\": \"Full Body HIIT\"\n}</code></pre><p><strong>Per-exercise feedback payload:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"feedback_submitted\",\n  \"source\": \"exercise_feedback\",\n  \"workout_id\": \"abc123\",\n  \"workout_title\": \"Full Body HIIT\",\n  \"feedbacks\": [\n    {\n      \"exercise_id\": \"squat_01\",\n      \"exercise_title\": \"Squats\",\n      \"is_like\": false,\n      \"description\": \"Reps not counted correctly\"\n    }\n  ]\n}</code></pre><p><strong>Migration</strong>: Listen for <code>\"feedback_submitted\"</code> events if you want to capture user feedback in your own analytics or CRM. No action required if you don't need this data.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>0% Progress Bar Rendering</strong>: Fixed progress bar showing a visible border line when accuracy was exactly 0%.</p></li><li><p><strong>Button Styles in Modals</strong>: Close/action buttons in feedback, audio help, audio report, exercises list, and exit-challenge modals now use <code>bg-background</code> for better theme consistency.</p></li><li><p><strong>Rep-Based Exercise with No Reps</strong>: Fixed a case where rep-based AI exercises with 0 completed reps could incorrectly show a timer-based progress calculation instead of \"No data available\".</p></li></ul>","title":"Interactive Guide System, Bug Fixes & New feedback_submitted event","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Home Page\"},{\"type\":\"text\",\"text\":\" \"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\"· \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/0468d4de-315e-4592-992e-32210565afed.png\",\"alt\":null,\"title\":null}},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/3ed17551-15fd-46bb-ad2a-3795209d68fd.png\",\"alt\":null,\"title\":null}},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/7d7f3148-f7a8-4eb3-b9df-20d62f61a6e8.png\",\"alt\":null,\"title\":null}},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/0dc887d4-dd08-45fc-bedd-dc0f978c2ac5.png\",\"alt\":null,\"title\":null}},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Interactive Guide System\"},{\"type\":\"text\",\"text\":\": A step-by-step tooltip overlay guides new users through key UI areas — workouts, exercises, plans, challenges, leaderboard, and settings. Each screen has contextual guide steps with highlighted elements, scroll-to behavior, and localized text (15 languages).\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"disableGuide\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" Configuration Option\"},{\"type\":\"text\",\"text\":\": Integrators can pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"disableGuide: true\"},{\"type\":\"text\",\"text\":\" in the customization config to suppress the guide entirely for embedded contexts where onboarding isn't needed.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"feedback_submitted\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"A new PostMessage event is dispatched to the host app whenever a user submits feedback (training rating or per-exercise feedback).\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Training feedback payload:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"feedback_submitted\\\",\\n  \\\"source\\\": \\\"training_feedback\\\",\\n  \\\"rating\\\": 4,\\n  \\\"is_like\\\": true,\\n  \\\"description\\\": \\\"Great workout!\\\",\\n  \\\"workout_id\\\": \\\"abc123\\\",\\n  \\\"workout_title\\\": \\\"Full Body HIIT\\\"\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Per-exercise feedback payload:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"feedback_submitted\\\",\\n  \\\"source\\\": \\\"exercise_feedback\\\",\\n  \\\"workout_id\\\": \\\"abc123\\\",\\n  \\\"workout_title\\\": \\\"Full Body HIIT\\\",\\n  \\\"feedbacks\\\": [\\n    {\\n      \\\"exercise_id\\\": \\\"squat_01\\\",\\n      \\\"exercise_title\\\": \\\"Squats\\\",\\n      \\\"is_like\\\": false,\\n      \\\"description\\\": \\\"Reps not counted correctly\\\"\\n    }\\n  ]\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": Listen for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"\\\"feedback_submitted\\\"\"},{\"type\":\"text\",\"text\":\" events if you want to capture user feedback in your own analytics or CRM. No action required if you don't need this data.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"0% Progress Bar Rendering\"},{\"type\":\"text\",\"text\":\": Fixed progress bar showing a visible border line when accuracy was exactly 0%.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Button Styles in Modals\"},{\"type\":\"text\",\"text\":\": Close/action buttons in feedback, audio help, audio report, exercises list, and exit-challenge modals now use \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"bg-background\"},{\"type\":\"text\",\"text\":\" for better theme consistency.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Rep-Based Exercise with No Reps\"},{\"type\":\"text\",\"text\":\": Fixed a case where rep-based AI exercises with 0 completed reps could incorrectly show a timer-based progress calculation instead of \\\"No data available\\\".\"}]}]}]}]}","slug":"interactive-guide-system-bug-fixes-new-feedback-submitted-event","publishedAt":"2026-03-08T19:15:18.360Z","status":"published","updatedAt":"2026-03-08T19:15:18.360Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-08T19:15:35.424Z","subscriberCountAtSend":17},{"id":"Kkaqr99UvG8FHnuiZPE3","title":"Auto-Start Workouts from Plan, Image Preloading, Assessment & Audio Fixes","slug":"auto-start-workouts-from-plan-image-preloading-assessment-audio-fixes","summary":"This release adds a seamless auto-start flow that launches workouts directly from the plan result screen, eliminating the intermediate workout detail page. Loading images are now preloaded during onboarding/assessment flows for snappier transitions for personalized plan. Several UX fixes improve assessment instructions and remove an unwanted countdown sound. No PostMessage or integration API changes — no code changes required from integrators.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Auto-start workout from plan result\"},{\"type\":\"text\",\"text\":\": Tapping \\\"Start\\\" on the plan result page now launches the first workout immediately with a loading skeleton, bypassing the workout detail screen for a faster start experience.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Image preloading for plan onboarding & assessments\"},{\"type\":\"text\",\"text\":\": Plan loading background images and result assets are preloaded early during the onboarding and assessment flows, reducing visible loading when transitioning to the plan result screen.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan result rest day styling\"},{\"type\":\"text\",\"text\":\": Rest day rows now use the primary background color instead of secondary, improving visual consistency with the rest of the plan schedule.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Removed countdown sound from outro rest\"},{\"type\":\"text\",\"text\":\": The countdown beep no longer plays during the final rest period (outro) of a workout, which was an unintended behavior.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Five Times STS assessment instruction\"},{\"type\":\"text\",\"text\":\": Fixed the instruction phrase displayed during the Five Times Sit-to-Stand assessment to show the correct 5-rep instruction instead of the 30-second variant.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Personalized Plan</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Assessments</code></p><h2><strong>New Features</strong></h2><ul><li><p><strong>Auto-start workout from plan result</strong>: Tapping \"Start\" on the plan result page now launches the first workout immediately with a loading skeleton, bypassing the workout detail screen for a faster start experience.</p></li><li><p><strong>Image preloading for plan onboarding &amp; assessments</strong>: Plan loading background images and result assets are preloaded early during the onboarding and assessment flows, reducing visible loading when transitioning to the plan result screen.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Plan result rest day styling</strong>: Rest day rows now use the primary background color instead of secondary, improving visual consistency with the rest of the plan schedule.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Removed countdown sound from outro rest</strong>: The countdown beep no longer plays during the final rest period (outro) of a workout, which was an unintended behavior.</p></li><li><p><strong>Five Times STS assessment instruction</strong>: Fixed the instruction phrase displayed during the Five Times Sit-to-Stand assessment to show the correct 5-rep instruction instead of the 30-second variant.</p></li></ul>","coverImage":null,"category":"new","product":"client","version":"2.18.10","notificationSentAt":null,"subscriberCountAtSend":null,"authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-05T19:10:25.041Z","publishedAt":"2026-03-05T19:10:44.834Z","status":"published","updatedAt":"2026-03-05T19:10:44.834Z"},{"id":"llkYE2UwEg0c7ap1hbRb","coverImage":null,"category":"new","product":"client","version":"2.18.9","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-04T19:19:12.478Z","summary":"This release adds a new hideCompletionOverlay configuration option for integrators who want to skip the completion animation, and upgrades the 3D motion replay with color-coded skeleton feedback. No breaking changes — all additions are backward-compatible.","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Challenge</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Personalized Plan</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Camera</code> </p><h2><strong>New Features</strong></h2><ul><li><p><code>hideCompletionOverlay</code><strong> configuration option</strong>: Integrators can now pass <code>hideCompletionOverlay: true</code> via the initialization config to suppress the confetti/completion overlay that appears after a workout finishes. The workout will go straight to the statistics screen.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Enhanced motion replay 3D skeleton</strong>: The replay viewer now renders a trapezoid-based skeleton with per-joint glow effects and color-coded feedback — white for normal frames, red flash for mistakes, green flash for rep completions.</p><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/53a7c6ff-b0fb-47a9-b9a6-1c04c5a1bea6.png\"></li><li><p><strong>Camera Access Denied modal refactored to BottomSheet</strong>: The modal now uses the shared <code>BottomSheet</code> component, providing consistent drag-to-dismiss behavior and a cleaner layout.</p></li><li><p><strong>CheckFrame skip button repositioned</strong>: The \"Skip\" button on the check-frame screen moved from below the body outline to the top navigation bar for better visibility.</p></li><li><p><strong>Rest screen navigation buttons restyled</strong>: Previous/next exercise buttons on the rest screen now use larger touch targets with updated rounded styling for better consistency.</p></li><li><p><strong>Challenge UI respects theme CSS variables</strong>: Challenge cards (progress items, countdown, ranked score) now read <code>--card-bg</code>, <code>--card-border-width</code>, and <code>--card-border-color</code> from the active theme, enabling white-label visual overrides.</p></li></ul><h2></h2><h2></h2><p></p>","title":"New hideCompletionOverlay Config, Enhanced Motion Replay Visualization","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"hideCompletionOverlay\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" configuration option\"},{\"type\":\"text\",\"text\":\": Integrators can now pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"hideCompletionOverlay: true\"},{\"type\":\"text\",\"text\":\" via the initialization config to suppress the confetti/completion overlay that appears after a workout finishes. The workout will go straight to the statistics screen.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Enhanced motion replay 3D skeleton\"},{\"type\":\"text\",\"text\":\": The replay viewer now renders a trapezoid-based skeleton with per-joint glow effects and color-coded feedback — white for normal frames, red flash for mistakes, green flash for rep completions.\"}]},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/53a7c6ff-b0fb-47a9-b9a6-1c04c5a1bea6.png\",\"alt\":null,\"title\":null}}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Camera Access Denied modal refactored to BottomSheet\"},{\"type\":\"text\",\"text\":\": The modal now uses the shared \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"BottomSheet\"},{\"type\":\"text\",\"text\":\" component, providing consistent drag-to-dismiss behavior and a cleaner layout.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"CheckFrame skip button repositioned\"},{\"type\":\"text\",\"text\":\": The \\\"Skip\\\" button on the check-frame screen moved from below the body outline to the top navigation bar for better visibility.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Rest screen navigation buttons restyled\"},{\"type\":\"text\",\"text\":\": Previous/next exercise buttons on the rest screen now use larger touch targets with updated rounded styling for better consistency.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Challenge UI respects theme CSS variables\"},{\"type\":\"text\",\"text\":\": Challenge cards (progress items, countdown, ranked score) now read \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"--card-bg\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"--card-border-width\"},{\"type\":\"text\",\"text\":\", and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"--card-border-color\"},{\"type\":\"text\",\"text\":\" from the active theme, enabling white-label visual overrides.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2}},{\"type\":\"heading\",\"attrs\":{\"level\":2}},{\"type\":\"paragraph\"}]}","slug":"new-hidecompletionoverlay-config-enhanced-motion-replay-visualization","publishedAt":"2026-03-04T19:23:55.354Z","status":"published","updatedAt":"2026-03-04T19:23:55.354Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-04T19:24:05.552Z","subscriberCountAtSend":17},{"id":"3qENgVKDUpKaWil5ZsdS","title":"Rounded Assessment Data, Loading Screen Fix, Theming Enhancements","slug":"rounded-assessment-data-loading-screen-fix-theming-enhancements","summary":"Assessment PostMessage payloads now return consistently rounded numeric values instead of raw floats — integrators parsing these values may see slight precision changes but no field additions or removals. A bug where the loading screen could get stuck at 75% for non-AI workouts has been fixed. White-label theming now supports additional CSS custom properties for gradient backgrounds and card borders.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Home Page\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Camera\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment data precision\"},{\"type\":\"text\",\"text\":\": All numeric fields in assessment PostMessage events (\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_overview\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_completed\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_restart\"},{\"type\":\"text\",\"text\":\") are now rounded to consistent decimal places (1–2 dp depending on the metric), matching the values displayed in the UI.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Larger progress arc on loading screen\"},{\"type\":\"text\",\"text\":\": The progress arc during model loading is now wider across all breakpoints (260→300→360 px) for better visibility.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"White-label theming support\"},{\"type\":\"text\",\"text\":\": Loading screen and workout setup cards now respect CSS custom properties for deeper visual customisation.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Loading screen stuck at 75%\"},{\"type\":\"text\",\"text\":\": Workouts that contain no AI-tracked exercises (e.g. timer-only) no longer stall the loading progress bar. The AI-models percentage now correctly resolves to 100% when there are no models to load.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment event numeric values are now rounded\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"All numeric fields in \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_overview\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_completed\"},{\"type\":\"text\",\"text\":\", and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_restart\"},{\"type\":\"text\",\"text\":\" payloads are now explicitly rounded before dispatch. No fields were added or removed. The same keys are sent with the same types — only the number of decimal places has changed.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Rounding rules by metric:\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"1 decimal place (1 dp):\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"time\"},{\"type\":\"text\",\"text\":\" (TUG/gait/stance types), \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"standingUpTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"sittingDownTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"walkingForwardTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"walkingBackwardTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"turningTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"standingTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"walkingTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"reach\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"maxHeelLift\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"timeInProperPosition_*\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"maxShoulderShift_*\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"maxHipShift_*\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"leftLegIdealTime_balancetest\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"rightLegIdealTime_balancetest\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"*Sway_balancetest\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"backBendingAngle*\"},{\"type\":\"text\",\"text\":\" (array elements)\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"2 decimal places (2 dp):\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"time\"},{\"type\":\"text\",\"text\":\" (SLS/fivetimessts/common), \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"rightTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"leftTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"averageSittingTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"averageStandingTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"repTimeVariance_minRepTime\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"repTimeVariance_maxRepTime\"},{\"type\":\"text\",\"text\":\" \"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before (TUG example):\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"assessment_completed\\\",\\n  \\\"assessmentType\\\": \\\"tug\\\",\\n  \\\"time\\\": 12.345678,\\n  \\\"standingUpTime\\\": 1.234567,\\n  \\\"sittingDownTime\\\": 2.345678,\\n  \\\"walkingForwardTime\\\": 3.456789,\\n  \\\"backBendingAngleSitting\\\": [15.678901, 12.345678]\\n}\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{\\n  \\\"type\\\": \\\"assessment_completed\\\",\\n  \\\"assessmentType\\\": \\\"tug\\\",\\n  \\\"time\\\": 12.3,\\n  \\\"standingUpTime\\\": 1.2,\\n  \\\"sittingDownTime\\\": 2.3,\\n  \\\"walkingForwardTime\\\": 3.5,\\n  \\\"backBendingAngleSitting\\\": [15.7, 12.3]\\n}\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Challenge</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Plan</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Personalized Plan</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Home Page</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Camera</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Assessments</code> </p><h2><strong>Improvements</strong></h2><ul><li><p><strong>Assessment data precision</strong>: All numeric fields in assessment PostMessage events (<code>assessment_overview</code>, <code>assessment_completed</code>, <code>assessment_restart</code>) are now rounded to consistent decimal places (1–2 dp depending on the metric), matching the values displayed in the UI.</p></li><li><p><strong>Larger progress arc on loading screen</strong>: The progress arc during model loading is now wider across all breakpoints (260→300→360 px) for better visibility.</p></li><li><p><strong>White-label theming support</strong>: Loading screen and workout setup cards now respect CSS custom properties for deeper visual customisation.</p></li></ul><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Loading screen stuck at 75%</strong>: Workouts that contain no AI-tracked exercises (e.g. timer-only) no longer stall the loading progress bar. The AI-models percentage now correctly resolves to 100% when there are no models to load.</p></li></ul><h3><strong>Assessment event numeric values are now rounded</strong></h3><p>All numeric fields in <code>assessment_overview</code>, <code>assessment_completed</code>, and <code>assessment_restart</code> payloads are now explicitly rounded before dispatch. No fields were added or removed. The same keys are sent with the same types — only the number of decimal places has changed.</p><p><strong>Rounding rules by metric:</strong></p><ul><li><p><strong>1 decimal place (1 dp):</strong><br><code>time</code> (TUG/gait/stance types), <code>standingUpTime</code>, <code>sittingDownTime</code>, <code>walkingForwardTime</code>, <code>walkingBackwardTime</code>, <code>turningTime</code>, <code>standingTime</code>, <code>walkingTime</code>, <code>reach</code>, <code>maxHeelLift</code>, <code>timeInProperPosition_*</code>, <code>maxShoulderShift_*</code>, <code>maxHipShift_*</code>, <code>leftLegIdealTime_balancetest</code>, <code>rightLegIdealTime_balancetest</code>, <code>*Sway_balancetest</code>, <code>backBendingAngle*</code> (array elements)</p></li><li><p><strong>2 decimal places (2 dp):</strong><br><code>time</code> (SLS/fivetimessts/common), <code>rightTime</code>, <code>leftTime</code>, <code>averageSittingTime</code>, <code>averageStandingTime</code>, <code>repTimeVariance_minRepTime</code>, <code>repTimeVariance_maxRepTime</code> </p></li></ul><p><strong>Before (TUG example):</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"assessment_completed\",\n  \"assessmentType\": \"tug\",\n  \"time\": 12.345678,\n  \"standingUpTime\": 1.234567,\n  \"sittingDownTime\": 2.345678,\n  \"walkingForwardTime\": 3.456789,\n  \"backBendingAngleSitting\": [15.678901, 12.345678]\n}</code></pre><p><strong>After:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{\n  \"type\": \"assessment_completed\",\n  \"assessmentType\": \"tug\",\n  \"time\": 12.3,\n  \"standingUpTime\": 1.2,\n  \"sittingDownTime\": 2.3,\n  \"walkingForwardTime\": 3.5,\n  \"backBendingAngleSitting\": [15.7, 12.3]\n}</code></pre>","coverImage":null,"category":"new","product":"client","version":"2.18.8","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-03T19:52:30.764Z","publishedAt":"2026-03-03T19:53:06.549Z","status":"published","updatedAt":"2026-03-03T19:53:06.549Z","emailMode":"all","notifiedGroupIds":null,"notificationSentAt":"2026-03-03T19:53:16.449Z","subscriberCountAtSend":17},{"id":"9LRUnK2hkD949DZsrAOT","summary":"This release fixes assessment UI layout issues that appeared when the SDK was embedded in apps using right-to-left (RTL) languages (e.g., Arabic, Hebrew). No integration code changes are required — these are visual fixes that apply automatically.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Bug Fixes\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment layout broken in RTL contexts\"},{\"type\":\"text\",\"text\":\": The assessment container now explicitly enforces \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"left to right\"},{\"type\":\"text\",\"text\":\" on its root element, preventing RTL host apps from mirroring the entire assessment UI (buttons, overlays, and camera feed).\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Assessments</code> </p><h2><strong>Bug Fixes</strong></h2><ul><li><p><strong>Assessment layout broken in RTL contexts</strong>: The assessment container now explicitly enforces <code>left to right</code> on its root element, preventing RTL host apps from mirroring the entire assessment UI (buttons, overlays, and camera feed).</p></li></ul>","coverImage":null,"category":"new","product":"client","version":"2.18.7","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-02T14:57:50.435Z","publishedAt":"2026-03-02T14:59:53.002Z","status":"published","emailMode":"groups","notifiedGroupIds":["0IkpaHDJ3Sf13IxiIsCm"],"notificationSentAt":"2026-03-02T14:59:54.492Z","subscriberCountAtSend":2,"title":"Fix Assessment Layout for RTL Locales","slug":"fix-assessment-layout-for-rtl-locales","updatedAt":"2026-03-02T15:00:09.301Z"},{"id":"6o2aznEQ8geyDNW7jeeY","title":"New Assessment Exit Events, Improved Exit UX","slug":"new-assessment-exit-events-improved-exit-ux","summary":"This release adds two new PostMessage events (assessment_exit and assessment_exit_results) that notify the host app when a user exits an assessment early. Integrators listening for assessment lifecycle events should subscribe to these new events. Users will also notice that audio stops immediately upon confirming an early exit.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment exit audio\"},{\"type\":\"text\",\"text\":\": Audio playback now stops immediately when a user confirms exiting an assessment, preventing lingering sound after leaving the screen.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"API Changes (PostMessage)\"}]},{\"type\":\"blockquote\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Action Required\"},{\"type\":\"text\",\"text\":\": Yes — subscribe to new events if you need to track early assessment exits.\"}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New events: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_exit\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_exit_results\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Previously, when a user tapped the close button and confirmed exiting an assessment early, \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"no PostMessage event was sent\"},{\"type\":\"text\",\"text\":\" to the host app. Now the SDK dispatches one of two new events depending on whether partial results were available.\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_exit\"},{\"type\":\"text\",\"text\":\" — The user exited early \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"without\"},{\"type\":\"text\",\"text\":\" enough data to show results (e.g., quit before completing any test phase). The SDK navigates back to the assessment overview internally.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_exit_results\"},{\"type\":\"text\",\"text\":\" — The user exited early \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"with\"},{\"type\":\"text\",\"text\":\" partial results. The SDK navigates to the results screen so the user can review what was captured, and the host is notified.\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Before:\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"text\":\"No message was posted on early exit.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"After:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"assessment_exit\\\" }\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"or\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"assessment_exit_results\\\" }\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Migration\"},{\"type\":\"text\",\"text\":\": These are \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"additive\"},{\"type\":\"text\",\"text\":\" events — no existing events were changed or removed. To take advantage of them, add listeners for \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_exit\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"assessment_exit_results\"},{\"type\":\"text\",\"text\":\" in your PostMessage handler. No changes are required if you don't need to react to early exits.\"}]}]}","contentHtml":"<p><strong>Impacted integrations</strong>: <code>Assessments</code> </p><h2><strong>Improvements</strong></h2><ul><li><p><strong>Assessment exit audio</strong>: Audio playback now stops immediately when a user confirms exiting an assessment, preventing lingering sound after leaving the screen.</p></li></ul><h2><strong>API Changes (PostMessage)</strong></h2><blockquote><p><strong>Action Required</strong>: Yes — subscribe to new events if you need to track early assessment exits.</p></blockquote><h3><strong>New events: </strong><code>assessment_exit</code><strong> and </strong><code>assessment_exit_results</code></h3><p>Previously, when a user tapped the close button and confirmed exiting an assessment early, <strong>no PostMessage event was sent</strong> to the host app. Now the SDK dispatches one of two new events depending on whether partial results were available.</p><ul><li><p><code>assessment_exit</code> — The user exited early <strong>without</strong> enough data to show results (e.g., quit before completing any test phase). The SDK navigates back to the assessment overview internally.</p></li><li><p><code>assessment_exit_results</code> — The user exited early <strong>with</strong> partial results. The SDK navigates to the results screen so the user can review what was captured, and the host is notified.</p></li></ul><p><strong>Before:</strong><br>No message was posted on early exit.</p><p><strong>After:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{ \"type\": \"assessment_exit\" }</code></pre><p>or</p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{ \"type\": \"assessment_exit_results\" }</code></pre><p><strong>Migration</strong>: These are <strong>additive</strong> events — no existing events were changed or removed. To take advantage of them, add listeners for <code>assessment_exit</code> and <code>assessment_exit_results</code> in your PostMessage handler. No changes are required if you don't need to react to early exits.</p>","coverImage":null,"category":"new","product":"client","version":"2.18.6","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-01T18:09:12.685Z","publishedAt":"2026-03-01T18:09:17.077Z","status":"published","updatedAt":"2026-03-01T18:09:17.077Z","notificationSentAt":"2026-03-01T18:09:27.662Z","subscriberCountAtSend":16},{"id":"dHhGD3sgJ4MacfgaHdo6","title":"Plan UI Animations and Asset Optimization","slug":"plan-ui-animations-and-asset-optimization","summary":"This release adds polished entry animations to the Personalized Plan assessment results and plan loading screens","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan Onboarding\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment result graph animations\"},{\"type\":\"text\",\"text\":\": The activity-level graph on the personalized plan result page now features a drawing curve, fade-in area fill, pop-in marker, and fade-in tooltip — giving users a more engaging reveal of their fitness level.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Progressive blur on result CTA\"},{\"type\":\"text\",\"text\":\": The \\\"Continue\\\" button at the bottom of the assessment result screen now sits above a progressive blur overlay, improving readability when content scrolls behind it.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan loading image unblur effect\"},{\"type\":\"text\",\"text\":\": The plan loading view images now animate from blurred to sharp on mount, providing a smoother visual transition while the plan is being generated.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Image asset optimization\"},{\"type\":\"text\",\"text\":\": Three image assets were optimized, reducing combined asset size by 60%\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Personalized Plan</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Plan Onboarding</code> </p><h2><strong>Improvements</strong></h2><ul><li><p><strong>Assessment result graph animations</strong>: The activity-level graph on the personalized plan result page now features a drawing curve, fade-in area fill, pop-in marker, and fade-in tooltip — giving users a more engaging reveal of their fitness level.</p></li><li><p><strong>Progressive blur on result CTA</strong>: The \"Continue\" button at the bottom of the assessment result screen now sits above a progressive blur overlay, improving readability when content scrolls behind it.</p></li><li><p><strong>Plan loading image unblur effect</strong>: The plan loading view images now animate from blurred to sharp on mount, providing a smoother visual transition while the plan is being generated.</p></li><li><p><strong>Image asset optimization</strong>: Three image assets were optimized, reducing combined asset size by 60%</p></li></ul>","coverImage":null,"category":"new","product":"client","version":"2.18.5","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-03-01T12:31:06.283Z","publishedAt":"2026-03-01T12:31:24.173Z","status":"published","updatedAt":"2026-03-01T12:31:24.173Z","notificationSentAt":"2026-03-01T12:31:34.031Z","subscriberCountAtSend":16},{"id":"W2FjRbdac9FLGas62bjF","title":"Revamped Plan Onboarding, New White-Labeled Theming System, Smart Onboarding Skip","slug":"revamped-plan-onboarding-new-white-labeled-theming-system-smart-onboarding-skip","summary":"This release introduces a fully redesigned Plan Onboarding flow, a new white-label theming/skin system for custom partner branding, and the ability to skip onboarding screens when user data (gender, age, height, weight, lifestyle) is provided. Integrators who pass user profile fields will see shorter onboarding flows automatically","coverImage":null,"category":"new","product":"client","version":"2.18.4","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-02-28T19:39:48.494Z","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: All Integrations</span><br><em>(Theme system and BackButton changes affect shared UI components used across every integration. Plan Onboarding and Personalized Plan flows have the most significant changes.)</em></p><h2><strong>New Features</strong></h2><ul><li><p><strong>Company Theme/Skin System</strong>: Integrators can pass a <code>themeName</code> parameter (via URL or PostMessage options) to activate company-specific visual overrides — custom CSS variables, icon replacements, and border-radius tokens. Please contact KinesteX team to find out more</p></li><li><p><strong>Assessment Screen in Plan Onboarding</strong>: When a user selects the \"automatic\" goal in plan onboarding, they now see an inline Assessment preview screen with a \"Begin Assessment\" CTA before launching the physical assessment workout.</p></li><li><p><strong>Personalized Plan Generation from Result Page</strong>: The Plan Result page now handles end-to-end personalized plan generation (post-assessment), showing a loading animation while the plan is being created.</p></li></ul><h2><strong>Improvements</strong></h2><ul><li><p><strong>Unified BackButton component</strong>: All back navigation buttons across every screen now use a single <code>BackButton</code> component that supports theme-driven icon and style overrides.</p></li><li><p><strong>Plan Result page redesign</strong>: New visual design with background imagery, improved week timeline UI (branded progress bar, separator lines, better stat formatting), and fully localized content.</p></li><li><p><strong>Fix Smart Onboarding Skip</strong>:  You can now pass <code>gender</code>, <code>age</code>, <code>height</code>, <code>weight</code>, and/or <code>lifestyle</code> in the initial PostMessage/UserDetails options. Any onboarding screen whose data is already provided will be automatically skipped, and the progress indicator adjusts accordingly.</p></li></ul><hr><h3><strong>New event: </strong><code>remind_me_later_clicked</code><strong> (Additive)</strong></h3><p>Fired when the user taps \"Remind me later\" on the Assessment screen within the plan onboarding flow. Allows the host app to dismiss the SDK and schedule a follow-up prompt.</p><p><strong>Payload:</strong></p><pre class=\"rounded-md bg-muted border p-4 font-mono text-sm\"><code>{ \"type\": \"remind_me_later_clicked\" }</code></pre><hr>","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": All Integrations\"},{\"type\":\"hardBreak\"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"(Theme system and BackButton changes affect shared UI components used across every integration. Plan Onboarding and Personalized Plan flows have the most significant changes.)\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Company Theme/Skin System\"},{\"type\":\"text\",\"text\":\": Integrators can pass a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"themeName\"},{\"type\":\"text\",\"text\":\" parameter (via URL or PostMessage options) to activate company-specific visual overrides — custom CSS variables, icon replacements, and border-radius tokens. Please contact KinesteX team to find out more\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Assessment Screen in Plan Onboarding\"},{\"type\":\"text\",\"text\":\": When a user selects the \\\"automatic\\\" goal in plan onboarding, they now see an inline Assessment preview screen with a \\\"Begin Assessment\\\" CTA before launching the physical assessment workout.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Personalized Plan Generation from Result Page\"},{\"type\":\"text\",\"text\":\": The Plan Result page now handles end-to-end personalized plan generation (post-assessment), showing a loading animation while the plan is being created.\"}]}]}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Improvements\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Unified BackButton component\"},{\"type\":\"text\",\"text\":\": All back navigation buttons across every screen now use a single \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"BackButton\"},{\"type\":\"text\",\"text\":\" component that supports theme-driven icon and style overrides.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan Result page redesign\"},{\"type\":\"text\",\"text\":\": New visual design with background imagery, improved week timeline UI (branded progress bar, separator lines, better stat formatting), and fully localized content.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Fix Smart Onboarding Skip\"},{\"type\":\"text\",\"text\":\":  You can now pass \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"gender\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"age\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"height\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"weight\"},{\"type\":\"text\",\"text\":\", and/or \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"lifestyle\"},{\"type\":\"text\",\"text\":\" in the initial PostMessage/UserDetails options. Any onboarding screen whose data is already provided will be automatically skipped, and the progress indicator adjusts accordingly.\"}]}]}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New event: \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"remind_me_later_clicked\"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\" (Additive)\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Fired when the user taps \\\"Remind me later\\\" on the Assessment screen within the plan onboarding flow. Allows the host app to dismiss the SDK and schedule a follow-up prompt.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Payload:\"}]},{\"type\":\"codeBlock\",\"attrs\":{\"language\":null},\"content\":[{\"type\":\"text\",\"text\":\"{ \\\"type\\\": \\\"remind_me_later_clicked\\\" }\"}]},{\"type\":\"horizontalRule\"}]}","publishedAt":"2026-03-01T08:35:44.336Z","status":"published","updatedAt":"2026-03-01T08:35:44.336Z","notificationSentAt":"2026-03-01T08:35:54.740Z","subscriberCountAtSend":16},{"id":"xdRt4TGGvzH3QlOKPaiw","title":"Plan Progression Tracking","slug":"plan-progression-tracking","summary":"You can now pass plan context (planId, planType, progressWorkoutId) through the initial PostMessage configuration, enabling plan-aware workout tracking without relying on internal routing. Two new outbound PostMessage events notify the host app when plan progression is saved or fails. No breaking changes — all additions are backwards-compatible.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Plan\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Personalized Plan\"},{\"type\":\"text\",\"text\":\" \"}]},{\"type\":\"heading\",\"attrs\":{\"level\":2},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"New Features\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan context via PostMessage\"},{\"type\":\"text\",\"text\":\": You can now send \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"planId\"},{\"type\":\"text\",\"text\":\", \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"planType\"},{\"type\":\"text\",\"text\":\", and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"progressWorkoutId\"},{\"type\":\"text\",\"text\":\" in the initial configuration message. The SDK uses these to associate a workout session with the correct plan and track progression — useful when launching plan workouts directly without navigating through the in-app plan flow.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Plan progression PostMessage events\"},{\"type\":\"text\",\"text\":\": Two new outbound events — \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_progression_saved\"},{\"type\":\"text\",\"text\":\" and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"plan_progression_failed\"},{\"type\":\"text\",\"text\":\" — are emitted after a plan workout finishes, letting the host app react to progression updates in real time.\"}]}]}]}]}","contentHtml":"<p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Plan</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Personalized Plan</code> </p><h2><strong>New Features</strong></h2><ul><li><p><strong>Plan context via PostMessage</strong>: You can now send <code>planId</code>, <code>planType</code>, and <code>progressWorkoutId</code> in the initial configuration message. The SDK uses these to associate a workout session with the correct plan and track progression — useful when launching plan workouts directly without navigating through the in-app plan flow.</p></li><li><p><strong>Plan progression PostMessage events</strong>: Two new outbound events — <code>plan_progression_saved</code> and <code>plan_progression_failed</code> — are emitted after a plan workout finishes, letting the host app react to progression updates in real time.</p></li></ul>","coverImage":null,"category":"new","product":"client","version":"2.18.3","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-02-26T07:15:14.752Z","publishedAt":"2026-02-26T07:15:19.176Z","status":"published","updatedAt":"2026-02-26T07:15:19.176Z","notificationSentAt":"2026-02-26T07:15:32.550Z","subscriberCountAtSend":16},{"id":"9zWEoBIbceINUR8LYW88","title":"Fixed Rest Countdown Timing","slug":"fixed-rest-countdown-timing","summary":"This update fixes rest countdown audio/visual sync issues in workouts","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Rest countdown audio/visual desync\"},{\"type\":\"text\",\"text\":\": Audio countdown ticks and \\\"GO\\\" sound now fire in sync with the on-screen rest timer value. Previously, audio cues were off by ~1 second from what was displayed.\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Rest-to-exercise transition timing\"},{\"type\":\"text\",\"text\":\": The next exercise now triggers at the correct countdown boundary, preventing a 1-second gap where the timer showed 0 but the exercise hadn't started.\"}]}]}]}]}","contentHtml":"<ul><li><p><strong>Rest countdown audio/visual desync</strong>: Audio countdown ticks and \"GO\" sound now fire in sync with the on-screen rest timer value. Previously, audio cues were off by ~1 second from what was displayed.</p></li><li><p><strong>Rest-to-exercise transition timing</strong>: The next exercise now triggers at the correct countdown boundary, preventing a 1-second gap where the timer showed 0 but the exercise hadn't started.</p></li></ul>","coverImage":null,"category":"new","product":"client","version":"2.18.2","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-02-24T13:24:29.242Z","publishedAt":"2026-02-24T13:26:49.289Z","status":"published","updatedAt":"2026-02-24T13:26:49.289Z","notificationSentAt":"2026-02-24T13:26:59.015Z","subscriberCountAtSend":16},{"id":"TMj9MZJBc7BSmIq2qNn2","summary":"This release fixes audio playback on iOS by unlocking the Web Audio context during user-initiated taps.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Users on iOS will now reliably hear exercise cues and audio feedback that were previously blocked requiring user action. \"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\"The SDK now plays a silent audio clip on user gesture (start buttons and motion access grant) to unlock the Web Audio API context, ensuring all subsequent programmatic audio plays correctly.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Impacted integrations\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\": \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Challenge\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Workout\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"code\"}],\"text\":\"Assessments\"},{\"type\":\"text\",\"marks\":[{\"type\":\"textStyle\",\"attrs\":{\"color\":\"rgb(240, 246, 252)\"}}],\"text\":\" · \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"All Integrations (shared audio infrastructure)\"}]}]}","contentHtml":"<p>Users on iOS will now reliably hear exercise cues and audio feedback that were previously blocked requiring user action. </p><p><span style=\"color: rgb(240, 246, 252);\">The SDK now plays a silent audio clip on user gesture (start buttons and motion access grant) to unlock the Web Audio API context, ensuring all subsequent programmatic audio plays correctly.</span></p><p><strong>Impacted integrations</strong><span style=\"color: rgb(240, 246, 252);\">: </span><code>Challenge</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Workout</code><span style=\"color: rgb(240, 246, 252);\"> · </span><code>Assessments</code><span style=\"color: rgb(240, 246, 252);\"> · </span><em>All Integrations (shared audio infrastructure)</em></p>","coverImage":null,"category":"new","product":"client","version":"2.18.1","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-02-22T15:57:09.491Z","title":"Fix (Web) iOS Audio Playback Across Workouts, Challenges, and Assessments","slug":"fix-web-ios-audio-playback-across-workouts-challenges-and-assessments","publishedAt":"2026-02-22T15:57:56.108Z","status":"published","updatedAt":"2026-02-22T15:57:56.108Z","notificationSentAt":"2026-02-22T15:58:07.639Z","subscriberCountAtSend":17},{"id":"5YotPgVvjMysqgww9V7V","title":"Visual Feedback System","slug":"visual-feedback-system","summary":"The Visual Feedback system adds real-time, multi-layered visual overlays on top of the camera feed that show users exactly what’s wrong with their exercise form and where on their body. It replaces the old indicator-based approach with an animation-driven system that triggers on mistakes, displays for 2.5 seconds, and will be rolled out across more configured exercises over time.","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The Visual Feedback system is a real-time form correction feature that gives users \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"immediate, multi-layered visual cues\"},{\"type\":\"text\",\"text\":\" when they perform an exercise incorrectly. Instead of relying solely on audio cues like \\\"straighten your back,\\\" the system now simultaneously shows the user \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"exactly\"},{\"type\":\"text\",\"text\":\" what's wrong and \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"where\"},{\"type\":\"text\",\"text\":\" on their body, using animated graphics overlaid on the camera feed for configured exercises.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"This replaces the previous indicator/turn-body rendering approach with a more flexible, animation-driven system.\"}]},{\"type\":\"horizontalRule\"},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"How It Works — The User's Perspective\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"When a user makes a form mistake during an exercise, up to \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"four layers of visual feedback\"},{\"type\":\"text\",\"text\":\" activate at the same time:\"}]},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/3738e4c9-8280-42ea-8ff3-3c0b97d822d8.png\",\"alt\":null,\"title\":null}},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"All four layers activate together and automatically clear after \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"2.5 seconds\"},{\"type\":\"text\",\"text\":\", returning the view to normal until the next mistake is detected.\"}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"This system will start applying to more and more exercises as we keep on enhancing it.\"}]}]}","contentHtml":"<p>The Visual Feedback system is a real-time form correction feature that gives users <strong>immediate, multi-layered visual cues</strong> when they perform an exercise incorrectly. Instead of relying solely on audio cues like \"straighten your back,\" the system now simultaneously shows the user <em>exactly</em> what's wrong and <em>where</em> on their body, using animated graphics overlaid on the camera feed for configured exercises.</p><p>This replaces the previous indicator/turn-body rendering approach with a more flexible, animation-driven system.</p><hr><h3><strong>How It Works — The User's Perspective</strong></h3><p>When a user makes a form mistake during an exercise, up to <strong>four layers of visual feedback</strong> activate at the same time:</p><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/3738e4c9-8280-42ea-8ff3-3c0b97d822d8.png\"><p>All four layers activate together and automatically clear after <strong>2.5 seconds</strong>, returning the view to normal until the next mistake is detected.</p><p>This system will start applying to more and more exercises as we keep on enhancing it.</p>","coverImage":null,"category":"new","product":"client","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-02-19T15:29:38.917Z","version":"2.18.0","publishedAt":"2026-02-19T15:30:03.188Z","status":"published","updatedAt":"2026-02-19T15:30:03.188Z","notificationSentAt":"2026-02-19T15:30:11.465Z","subscriberCountAtSend":16},{"id":"x0HUvAlxQQMG84MYcqQ0","title":"Workout Completion Overlay and Laptop Device Orientation Placement video","slug":"workout-completion-overlay-and-laptop-device-orientation-placement-video","summary":"We’ve added a celebratory workout completion overlay with efficiency-based messages and introduced device-specific instructional videos for the accelerometer setup on desktop and mobile.","coverImage":null,"category":"new","product":"client","version":"2.17.3","authorName":"vladimir@kinestex.com","authorId":"11","createdAt":"2026-02-19T14:07:51.596Z","publishedAt":"2026-02-19T14:08:00.467Z","status":"published","notificationSentAt":"2026-02-19T14:08:01.765Z","subscriberCountAtSend":1,"contentHtml":"<h3><strong>Workout Completion Overlay</strong></h3><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/fad91b2d-cf7e-4c9f-ae04-bdea02e40767.png\"><img class=\"rounded-lg border max-w-full\" src=\"https://682ccba6ac8c4b22597e6549.featurebase-attachments.com/c/changelog/69961211886ed624b422212b/019c723d-f244-75e2-b595-a62029a60b2f/b64u-U2NyZWVuc2hvdCAyMDI2LTAyLTE4IGF0IDExLjIwLjIz4oCvUE0ucG5n.png?X-Amz-Expires=3600&amp;X-Amz-Date=20260219T110000Z&amp;X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=DO801TYC4FCVNNEKURKM%2F20260219%2Ffra1%2Fs3%2Faws4_request&amp;X-Amz-SignedHeaders=host&amp;X-Amz-Signature=c179909c48ddee02be48e002cce9eb06eff198ef910a5d23562ce78c266ec2ba\" alt=\"\"><p>Added an animated overlay shown at the end of each workout before revealing stats. Displays a badge with confetti, a motivational message, and auto-dismisses after 3.5s (or on tap). The message adapts based on the user's efficiency score:</p><ul><li><p><strong>&lt; 20% efficiency</strong> — <em>\"Good job / You Showed Up\"</em> — encourages consistency over perfection</p></li><li><p><strong>20–89% efficiency</strong> — <em>\"Well done / Crushed It\"</em> — acknowledges a solid effort</p></li><li><p><strong>≥ 90% efficiency</strong> — <em>\"Well done / Legendary Session\"</em> — recognizes near-perfect performance</p></li></ul><p>Includes a progress bar at the bottom indicating time until auto-dismiss. Fully translated across all supported languages.</p><h3><strong>Device-Specific Orientation Video</strong></h3><img class=\"rounded-lg border max-w-full\" src=\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/6328868a-e644-4fc2-9373-2b01edc1afda.png\"><img class=\"rounded-lg border max-w-full\" src=\"https://682ccba6ac8c4b22597e6549.featurebase-attachments.com/c/changelog/69961211886ed624b422212b/019c723e-4696-7bb4-a512-243a6f981805/b64u-U2NyZWVuc2hvdCAyMDI2LTAyLTE4IGF0IDExLjE5LjQw4oCvUE0ucG5n.png?X-Amz-Expires=3600&amp;X-Amz-Date=20260219T110000Z&amp;X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=DO801TYC4FCVNNEKURKM%2F20260219%2Ffra1%2Fs3%2Faws4_request&amp;X-Amz-SignedHeaders=host&amp;X-Amz-Signature=79621c6d315a03289aedfcbfe89870d72a867aab6ce73e7ba30b1efe119c2c8e\" alt=\"\"><p>The accelerometer setup step now shows a <strong>laptop-specific instructional video</strong> on desktop devices instead of the phone video. Mobile devices continue to see the original phone video. Both the preloader and the orientation section detect the device type and load the correct asset.</p>","content":"{\"type\":\"doc\",\"content\":[{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Workout Completion Overlay\"}]},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/fad91b2d-cf7e-4c9f-ae04-bdea02e40767.png\",\"alt\":null,\"title\":null}},{\"type\":\"image\",\"attrs\":{\"src\":\"https://682ccba6ac8c4b22597e6549.featurebase-attachments.com/c/changelog/69961211886ed624b422212b/019c723d-f244-75e2-b595-a62029a60b2f/b64u-U2NyZWVuc2hvdCAyMDI2LTAyLTE4IGF0IDExLjIwLjIz4oCvUE0ucG5n.png?X-Amz-Expires=3600&X-Amz-Date=20260219T110000Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=DO801TYC4FCVNNEKURKM%2F20260219%2Ffra1%2Fs3%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=c179909c48ddee02be48e002cce9eb06eff198ef910a5d23562ce78c266ec2ba\",\"alt\":\"\",\"title\":null}},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Added an animated overlay shown at the end of each workout before revealing stats. Displays a badge with confetti, a motivational message, and auto-dismisses after 3.5s (or on tap). The message adapts based on the user's efficiency score:\"}]},{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"< 20% efficiency\"},{\"type\":\"text\",\"text\":\" — \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"\\\"Good job / You Showed Up\\\"\"},{\"type\":\"text\",\"text\":\" — encourages consistency over perfection\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"20–89% efficiency\"},{\"type\":\"text\",\"text\":\" — \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"\\\"Well done / Crushed It\\\"\"},{\"type\":\"text\",\"text\":\" — acknowledges a solid effort\"}]}]},{\"type\":\"listItem\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"≥ 90% efficiency\"},{\"type\":\"text\",\"text\":\" — \"},{\"type\":\"text\",\"marks\":[{\"type\":\"italic\"}],\"text\":\"\\\"Well done / Legendary Session\\\"\"},{\"type\":\"text\",\"text\":\" — recognizes near-perfect performance\"}]}]}]},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Includes a progress bar at the bottom indicating time until auto-dismiss. Fully translated across all supported languages.\"}]},{\"type\":\"heading\",\"attrs\":{\"level\":3},\"content\":[{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"Device-Specific Orientation Video\"}]},{\"type\":\"image\",\"attrs\":{\"src\":\"https://storage.googleapis.com/kinestex-changelogs.firebasestorage.app/changelog-images/6328868a-e644-4fc2-9373-2b01edc1afda.png\",\"alt\":null,\"title\":null}},{\"type\":\"image\",\"attrs\":{\"src\":\"https://682ccba6ac8c4b22597e6549.featurebase-attachments.com/c/changelog/69961211886ed624b422212b/019c723e-4696-7bb4-a512-243a6f981805/b64u-U2NyZWVuc2hvdCAyMDI2LTAyLTE4IGF0IDExLjE5LjQw4oCvUE0ucG5n.png?X-Amz-Expires=3600&X-Amz-Date=20260219T110000Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=DO801TYC4FCVNNEKURKM%2F20260219%2Ffra1%2Fs3%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=79621c6d315a03289aedfcbfe89870d72a867aab6ce73e7ba30b1efe119c2c8e\",\"alt\":\"\",\"title\":null}},{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"The accelerometer setup step now shows a \"},{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"}],\"text\":\"laptop-specific instructional video\"},{\"type\":\"text\",\"text\":\" on desktop devices instead of the phone video. Mobile devices continue to see the original phone video. Both the preloader and the orientation section detect the device type and load the correct asset.\"}]}]}","updatedAt":"2026-02-19T14:09:38.150Z"}]}