# Getting Started

Integrate your app with sTvOS in minutes.

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

Requirement Value
Base URL https://api.studiostv.net/api/v1
Required Header X-API-Key: <your-client-api-key>
Content Type application/json for all request and response bodies
Rate Limit 100 requests per minute per API key (burst: 20)

# 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
Layer Purpose How to Get It
Client API Key Identifies your client tenant Provisioned by the platform admin
Access Token Identifies the authenticated user Returned by /auth/login or /auth/register
Refresh Token Obtains new access tokens Returned alongside the access token

# 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 feed

If 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.


# 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
  }'

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"
}
Status Meaning Common Cause
400 Bad Request Invalid request body, missing required fields, validation failure
401 Unauthorized Missing X-API-Key, expired bearer token, invalid credentials
403 Forbidden Valid auth but insufficient role (e.g. non-trainer accessing trainer portal)
404 Not Found Resource does not exist or belongs to a different client
409 Conflict Duplicate resource (e.g. registering with an existing email)
429 Rate Limited Too many requests -- check the Retry-After response header
500 Server Error Internal error -- contact support if persistent

# Frequently Asked Questions


# Next Steps