TypeScript SDK
Package @trainheroic-unofficial/js wraps auth, workouts, roster helpers, athlete reads, analytics, and messaging. Types ship in @trainheroic-unofficial/dto. The runtime runs on Node, workerd, and browsers.
Install
npm install @trainheroic-unofficial/jsCoach workflow
import {
TrainHeroicClient,
ExerciseLibrary,
buildSession,
fetchCoachRoster,
} from "@trainheroic-unofficial/js";
import { JsonFileLibraryCache } from "@trainheroic-unofficial/js/node";
const client = new TrainHeroicClient(email, password);
const library = new ExerciseLibrary(client, new JsonFileLibraryCache());
for (const athlete of await fetchCoachRoster(client)) {
console.log(athlete.name, athlete.id);
}
const { match } = await library.resolve("Back Squat");
if (!match) throw new Error("Resolve to one exercise first");
const { pwId } = await buildSession(client, {
programId: 12345, // from list_teams → group_program
date: [2026, 6, 22],
blocks: [{
title: "Strength",
exercises: [{ id: match.id, sets: 5, reps: 5, weight: 225, rpe: 8 }],
}],
publish: false,
});Athlete reads
import {
TrainHeroicClient,
resolveAthleteUserId,
fetchAthleteProfileSummary,
searchExerciseHistory,
fetchExerciseHistoryDetail,
fetchPersonalRecords,
} from "@trainheroic-unofficial/js";
const client = new TrainHeroicClient(email, password);
const userId = await resolveAthleteUserId(client);
const profile = await fetchAthleteProfileSummary(client, userId);
console.log(profile.volume_sum);
const [squat] = await searchExerciseHistory(client, "back squat", 1);
if (squat) {
const id = Number(squat.id);
console.log(await fetchExerciseHistoryDetail(client, id, userId));
console.log(await fetchPersonalRecords(client, id));
}Analytics
Analytics reports are read-only data pulls. TrainHeroic uses POST for these queries, but they do not mutate anything.
import {
TrainHeroicClient,
analyticsMetricCatalog,
queryAnalytics,
} from "@trainheroic-unofficial/js";
const client = new TrainHeroicClient(email, password);
// Read-only report (TrainHeroic uses POST for these queries).
const readiness = await queryAnalytics(client, {
metric: "readiness-team",
teamId: 42,
date: "2026-06-22",
});
console.log(readiness);
// Scope + required params for every metric key:
console.log(analyticsMetricCatalog());Messaging
import {
TrainHeroicClient,
fetchStreams,
sendComment,
} from "@trainheroic-unofficial/js";
const client = new TrainHeroicClient(email, password);
const team = (await fetchStreams(client)).find((s) => s.kind === "team");
if (team && typeof team.stream.id === "number") {
const comment = await sendComment(client, team.stream.id, "Great week.");
console.log(comment);
}