Skip to content
StudyFetch API

API References

Audio Recap

Overview

The Audio Recap component converts your study materials into audio summaries, perfect for reviewing content during commutes, workouts, or when visual reading isn't practical. Supports multiple voices and languages.

When topic is set and folders are included, generation can narrow folder-derived materials toward that topic (best-effort). Use excludedMaterialIds to omit specific materials that would otherwise be included from selected folders; IDs listed here are ignored if the same material is also listed in materials (explicit selections win).

You can also create a recap from a topic alone, with no materials or folders. A topic-only recap is generated from the model's own knowledge of the topic, so its sections have no source citations. A component needs at least one material, one folder, or a non-empty topic; otherwise the request fails with Component requires at least one material or folder.

Creating an Audio Recap Component

JavaScript

import StudyfetchSDK from '@studyfetch/sdk';const client = new StudyfetchSDK({
  apiKey: 'your-api-key',
  baseURL: 'https://studyfetchapi.com',
});

const audioComponent = await client.v1.components.create({
  name: 'Biology Chapter Audio Summary',
  type: 'audio_recap',
  config: {
    materials: ['mat-123', 'mat-456'],
    folders: ['folder-789'],
    excludedMaterialIds: ['mat-skip-from-folder'],
    recapType: 'SUMMARY',
    duration: 10,
    numParts: 3,
    isMultiVoice: true,
    voice1: 'Morgan',
    voice2: 'Riley',
    model: 'gpt-4.1-2025-04-14',
    topic: 'Cell Biology',
    theme: 'Educational podcast style'
  }
});

console.log('Audio recap component created:', audioComponent._id);

Python

from studyfetch_sdk import StudyfetchSDK

client = StudyfetchSDK(
    api_key="your-api-key",
    base_url="https://studyfetchapi.com",
)

audio_component = client.v1.components.create(
    name="Biology Chapter Audio Summary",
    type="audio_recap",
    config={
        "materials": ["mat-123", "mat-456"],
        "folders": ["folder-789"],
        "excluded_material_ids": ["mat-skip-from-folder"],
        "recap_type": "SUMMARY",
        "duration": 10,
        "num_parts": 3,
        "is_multi_voice": True,
        "voice1": "Morgan",
        "voice2": "Riley",
        "model": "gpt-4.1-2025-04-14",
        "topic": "Cell Biology",
        "theme": "Educational podcast style"
    }
)

print(f"Audio recap component created: {audio_component.api_id}")

Java

import com.studyfetch.javasdk.client.StudyfetchSdkClient;
import com.studyfetch.javasdk.client.okhttp.StudyfetchSdkOkHttpClient;
import com.studyfetch.javasdk.models.v1.components.ComponentResponse;
import com.studyfetch.javasdk.models.v1.components.ComponentCreateParams;

import java.util.List;

public class CreateAudioRecapComponent {
    public static void main(String[] args) {
        StudyfetchSdkClient client = StudyfetchSdkOkHttpClient.builder()
            .fromEnv()
            .baseUrl("https://studyfetchapi.com")
            .build();

        ComponentCreateParams params = ComponentCreateParams.builder()
            .name("Biology Chapter Audio Summary")
            .type(ComponentCreateParams.Type.AUDIO_RECAP)
            .config(ComponentCreateParams.Config.AudioRecapConfigDto.builder()
                .materials(List.of("mat-123", "mat-456"))
                .folders(List.of("folder-789"))
                .excludedMaterialIds(List.of("mat-skip-from-folder"))
                .recapType(ComponentCreateParams.Config.AudioRecapConfigDto.RecapType.SUMMARY)
                .duration(10)
                .numParts(3)
                .isMultiVoice(true)
                .voice1(ComponentCreateParams.Config.AudioRecapConfigDto.AudioRecapVoice.MORGAN)
                .voice2(ComponentCreateParams.Config.AudioRecapConfigDto.AudioRecapVoice.RILEY)
                .model("gpt-4.1-2025-04-14")
                .topic("Cell Biology")
                .theme("Educational podcast style")
                .build())
            .build();

        ComponentResponse component = client.v1().components().create(params);
        System.out.println("Audio recap component created: " + component._id());
    }
}

C#

using StudyfetchSDK;
using StudyfetchSDK.Models.V1.Components;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using RecapType = StudyfetchSDK.Models.V1.Components.ComponentCreateParamsProperties.ConfigProperties.AudioRecapConfigDtoProperties.RecapType;
using AudioRecapVoice = StudyfetchSDK.Models.V1.Components.ComponentCreateParamsProperties.ConfigProperties.AudioRecapConfigDtoProperties.AudioRecapVoice;

public class CreateAudioRecapComponent
{
    public static async Task CreateAudioRecap()
    {
        var client = new StudyfetchSDKClient()
        {
            APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
            BaseUrl = new Uri("https://studyfetchapi.com")
        };

        var audioComponent = await client.V1.Components.Create(new()
        {
            Name = "Biology Chapter Audio Summary",
            Type = StudyfetchSDK.Models.V1.Components.ComponentCreateParamsProperties.Type.AudioRecap,
            Config = new StudyfetchSDK.Models.V1.Components.ComponentCreateParamsProperties.ConfigProperties.AudioRecapConfigDto()
            {
                Materials = new List<string> { "mat-123", "mat-456" },
                Folders = new List<string> { "folder-789" },
                ExcludedMaterialIds = new List<string> { "mat-skip-from-folder" },
                RecapType = RecapType.Summary,
                Duration = 10,
                NumParts = 3,
                IsMultiVoice = true,
                Voice1 = AudioRecapVoice.Morgan,
                Voice2 = AudioRecapVoice.Riley,
                Model = "gpt-4.1-2025-04-14",
                Topic = "Cell Biology",
                Theme = "Educational podcast style",
            }
        });

        Console.WriteLine($"Audio recap component created: {audioComponent._ID}");
    }
}

Configuration Parameters

name

string · required

Name of the audio recap component

type

string · required

Must be "audio_recap"

config

object · required

Audio recap configuration object. Provide at least one of materials, folders, or topic.

Configuration Properties

These properties go inside config.

materials

array

Array of material IDs to generate audio from. Optional when folders or topic is set.

folders

array

Array of folder IDs containing materials

excludedMaterialIds

array

Material IDs to exclude when expanding selected folders. Has no effect on materials you list directly in materials; if an ID appears in both materials and excludedMaterialIds, the explicit materials entry wins.

recapType

string · default: SUMMARY

Type of audio recap to generate:

  • SUMMARY - Concise summary highlighting important points
  • LECTURE - Formal academic lecture style
  • PODCAST - Conversational podcast-style discussion
  • AUDIO_BOOK - Detailed audio book narration

duration

number · default: 10

Target duration in minutes for the audio recap (affects content depth). Longer durations also raise the amount of source material included before truncation, see the source-material budget table in the Overview.

numParts

number · default: 3

Number of sections to divide the content into

isMultiVoice

boolean · default: false

Whether to use multiple voices for a conversational format

voice1

string · default: Morgan

Primary narrator voice. One of:

  • Alex (female)
  • Morgan (male) - Default
  • Riley (female)
  • Jordan (female)
  • Sparky - StudyFetch's mascot voice; uses its own intro lines
  • Taylor (male)

Older values are still accepted and mapped to the voices above, but new integrations should use these names.

voice2

string · default: Riley

Secondary voice for multi-voice conversations (used when isMultiVoice is true). Accepts the same values as voice1.

model

string · default: gemini-3-flash-preview

AI model to use for content generation

topic

string

Specific topic to focus on within the materials. Together with folders, this can steer which folder-derived materials are used (best-effort relevance filtering). With no materials or folders, the recap is generated from the topic alone.

theme

string

Style or theme for the audio content (e.g., "casual educational", "formal academic")

Response

{
  "_id": "comp_202mno",
  "name": "Biology Chapter Audio Summary",
  "type": "audio_recap",
  "status": "processing",
  "config": {
    "materials": ["mat-123", "mat-456"],
    "folders": ["folder-789"],
    "excludedMaterialIds": ["mat-skip-from-folder"],
    "recapType": "SUMMARY",
    "duration": 10,
    "numParts": 3,
    "isMultiVoice": true,
    "voice1": "Puck",
    "voice2": "Aoede",
    "model": "gpt-4.1-2025-04-14",
    "topic": "Cell Biology",
    "theme": "Educational podcast style"
  },
  "createdAt": "2024-01-15T10:00:00Z",
  "updatedAt": "2024-01-15T10:00:00Z",
  "organizationId": "org_456def",
  "audioFile": {
    "url": null,
    "duration": null,
    "size": null
  }
}

Embedding This Component

Once you've created an Audio Recap component, you can embed it on your website using the embedding API.

Generate Embed URL

JavaScript

const embedResponse = await client.v1.components.generateEmbed(audioComponent._id, {
  // User tracking
  userId: 'user-456',
  studentName: 'Jane Smith',  // Student name for display
  groupIds: ['class-101', 'class-102'],
  sessionId: 'session-789',  // Audio-specific features
  features: {
    enableBadWordsFilter: true,  // Required by the SDK type
    enableTranscript: true,
    enableOutline: true,
    enableHistory: true
  },

  // Dimensions
  width: '100%',
  height: '400px',

  // Token expiry
  expiryHours: 24
});

Python

embed_response = client.v1.components.generate_embed(
    audio_component.api_id,
    user_id="user-456",
    student_name="Jane Smith",  # Student name for display
    group_ids=["class-101", "class-102"],
    session_id="session-789",
    features={
        "enable_bad_words_filter": True,  # Required by the SDK type
        "enable_transcript": True,
        "enable_outline": True,
        "enable_history": True
    },
    width="100%",
    height="400px",
    expiry_hours=24
)

Java

import com.studyfetch.javasdk.client.StudyfetchSdkClient;
import com.studyfetch.javasdk.client.okhttp.StudyfetchSdkOkHttpClient;
import com.studyfetch.javasdk.models.v1.components.ComponentGenerateEmbedParams;
import com.studyfetch.javasdk.models.v1.components.ComponentGenerateEmbedResponse;

StudyfetchSdkClient client = StudyfetchSdkOkHttpClient.builder()
    .fromEnv()
    .baseUrl("https://studyfetchapi.com")
    .build();

ComponentGenerateEmbedParams params = ComponentGenerateEmbedParams.builder()
    .id(audioComponent._id())
    // User tracking
    .userId("user-456")
    .studentName("Jane Smith")  // Student name for display
    .groupIds(List.of("class-101", "class-102"))
    .sessionId("session-789")
    
    // Audio-specific features
    .features(ComponentGenerateEmbedParams.Features.builder()
        .enableBadWordsFilter(true)  // Required: build() throws without it
        .enableTranscript(true)
        .enableOutline(true)
        .enableHistory(true)
        .build())
    
    // Dimensions
    .width("100%")
    .height("400px")
    
    // Token expiry
    .expiryHours(24)
    .build();

ComponentGenerateEmbedResponse embedResponse = client.v1().components()
    .generateEmbed(params);

C#

using StudyfetchSDK;
using StudyfetchSDK.Models.V1.Components;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class GenerateAudioRecapEmbed
{
    public static async Task GenerateEmbed()
    {
        var client = new StudyfetchSDKClient()
        {
            APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
            BaseUrl = new Uri("https://studyfetchapi.com")
        };

        var embedResponse = await client.V1.Components.GenerateEmbed(new()
        {
            ID = "component_123abc", // Replace with your component ID
            UserID = "user-456",
            StudentName = "Jane Smith",  // Student name for display
            GroupIDs = new List<string> { "class-101", "class-102" },
            Width = "100%",
            Height = "600px"
        });

        Console.WriteLine($"Embed URL: {embedResponse.EmbedURL}");
        Console.WriteLine($"Token: {embedResponse.Token}");
    }
}

Audio Recap-Specific Embedding Features

features.enableTranscript

boolean · default: true

Show synchronized transcript alongside the audio player

features.enableOutline

boolean · default: true

Display chapter markers and content outline for easy navigation

features.enableHistory

boolean · default: true

Remember playback position and show listening history

Generation Status

Recaps are generated in the background after the component is created. Use the generation status endpoint to poll from your server until the recap is ready. Generation has two stages:

Stage Fields Values
Section scripts recap.generationStatus, recap.generationCompleted, recap.generationError pending → completed, or failed with generationError
Section audio sections[].generated, sections[].generating, sections[].errorMsg Per section: generating while text-to-speech runs, generated once audio is ready, errorMsg if it failed

recap is null until generation has created the recap record, so keep polling until it is present. The componentId can be the public component ID (for example, audio_recap_abc123) or the component's _id. A component that is not an audio recap in your organization returns 404.

JavaScript

const status = await client.v1.audioRecaps.retrieveGenerationStatus('audio_recap_abc123');if (status.recap?.generationStatus === 'failed') {
  console.error(status.recap.generationError);
} else {
  const ready = status.sections.filter((s) => s.generated).length;
  console.log(`${ready} of ${status.sections.length} sections ready`);
}

Python

status = client.v1.audio_recaps.retrieve_generation_status("audio_recap_abc123")

if status.recap and status.recap.generation_status == "failed":
    print(status.recap.generation_error)
else:
    ready = sum(1 for s in status.sections if s.generated)
    print(f"{ready} of {len(status.sections)} sections ready")

Java

import com.studyfetch.javasdk.models.v1.audiorecaps.AudioRecapGenerationStatusResponse;

AudioRecapGenerationStatusResponse status = client.v1().audioRecaps()
    .retrieveGenerationStatus("audio_recap_abc123");

status.recap().ifPresent(recap -> System.out.println(recap.generationStatus()));
long ready = status.sections().stream().filter(s -> s.generated()).count();
System.out.println(ready + " of " + status.sections().size() + " sections ready");

C#

using System.Linq;

var status = await client.V1.AudioRecaps.RetrieveGenerationStatus(new()
{
    ComponentID = "audio_recap_abc123",
});

Console.WriteLine(status.Recap?.GenerationStatus);
Console.WriteLine($"{status.Sections.Count(s => s.Generated)} of {status.Sections.Count} sections ready");

cURL

curl "https://studyfetchapi.com/api/v1/audio-recaps/components/audio_recap_abc123" \
  -H "x-api-key: your-api-key"

Response

{
  "recap": {
    "id": "recap-id-1",
    "title": "Biology Chapter Audio Summary",
    "generationCompleted": true,
    "generationStatus": "completed"
  },
  "sections": [
    { "id": "section-id-1", "order": 0, "generated": true, "generating": false },
    { "id": "section-id-2", "order": 1, "generated": false, "generating": true },
    { "id": "section-id-3", "order": 2, "generated": false, "generating": false, "errorMsg": "..." }
  ]
}

User Progress & Status Tracking

Audio Recap embeds can track how far each learner has listened through a recap. Progress is tracked per user, per section, and can be grouped by class or cohort using groupIds.

Progress is recorded only when the embed token includes a userId. Anonymous embeds do not create progress records.

How Tracking Works

When you generate an embed URL, include one userId for the learner viewing that iframe:

{
  "userId": "student-001",
  "groupIds": ["class-101"]
}

One embed session should represent one learner. To track a full class, generate one embed token per learner with a unique userId and shared groupIds.

The Audio Recap player reports:

  • lastPosition, the learner's last known playback position for a section, in seconds. Only moves forward; a lower position does not overwrite a higher one.
  • completed, whether the learner completed a section. Once a section is completed it stays completed.
  • groupIds, group or class IDs supplied when generating the embed

List Progress for a Recap

Use the audio recap componentId (for example, audio_recap_abc123) to list all users with recorded progress, most recently active first. Filter with groupId or userId, and page with limit and skip.

JavaScript

const progress = await client.v1.audioRecaps.listProgress('audio_recap_abc123', {
  groupId: 'class-101',
});console.log(progress.users);

Python

progress = client.v1.audio_recaps.list_progress(
    "audio_recap_abc123",
    group_id="class-101",
)

print(progress.users)

Java

var progress = client.v1().audioRecaps().listProgress(
    "audio_recap_abc123",
    AudioRecapListProgressParams.builder()
        .groupId("class-101")
        .build()
);

System.out.println(progress.users());

C#

var progress = await client.V1.AudioRecaps.ListProgress(new()
{
    ComponentID = "audio_recap_abc123",
    GroupID = "class-101",
});

Console.WriteLine(progress.TotalUsers);

cURL

curl "https://studyfetchapi.com/api/v1/audio-recaps/progress/audio_recap_abc123?groupId=class-101" \
  -H "x-api-key: your-api-key"

Response

{
  "totalSections": 21,
  "totalUsers": 2,
  "users": [
    {
      "userId": "student-001",
      "groupIds": ["class-101"],
      "sectionsCompleted": 2,
      "completionPct": 10,
      "lastActivityAt": "2026-06-18T15:20:45.577Z",
      "currentSection": {
        "order": 2,
        "lastPosition": 9
      }
    }
  ]
}

Get One User's Section Progress

Use this endpoint to see section-by-section playback status for a learner.

JavaScript

const detail = await client.v1.audioRecaps.retrieveUserProgress(
  'audio_recap_abc123',
  'student-001'
);console.log(detail.sections);

Python

detail = client.v1.audio_recaps.retrieve_user_progress(
    "audio_recap_abc123",
    "student-001",
)

print(detail.sections)

Java

var detail = client.v1().audioRecaps().retrieveUserProgress(
    "audio_recap_abc123",
    "student-001"
);

System.out.println(detail.sections());

C#

var detail = await client.V1.AudioRecaps.RetrieveUserProgress(new()
{
    ComponentID = "audio_recap_abc123",
    UserID = "student-001",
});

Console.WriteLine(detail.User.CompletionPct);

cURL

curl "https://studyfetchapi.com/api/v1/audio-recaps/progress/audio_recap_abc123/users/student-001" \
  -H "x-api-key: your-api-key"

Response

{
  "user": {
    "userId": "student-001",
    "groupIds": ["class-101"],
    "sectionsCompleted": 2,
    "totalSections": 21,
    "completionPct": 10
  },
  "sections": [
    {
      "sectionId": "section-id-1",
      "order": 0,
      "isUserQuestion": false,
      "scriptPreview": "Sparky here! Let's explore...",
      "lastPosition": 8,
      "completed": true,
      "completedAt": "2026-06-18T15:20:17.740Z",
      "updatedAt": "2026-06-18T15:20:17.764Z"
    },
    {
      "sectionId": "section-id-2",
      "order": 1,
      "isUserQuestion": false,
      "scriptPreview": "Our exploration begins...",
      "lastPosition": 9,
      "completed": false,
      "updatedAt": "2026-06-18T15:20:45.577Z"
    }
  ]
}

isUserQuestion is true for sections added when a learner asks a follow-up question in the player.

List Progress Across Recaps for One User

Use this endpoint to build a "My recaps" list: one call returns recap-level progress for a learner on up to 100 audio recaps. Pass componentIds (public component IDs or component _ids) to choose the recaps, or omit it to return every recap the learner has started. IDs that are not audio recap components in your organization are returned in notFound instead of failing the request. More than 100 IDs returns 400.

JavaScript

const progress = await client.v1.audioRecaps.listUserProgress('student-001', {
  componentIds: ['audio_recap_abc123', 'audio_recap_def456'],
});for (const recap of progress.recaps) {
  console.log(recap.componentId, `${recap.completionPct}%`, `${recap.remainingSeconds}s left`);
}

Python

progress = client.v1.audio_recaps.list_user_progress(
    "student-001",
    component_ids=["audio_recap_abc123", "audio_recap_def456"],
)

for recap in progress.recaps:
    print(recap.component_id, f"{recap.completion_pct}%", f"{recap.remaining_seconds}s left")

Java

import com.studyfetch.javasdk.models.v1.audiorecaps.AudioRecapListUserProgressParams;
import com.studyfetch.javasdk.models.v1.audiorecaps.AudioRecapListUserProgressResponse;

AudioRecapListUserProgressResponse progress = client.v1().audioRecaps().listUserProgress(
    "student-001",
    AudioRecapListUserProgressParams.builder()
        .addComponentId("audio_recap_abc123")
        .addComponentId("audio_recap_def456")
        .build()
);

progress.recaps().forEach(recap ->
    System.out.println(recap.componentId() + " " + recap.completionPct() + "%"));

C#

var progress = await client.V1.AudioRecaps.ListUserProgress(new()
{
    UserID = "student-001",
    ComponentIDs = new List<string> { "audio_recap_abc123", "audio_recap_def456" },
});

foreach (var recap in progress.Recaps)
{
    Console.WriteLine($"{recap.ComponentID}: {recap.CompletionPct}% ({recap.RemainingSeconds}s left)");
}

cURL

curl "https://studyfetchapi.com/api/v1/audio-recaps/users/student-001/progress?componentIds=audio_recap_abc123,audio_recap_def456" \
  -H "x-api-key: your-api-key"

Response

{
  "userId": "student-001",
  "recaps": [
    {
      "componentId": "audio_recap_abc123",
      "generationStatus": "completed",
      "completionPct": 33,
      "totalSections": 3,
      "sectionsCompleted": 1,
      "durationSeconds": 540,
      "listenedSeconds": 230,
      "remainingSeconds": 310,
      "lastActivityAt": "2026-10-05T15:20:45.577Z"
    }
  ],
  "notFound": ["audio_recap_def456"]
}
  • recaps has one entry per resolved component, in request order.
  • completionPct is sectionsCompleted / totalSections, from 0 to 100.
  • durationSeconds is approximate: it uses each section's generated audio length, or an estimate from the script (about 150 words per minute) while that section's audio is not ready.
  • listenedSeconds counts completed sections in full plus the last position in unfinished ones. remainingSeconds is durationSeconds - listenedSeconds.
  • lastActivityAt is null if the learner has not started the recap.

Embed in Your HTML

<iframe 
  src="https://embed.studyfetch.com/component/comp_202mno?token=..."
  width="100%"
  height="400px"
  frameborder="0"
  style="border: 1px solid #e5e5e5; border-radius: 8px;">
</iframe>

Section Feedback

Learners can rate each section with a thumbs up or thumbs down and an optional reason. List feedback for your organization, filtered by componentId, recapId, feedbackType (thumbsUp or thumbsDown), userId, groupId, startDate, and endDate. Results are paged with limit (default 100) and skip.

JavaScript

const feedback = await client.v1.audioRecaps.listFeedback({
  componentId: 'audio_recap_abc123',
  feedbackType: 'thumbsDown',
});console.log(feedback.total, feedback.stats);

// The rated section plus its sibling sections, for one feedback item
const context = await client.v1.audioRecaps.retrieveFeedbackContext(feedback.feedback[0]._id);

Python

feedback = client.v1.audio_recaps.list_feedback(
    component_id="audio_recap_abc123",
    feedback_type="thumbsDown",
)

print(feedback.total, feedback.stats)

# The rated section plus its sibling sections, for one feedback item
context = client.v1.audio_recaps.retrieve_feedback_context(feedback.feedback[0].api_id)

Java

import com.studyfetch.javasdk.models.v1.audiorecaps.AudioRecapListFeedbackParams;
import com.studyfetch.javasdk.models.v1.audiorecaps.AudioRecapListFeedbackResponse;

AudioRecapListFeedbackResponse feedback = client.v1().audioRecaps().listFeedback(
    AudioRecapListFeedbackParams.builder()
        .componentId("audio_recap_abc123")
        .feedbackType(AudioRecapListFeedbackParams.FeedbackType.THUMBS_DOWN)
        .build()
);

System.out.println(feedback.total());

C#

using StudyfetchSDK.Models.V1.AudioRecaps.AudioRecapListFeedbackParamsProperties;

var feedback = await client.V1.AudioRecaps.ListFeedback(new()
{
    ComponentID = "audio_recap_abc123",
    FeedbackType = FeedbackType.ThumbsDown,
});

Console.WriteLine(feedback.Total);

cURL

curl "https://studyfetchapi.com/api/v1/audio-recaps/feedback?componentId=audio_recap_abc123&feedbackType=thumbsDown" \
  -H "x-api-key: your-api-key"

The response contains feedback (the feedback items), total, groups (group IDs seen in the results), and stats with thumbsUp, thumbsDown, and withReason counts.

React Native

The React Native SDK (@studyfetch/react-native-sdk) renders Audio Recap natively with embed tokens instead of an iframe. Import it from @studyfetch/react-native-sdk/audio-recap, which requires the expo-audio peer dependency, and wrap your app in EmbedTokenProvider with a getToken function that asks your backend to mint an embed token for a component.

import { EmbedTokenProvider } from '@studyfetch/react-native-sdk';
import {
  StudyFetchAudioRecap,
  useAudioRecap,
  useAudioRecapsProgress,
} from '@studyfetch/react-native-sdk/audio-recap';

// Full player: playback, per-section progress, follow-up questions, feedback
<EmbedTokenProvider getToken={fetchToken}>
  <StudyFetchAudioRecap componentId="audio_recap_abc123" userId="student-001" />
</EmbedTokenProvider>

// Generation status: the hook polls while anything is still generating
const { recap, sections, generating, retrySection } = useAudioRecap({
  componentId: 'audio_recap_abc123',
  userId: 'student-001',
});
if (recap?.generationStatus === 'failed') console.warn(recap.generationError);

// Recap-level progress for a "My recaps" list
const { progress, errors, loading, refresh } = useAudioRecapsProgress({
  componentIds: ['audio_recap_abc123', 'audio_recap_def456'],
  userId: 'student-001',
});
  • Pass a userId. Progress is stored per user; without one, playback position is not saved.
  • useAudioRecapsProgress returns the same fields as List Progress Across Recaps for One User, keyed by componentId. Each recap is read with its own embed token, so getToken is called once per component. useAudioRecapProgress does the same for a single recap.
  • On iOS, add "UIBackgroundModes": ["audio"] to your app config so playback continues when the screen locks.