Everything a React Native developer needs to build a native ReadIn client.
Download OpenAPI Spec →Returns all available voices with descriptions. Use these voice IDs when generating audio.
{
"success": true,
"voices": [
{ "id": "alloy", "name": "Alloy", "description": "Neutral, balanced" },
{ "id": "echo", "name": "Echo", "description": "Warm, resonant" },
{ "id": "fable", "name": "Fable", "description": "Expressive, British" },
{ "id": "onyx", "name": "Onyx", "description": "Deep, authoritative" },
{ "id": "nova", "name": "Nova", "description": "Energetic, bright" },
{ "id": "shimmer", "name": "Shimmer", "description": "Soft, clear" }
],
"count": 6
}
Parses raw script text into structured dialogue. Auto-detects characters (UPPERCASE names followed by colon), assigns voices, and counts lines per character.
| Field | Type | Description |
|---|---|---|
text required | string | Raw script text. Format: CHARACTER: dialogue |
// Request { "text": "MARCUS: You said you'd be there.\nELENA: Things changed.\nSARAH: I tried to warn you." }
{
"success": true,
"lines": [
{ "character": "MARCUS", "dialogue": "You said you'd be there." },
{ "character": "ELENA", "dialogue": "Things changed." },
{ "character": "SARAH", "dialogue": "I tried to warn you." }
],
"characters": ["MARCUS", "ELENA", "SARAH"],
"voiceAssignments": { "MARCUS": "alloy", "ELENA": "echo", "SARAH": "fable" },
"lineCounts": { "MARCUS": 1, "ELENA": 1, "SARAH": 1 }
}
Generates MP3 audio from text using AI voices. Returns binary audio data. Cached for 1 hour. Text truncated to 4096 characters.
| Field | Type | Description |
|---|---|---|
text required | string | Text to speak (max 4096 chars) |
voice | string | Voice ID from /api/v1/voices. Default: alloy |
speed | number | Speed multiplier 0.25–4.0. Default: 1.0 |
// Request { "text": "You said you'd be there. You promised me.", "voice": "echo", "speed": 1.0 } // Response: audio/mpeg binary (MP3) // Headers: X-Voice: echo, X-Text-Length: 42
// Fetch audio blob and play it const res = await fetch('https://readin-jdbx.polsia.app/api/v1/tts/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: 'Hello world', voice: 'nova' }) }); const blob = await res.blob(); // Use expo-av or react-native-sound to play the blob
Generates a complete playback sequence with timing, gaps, and TTS payloads. Native clients use this to orchestrate rehearsal without reimplementing the sequencing logic. Each item tells you exactly what to do: play audio (AI line) or wait (user line).
| Field | Type | Description |
|---|---|---|
lines required | array | Lines from /api/v1/scripts/parse |
selectedCharacter required | string | Character the user is playing |
voiceAssignments | object | Voice mapping from parse response |
// Response (truncated) { "success": true, "selectedCharacter": "ELENA", "summary": { "totalLines": 13, "userLines": 4, "aiLines": 9, "estimatedDurationSec": 120, "estimatedDurationMin": 2.0 }, "sequence": [ { "index": 0, "character": "MARCUS", "dialogue": "You said you'd be there.", "isUser": false, "voice": "alloy", "estimatedDurationSec": 3, "gapDurationSec": 0, "ttsEndpoint": "/api/v1/tts/generate", "ttsPayload": { "text": "You said...", "voice": "alloy" }, "delayAfterMs": 400 }, { "index": 1, "character": "ELENA", "isUser": true, "voice": null, "gapDurationSec": 4, "ttsEndpoint": null, "ttsPayload": null } ] }
// 1. Parse script const parsed = await parseScript(scriptText); // 2. Get rehearsal plan const plan = await createPlan(parsed.lines, 'ELENA', parsed.voiceAssignments); // 3. Pre-load all AI audio for (const item of plan.sequence.filter(s => !s.isUser)) { const audio = await fetch(item.ttsEndpoint, { method: 'POST', body: JSON.stringify(item.ttsPayload) }); cache[item.index] = await audio.blob(); } // 4. Play sequence for (const item of plan.sequence) { if (item.isUser) { // Show "Your turn" UI, wait gapDurationSec await waitForUser(item.gapDurationSec); } else { // Play cached audio await playAudio(cache[item.index]); } await delay(item.delayAfterMs); }
{
"status": "healthy",
"version": "2.0.0",
"services": { "tts": true, "database": true }
}
All errors follow a consistent format with machine-readable error codes:
{
"success": false,
"error": "MISSING_TEXT", // Machine-readable code
"message": "Text is required" // Human-readable message
}
| Code | HTTP | Meaning |
|---|---|---|
MISSING_TEXT | 400 | Required text field is empty |
NO_DIALOGUE | 400 | Script contains no parseable dialogue |
MISSING_LINES | 400 | Rehearsal plan needs parsed lines |
MISSING_CHARACTER | 400 | No character selected for rehearsal |
PARSE_FAILED | 500 | Internal parsing error |
TTS_FAILED | 500 | OpenAI TTS generation failed |
PLAN_FAILED | 500 | Rehearsal plan creation failed |
OFFLINE | - | PWA offline (service worker response) |
All /api/* endpoints include permissive CORS headers for cross-origin access from native apps and other domains.