FittyAI Integration — React Native

FittyAI lets you embed a fully‑featured virtual trainer inside your application in minutes. Integration takes just:

  1. Build the workout URL
  2. Drop it into a suitable container
  3. Listen for a handful of Event Bridge messages

Prerequisites

Credential How to obtain
clientId Given during onboarding
secret Given during onboarding (used to sign your JWT)

To get workoutId either fetch possible workouts using Workouts API or store enabled for your client workouts in your storage.

Project Setup

npx react-native init FittyDemo
cd FittyDemo
npm install react-native-webview uuid

Inject env variables with react-native-config or pass as props.

Preparation

⚠️ Important: Acquiring the JWT must be managed on your backend server. It’s essential because this process involves sending your secret. For security, never store or send secrets from a client device.

Read the instructions to receive your JWT here.

Upon retrieval, the JWT will resemble:

    const userToken = "eyJhbGciOiJIUzI1NiIsInR5cCI...";
    import { v4 as uuidv4 } from "uuid";
    const sessionId = uuidv4();
    const env = "prod";

Embedding the Player (React Native)

import React, { useRef } from 'react';
import { View, StyleSheet } from 'react-native';
import { WebView } from 'react-native-webview';

export default function FittyWorkoutMobile({ workoutId }) {
  const webRef = useRef(null);

  const url = `https://${env}.fittyai.com/web/${clientId}/${workoutId}/${sessionId}?JWT=${userToken}`;

  const onMessage = event => {
    const data = JSON.parse(event.nativeEvent.data || '{}');
    if (data.type === 'sessionEnd') {
      // fetch results using sessionId and our session API endpoint
    }
  };

  return (
    <View style={styles.container}>
      <WebView
        ref={webRef}
        source=
        style={styles.webview}           // full-screen
        javaScriptEnabled
        originWhitelist={[`https://${env}.fittyai.com`]}
        onMessage={onMessage}
        allowsInlineMediaPlayback
        mediaPlaybackRequiresUserAction={false}
        allowsFullscreenVideo
        // Autogrant camera permission (Android 12+) if host matches:
        mediaCapturePermissionGrantType="grantIfSameHostElsePrompt"
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#000' },
  webview:  { flex: 1 },
});


Event Bridge

Event Payload When
initiationCompleted { type: "initiationCompleted" } After internal bootstrap
indexUpdated { type: "indexUpdated", index: number } Whenever the active exercise changes
closeRequested { type: "closeRequested" } User hit X and the URL contained suppressFinish=true
sessionEnd { type: "sessionEnd" } Workout finished or host called nativeBridge.close()

Fetching the Results

After sessionEnd call the Get Session endpoint:

GET /v1/{clientName}/sessions/{sessionId}
fetch(`/v1/<clientId>/sessions/${sessionId}`, {
  headers: { Authorization: 'Bearer <jwt>' }
}).then(r => r.json()).then(console.log);

See the full schema in the Get Session docs.

Optional URL Query Parameters

Add these only when you need them—they all work independently.

Parameter Purpose Example
eventIndex Load the workout starting from a specific exercise ?eventIndex=5
suppressFinish Use suppressFinish=true to disable our workout summary screen and show your custom summary screen; emits closeRequested instead ?suppressFinish=true

Simply append one or more to the iframe URL (order doesn’t matter).