#
Getting Started
This guide walks you through integrating a client application with the sTvOS v1 API -- from your first API call to a fully working user journey.
#
Prerequisites
Before you begin
You need a Client API Key provisioned by the platform admin. This key identifies your client (studio/gym) and scopes all data to your tenant.
Rate limiting
When rate limited, the API returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait. Implement exponential backoff in production clients.
#
Your First API Call
Hit the health endpoint to confirm the API is reachable. No authentication required.
curl https://api.studiostv.net/api/v1/health
const response = await fetch('https://api.studiostv.net/api/v1/health');
const data = await response.json();
console.log(data);
import requests
response = requests.get('https://api.studiostv.net/api/v1/health')
print(response.json())
Expected response:
{
"success": true,
"data": {
"status": "ok",
"timestamp": "2026-02-28T12:00:00Z",
"uptime": 86400,
"version": "1.0.0",
"environment": "development"
}
}
Add the X-API-Key header. Try fetching the app configuration for your client:
curl https://api.studiostv.net/api/v1/app/config \
-H "X-API-Key: your-client-api-key"
This returns your client's branding, enabled features, and registration requirements (e.g., whether a voucher code is needed).
Create a user account under your client:
curl -X POST https://api.studiostv.net/api/v1/auth/register \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-d '{
"email": "test@example.com",
"password": "securepassword",
"name": "Test User"
}'
const response = await fetch('https://api.studiostv.net/api/v1/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your-client-api-key'
},
body: JSON.stringify({
email: 'test@example.com',
password: 'securepassword',
name: 'Test User'
})
});
const { data } = await response.json();
const { accessToken, refreshToken, user } = data;
import requests
response = requests.post(
'https://api.studiostv.net/api/v1/auth/register',
headers={'X-API-Key': 'your-client-api-key'},
json={
'email': 'test@example.com',
'password': 'securepassword',
'name': 'Test User'
}
)
data = response.json()['data']
access_token = data['accessToken']
Save the accessToken and refreshToken from the response. The access token is a short-lived JWT used in the Authorization: Bearer <token> header. The refresh token is long-lived and used to obtain new access tokens.
Use the bearer token to fetch user-specific data:
curl https://api.studiostv.net/api/v1/users/me \
-H "X-API-Key: your-client-api-key" \
-H "Authorization: Bearer <access-token>"
#
Authentication Model
sTvOS uses a layered authentication model. Most endpoints require both layers:
---
config:
theme: base
themeVariables:
primaryColor: "#dbeafe"
primaryTextColor: "#1e3a5f"
primaryBorderColor: "#60a5fa"
secondaryColor: "#f0fdf4"
secondaryTextColor: "#166534"
secondaryBorderColor: "#86efac"
tertiaryColor: "#f8fafc"
tertiaryTextColor: "#475569"
tertiaryBorderColor: "#cbd5e1"
lineColor: "#94a3b8"
fontFamily: "Inter, system-ui, sans-serif"
fontSize: "13px"
---
flowchart LR
subgraph app["Your App"]
M["Mobile / Web Client"]
end
subgraph auth["Authentication Layer"]
KEY["X-API-Key -- Identifies Client"]
JWT["Bearer Token -- Identifies User"]
end
subgraph scoped["Client-Scoped Data"]
direction TB
C1["Content & Classes"]
C2["Trainers & Bookings"]
C3["AI Plans & Progress"]
C4["Voice AI Sessions"]
end
M -->|"every request"| KEY
M -->|"user requests"| JWT
KEY --> scoped
JWT --> scoped
#
Token Refresh
Access tokens expire. Use the refresh token to get a new one without re-authenticating:
curl -X POST https://api.studiostv.net/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-d '{ "refreshToken": "eyJhbGciOi..." }'
#
Voucher-Based Registration
The voucher registration flow from the app's perspective:
---
config:
theme: base
themeVariables:
primaryColor: "#dbeafe"
primaryTextColor: "#1e3a5f"
primaryBorderColor: "#60a5fa"
actorBkg: "#f8fafc"
actorBorder: "#94a3b8"
actorTextColor: "#1e293b"
signalColor: "#475569"
signalTextColor: "#475569"
labelBoxBkgColor: "#f8fafc"
labelBoxBorderColor: "#e2e8f0"
loopTextColor: "#475569"
activationBorderColor: "#94a3b8"
activationBkgColor: "#f1f5f9"
noteBkgColor: "#f0f9ff"
noteBorderColor: "#bae6fd"
noteTextColor: "#0c4a6e"
fontFamily: "Inter, system-ui, sans-serif"
fontSize: "13px"
---
sequenceDiagram
participant App as Mobile App
participant API as sTvOS API
participant DB as Client Store
App->>API: GET /app/config
API-->>App: branding, requiresVoucher: true
App->>API: POST /auth/voucher/validate
Note right of App: { code: "STUDIO-ABC" }
API-->>App: valid: true, partner info
App->>API: POST /auth/register
Note right of App: { email, password,<br/>voucherCode: "STUDIO-ABC" }
API->>DB: Create user, assign to partner client
API->>DB: Redeem voucher
API-->>App: user + accessToken + refreshToken
App->>API: GET /content/discover
Note right of App: X-API-Key + Bearer token
API-->>App: Client-scoped content feedIf your client requires voucher codes for registration (common for partner-managed access), the flow adds a validation step:
Check if the code is valid before showing the registration form:
curl -X POST https://api.studiostv.net/api/v1/auth/voucher/validate \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-d '{ "code": "STUDIO-ABC123" }'
The response includes the partner name and client information.
Include the voucher code in the registration request:
curl -X POST https://api.studiostv.net/api/v1/auth/register \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-d '{
"email": "user@example.com",
"password": "securepassword",
"name": "Jane Doe",
"voucherCode": "STUDIO-ABC123"
}'
The voucher is redeemed and the user is mapped to the partner's client.
Deep links
sTvOS supports deep links for voucher redemption. When a user opens studiostv://join/VOUCHER-CODE, call GET /api/v1/app/join/VOUCHER-CODE to validate the code and retrieve the client branding -- no authentication required.
#
Onboarding Flow
---
config:
theme: base
themeVariables:
primaryColor: "#dbeafe"
primaryTextColor: "#1e3a5f"
primaryBorderColor: "#60a5fa"
secondaryColor: "#f0fdf4"
secondaryTextColor: "#166534"
secondaryBorderColor: "#86efac"
tertiaryColor: "#f8fafc"
tertiaryTextColor: "#475569"
tertiaryBorderColor: "#cbd5e1"
lineColor: "#94a3b8"
fontFamily: "Inter, system-ui, sans-serif"
fontSize: "13px"
---
flowchart LR
subgraph voice["Voice AI (optional)"]
V["LiveKit Token"] --> H["Hume EVI Conversation"]
end
subgraph onboard["Onboarding (pre-registration)"]
direction LR
P["Save Profile"] --> R["Recommend Coaches"]
R --> S["Select Coach"]
end
subgraph reg["Registration"]
REG["POST /auth/register\n+ sessionId + coachId"]
end
voice -.->|"collects profile"| onboard
onboard --> reg
reg --> DONE(("Account\nCreated"))The onboarding flow captures user profile data before account creation. This data is stored temporarily and merged into the user account on registration.
Collect fitness profile data and store it with a temporary session ID:
curl -X POST https://api.studiostv.net/api/v1/onboarding/profile \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-d '{
"sessionId": "unique-session-id",
"name": "Jane",
"age": 28,
"gender": "female",
"height": 170,
"weight": 65,
"fitnessGoal": "weight_loss",
"fitnessLevel": "intermediate",
"workoutsPerWeek": 4
}'
Session expiry
Onboarding profiles expire after 24 hours. The user must complete registration before then.
Based on the stored profile, get a ranked list of recommended coaches:
curl -X POST https://api.studiostv.net/api/v1/onboarding/recommend-coaches \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-d '{
"sessionId": "unique-session-id",
"fitnessGoal": "weight_loss",
"fitnessLevel": "intermediate"
}'
Save the user's coach selection:
curl -X POST https://api.studiostv.net/api/v1/onboarding/select-coach \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-d '{ "sessionId": "unique-session-id", "coachId": 5 }'
Register the user, passing the session ID and coach ID so the profile is merged:
curl -X POST https://api.studiostv.net/api/v1/auth/register \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-d '{
"email": "user@example.com",
"password": "securepassword",
"name": "Jane",
"sessionId": "unique-session-id",
"coachId": 5
}'
#
Voice AI Integration
sTvOS integrates with Hume's Empathic Voice Interface for conversational AI coaching. Voice sessions run through LiveKit rooms.
Fetch the voice AI config for your client:
curl https://api.studiostv.net/api/v1/hume-config \
-H "X-API-Key: your-client-api-key"
Create a room token. For anonymous onboarding sessions, omit the bearer token:
curl -X POST https://api.studiostv.net/api/v1/livekit/token \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-d '{
"roomName": "onboarding-session",
"context": "User is starting their fitness journey",
"agentName": "fitness-coach"
}'
Use the returned token and wsUrl to connect your client to the LiveKit room. The Hume EVI agent will join automatically and can query the user's plans, recommend classes, and book sessions through tool calls.
#
Content Discovery
#
Building the Home Screen
curl https://api.studiostv.net/api/v1/content/discover \
-H "X-API-Key: your-client-api-key" \
-H "Authorization: Bearer <token>"
Returns a structured response with:
- Featured classes -- editorially highlighted content
- Continue watching -- classes the user has started but not finished
- Upcoming live -- scheduled live classes
- Categories -- content categories assigned to your client
- New this week -- recently added classes
#
Session Tracking
Track viewing progress by sending heartbeats every ~30 seconds:
curl -X POST https://api.studiostv.net/api/v1/sessions/start \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-H "Authorization: Bearer <token>" \
-d '{ "classId": 42, "deviceType": "mobile" }'
curl -X POST https://api.studiostv.net/api/v1/sessions/<session-id>/progress \
-H "Content-Type: application/json" \
-H "X-API-Key: your-client-api-key" \
-H "Authorization: Bearer <token>" \
-d '{ "progressSeconds": 540, "totalDurationSeconds": 1800 }'
Sessions auto-complete at 90% progress. You can also manually complete with POST /sessions/<id>/complete.
#
Error Handling
All errors return a consistent JSON structure:
{
"error": "Human-readable description",
"code": "MACHINE_READABLE_CODE",
"details": "Additional context"
}
Handling 401 responses
The most common cause of 401 is an expired access token. Implement a token refresh interceptor in your HTTP client: when you receive a 401, call POST /auth/refresh with the stored refresh token, then retry the original request with the new access token.
#
Frequently Asked Questions
Can I serve multiple clients from a single mobile app?
Yes. Use the universal API key for registration and login. When a user registers with a voucher code, they are mapped to the voucher's partner client. Login responses include a client object with branding information so the app can adapt its UI dynamically.
Do users need a voucher code to register?
It depends on the client configuration. The GET /app/config endpoint returns a requiresVoucher flag. Some clients allow open registration; others require a valid voucher code.
How do I handle token expiry?
Access tokens are short-lived. When you receive a 401 response, call POST /auth/refresh with the stored refresh token to get a new access token without re-authenticating the user.
Can trainers have different voice AI personalities?
Yes. Each trainer can have a separate Hume chat configuration ID. Use PUT /hume-config/trainers to set per-trainer configs. The voice agent adapts its personality and context based on which trainer the user is matched with.
#
Next Steps