3.3. Exercise API Endpoints
Base URL: https://api.platform.hemscap.com
A. Exercise Lifecycle Management
A.1. Create Exercise
/api/platform/portal/info-swagger/exercise/create
Summary: Initializes a new, empty exercise document. It generates a unique exerciseKey, sets the version to 1, assigns the standing posture, and binds ownership to the authenticated user.
interface CreateExerciseDtoIn {
title: string;
description: string;
}
interface CreateExerciseDtoOut {
exerciseKey: string;
}
async function createExercise(payload: CreateExerciseDtoIn): Promise<CreateExerciseDtoOut> {
const response = await fetch(`https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/create`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', /* Authorization: Bearer <TOKEN> */ },
body: JSON.stringify(payload)
});
return await response.json();
}A.2. Get My Exercises
/api/platform/portal/info-swagger/exercise/me
Summary: Returns a paginated and filtered list of exercises exclusively owned by the authenticated user.
type CameraType = 'left' | 'front' | 'right';
type PostureType = 'standing' | 'sitting' | 'sleeping1' | 'sleeping2';
type ExerciseTypeFilter = 'face' | 'distance' | 'range_of_motion';
type PushTimelineMode = 'pose-changed' | 'fps';
interface ExerciseFilterDtoIn {
label?: string;
offset?: number;
limit?: number;
camera?: CameraType;
posture?: PostureType;
exerciseType?: ExerciseTypeFilter;
targetJoint?: Array<string> | string;
pushTimelineMode?: PushTimelineMode;
}
async function getMyExercises(filters: ExerciseFilterDtoIn): Promise<[any[], number]> {
const query = new URLSearchParams(filters as any).toString();
const response = await fetch(`https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/me?${query}`);
return await response.json();
}A.3. Get Exercise
/api/platform/portal/info-swagger/exercise/{exerciseKey}
Summary: Fetches the full exercise document.
- Ownership: Validates ownership. Returns
404 Not Foundif the user does not own the exercise. - AppToken Behavior: If requested via an
AppToken, thelabelanddescriptionfields are stripped from the response.
interface ExerciseDocument {
exerciseKey: string;
label?: string; // Omitted if requested via AppToken
description?: string; // Omitted if requested via AppToken
cameras: Record<string, any>;
version: number;
posture: string;
authUserId: number;
options?: any;
}
async function getExercise(exerciseKey: string): Promise<Partial<ExerciseDocument>> {
const response = await fetch(`https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/${exerciseKey}`);
return await response.json();
}A.4. Append (Clone) Exercise
/api/platform/portal/info-swagger/exercise/append-exercise/{exerciseKey}
Summary: Clones an existing exercise and assigns ownership of the new clone to the authenticated user.
async function appendExercise(exerciseKey: string): Promise<string> {
const response = await fetch(`https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/append-exercise/${exerciseKey}`, {
method: 'POST'
});
return await response.text();
}A.5. Delete My Exercise
/api/platform/portal/info-swagger/exercise/{exerciseKey}
Summary: Permanently deletes the specified exercise. Only the owner can perform this action.
async function deleteExercise(exerciseKey: string): Promise<void> {
const response = await fetch(`https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/${exerciseKey}`, {
method: 'DELETE',
headers: { /* Authorization: Bearer <TOKEN> */ }
});
if (!response.ok) {
throw new Error('Failed to delete exercise');
}
}B. Search & Metadata
B.1. Search Exercises
/api/platform/portal/info-swagger/exercise/search
Summary: Searches globally across the exercise engine using filters like posture, camera, or target joints.
async function searchExercises(filters: ExerciseFilterDtoIn): Promise<[any[], number]> {
const query = new URLSearchParams(filters as any).toString();
const response = await fetch(`https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/search?${query}`);
return await response.json();
}B.2. Get Exercise Info
/api/platform/portal/info-swagger/exercise/{exerciseKey}/info
Summary: Returns lightweight metadata (primary camera, pose count, and model type) without loading the full exercise document.
interface GetInfoDtoOut {
camera?: 'left' | 'front' | 'right';
posesCount: number;
model?: string;
}
async function getExerciseInfo(exerciseKey: string): Promise<GetInfoDtoOut> {
const response = await fetch(`https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/${exerciseKey}/info`);
return await response.json();
}C. Media Management (Images)
C.1. Get Image by Pose
/api/platform/portal/info-swagger/exercise/get-image-by-pose/{exerciseKey}/{poseId}
Summary: Retrieves the contour image buffer for a specific pose index on the exercise's primary camera.
async function getImageByPose(exerciseKey: string, poseId: number): Promise<Blob> {
const response = await fetch(`https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/get-image-by-pose/${exerciseKey}/${poseId}`);
if (!response.ok) throw new Error('Failed to fetch image');
return await response.blob();
}C.2. Get Pose Image (Specific Camera Angle)
/api/platform/portal/info-swagger/exercise/get-pose-image/{exerciseKey}/{cameraMode}/{poseId}
Summary: Fetches the contour image for a specific camera angle and pose ID. Returns an exercise-version header.
type CameraMode = 'front' | 'left' | 'right';
async function getPoseImage(exerciseKey: string, cameraMode: CameraMode, poseId: number): Promise<Blob> {
const response = await fetch(`https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/get-pose-image/${exerciseKey}/${cameraMode}/${poseId}`);
if (!response.ok) throw new Error('Image not found');
return await response.blob();
}C.3. Publish Custom Animation/Video
/api/platform/portal/info-swagger/exercise/{exerciseKey}/publish-movie
Summary: Uploads a custom video or animation (typically .webm format) to serve as the instructional media for the specified exercise. Max file size is 10MB.
async function publishMovie(exerciseKey: string, videoFile: File): Promise<{ url: string, key: string }> {
const formData = new FormData();
formData.append('file', videoFile);
const response = await fetch(`https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/${exerciseKey}/publish-movie`, {
method: 'POST',
// Authorization headers omitted for brevity
body: formData
});
if (!response.ok) throw new Error('Upload failed');
return await response.json();
}C.4. Stream/Retrieve Exercise Video
/api/platform/portal/info-swagger/exercise/{exerciseKey}/get-movie
Summary: Retrieves the published video for an exercise. The endpoint supports HTTP range requests (byte-serving) enabling seeking, scrubbing, and smooth playback directly in html video players.
src attribute of an HTML <video> element. It handles long-term caching and proper MIME types (video/webm) automatically. If no video exists, it returns a 404 Not Found.<!-- Usage in HTML/React -->
<video controls>
<source
src="https://api.platform.hemscap.com/api/platform/portal/info-swagger/exercise/{exerciseKey}/get-movie"
type="video/webm"
/>
</video>