Ramadan Last 10 Nights Campaign

Used by thousands of students worldwide - help us reach 150 members

... / 150 members...
APIAudio

Audio

/audio

Text-to-speech audio for book lines. Audio is generated per line field (arabic, english, commentary) with an OpenAI TTS voice, stored as MP3 in S3 under the key bookId/lineId/field/voice_contentHash.mp3, and handed to clients either as a time-limited S3 presigned URL or as an MP3 stream proxied by the API. The content hash covers all of the line text plus the voice, so editing a line invalidates its cached audio. Generating audio is restricted to editors and admins; anonymous callers and members can only fetch audio that has already been generated for the current line content, and the listing and URL endpoints apply the same read-access rules as GET /books/:bookId (private and subscriber-only books require a token).

GET/audio/voices

List available TTS voices

Public

Returns the static list of voices that can be passed as the voice parameter on the other audio endpoints. The default voice everywhere is alloy.

Request
curl -X GET "https://app.ummahspot.com/audio/voices"
Response · 200
{
  "voices": [
    { "id": "alloy", "name": "Alloy", "description": "Neutral, balanced voice" },
    { "id": "echo", "name": "Echo", "description": "Clear, articulate voice" },
    { "id": "fable", "name": "Fable", "description": "Warm, storytelling voice" },
    { "id": "onyx", "name": "Onyx", "description": "Deep, authoritative voice" },
    { "id": "nova", "name": "Nova", "description": "Bright, energetic voice" },
    { "id": "shimmer", "name": "Shimmer", "description": "Gentle, soothing voice" }
  ]
}
GET/audio/fields

List line fields that support audio

Public

Returns the static list of line fields audio can be generated for. These map to the Arabic, English and commentary text of a book line.

Request
curl -X GET "https://app.ummahspot.com/audio/fields"
Response · 200
{
  "fields": ["arabic", "english", "commentary"]
}
POST/audio/:bookId/generate

Start a whole-book audio generation job

Auth requirededitor / admin only

Creates an AudioJob with status "pending" and responds immediately with its id. Generation then runs in the background inside the API process: the job moves to "in_progress", and for every line of the book and every field (arabic, english, commentary) audio is fetched from the S3 cache or generated with OpenAI TTS and uploaded. progress (0-100) is saved after each line/field, and the job ends as "completed", or "failed" with an error message if the job itself crashes. Failures for an individual line/field (for example an empty field) are only logged, still count towards progress, and do not fail the job. The voice is not validated here. Poll GET /audio/jobs/:jobId to follow the job.

Path parameters

bookId
string
Book ObjectId

Body parameters

voice
string
TTS voice id: alloy, echo, fable, onyx, nova or shimmer (see GET /audio/voices)Default: alloy

Errors

  • 401No bearer token provided (plain-text body)
  • 403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)
  • 404Book not found (or, as plain text, the user behind the token no longer exists)
  • 500Failed to start audio generation (including a malformed bookId)
Request
curl -X POST "https://app.ummahspot.com/audio/:bookId/generate" \
  -H "Authorization: Bearer $SHARH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"voice":"string"}'
Response · 200
{
  "success": true,
  "jobId": "66f1c2a9e4b0a1d2c3f4a5b6"
}
GET/audio/jobs/:jobId

Get an audio generation job

Auth requirededitor / admin only

Returns the raw AudioJob document so a client can poll status and progress. status is one of pending, in_progress, completed or failed; progress is a percentage from 0 to 100; error is only present on failed jobs. Any editor or admin can read any job.

Path parameters

jobId
string
AudioJob ObjectId returned by POST /audio/:bookId/generate

Errors

  • 401No bearer token provided (plain-text body)
  • 403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)
  • 404Job not found (or, as plain text, the user behind the token no longer exists)
  • 500Failed to fetch job (including a malformed jobId)
Request
curl -X GET "https://app.ummahspot.com/audio/jobs/:jobId" \
  -H "Authorization: Bearer $SHARH_TOKEN"
Response · 200
{
  "_id": "66f1c2a9e4b0a1d2c3f4a5b6",
  "bookId": "665f1b2c3d4e5f6a7b8c9d0e",
  "voice": "alloy",
  "status": "in_progress",
  "progress": 42,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T10:34:12.000Z",
  "__v": 0
}
GET/audio/:bookId/lines-with-audio

List every line of a book with audio availability for one voice

Auth optional

Lists all audio objects stored in S3 for the book, keeps those matching the requested voice (case-insensitive), and returns every line of the book with, per field (arabic, english, commentary), the field text, whether audio exists, and a presigned S3 URL valid for 1 hour (null when there is no audio). This endpoint never generates audio. Read access to the book is enforced exactly like GET /books/:bookId: audio of public books is readable by anyone, while a bearer token is needed to read audio of private books (owner, contributors and admins only — everyone else gets a 404) and of subscriber-only books (accessModel "creator_subscription": callers who cannot manage the book need an active subscription to its creator at the required tier, otherwise a 402 is returned). An invalid token is silently treated as anonymous.

Path parameters

bookId
string
Book ObjectId

Query parameters

voice
string
Voice id to check audio for (alloy, echo, fable, onyx, nova, shimmer). Echoed back in the response.Default: alloy

Errors

  • 402Creator subscription required to read this book. JSON body: { requiresSubscription: true, message, contentType: "book", contentId, requiredSubscriptionTier, creator }
  • 404Book not found: it does not exist, is deleted, or is not public and the caller is not its owner, a contributor or an admin
  • 500Failed to get line audio list (S3 error or malformed bookId); body includes details
Request
curl -X GET "https://app.ummahspot.com/audio/:bookId/lines-with-audio" \
  -H "Authorization: Bearer $SHARH_TOKEN"
Response · 200
{
  "success": true,
  "bookId": "665f1b2c3d4e5f6a7b8c9d0e",
  "bookTitle": "Al-Ajrumiyyah",
  "voice": "alloy",
  "lines": [
    {
      "lineId": "665f1b2c3d4e5f6a7b8c9d11",
      "fields": {
        "arabic": {
          "text": "الكلام هو اللفظ المركب المفيد بالوضع",
          "hasAudio": true,
          "audioUrl": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/arabic/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=3600&X-Amz-Signature=..."
        },
        "english": {
          "text": "Speech is a composed utterance that conveys meaning by convention.",
          "hasAudio": true,
          "audioUrl": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=3600&X-Amz-Signature=..."
        },
        "commentary": {
          "text": "",
          "hasAudio": false,
          "audioUrl": null
        }
      }
    }
  ]
}
GET/audio/jobs

List all audio generation jobs

Auth requirededitor / admin only

Returns every AudioJob, newest first, with bookId populated with the book title, author, category, progress and difficulty. Jobs are not filtered by user: any editor or admin sees all jobs. There is no pagination.

Errors

  • 401No bearer token provided (plain-text body)
  • 403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)
  • 404The user behind the token no longer exists (plain-text body)
  • 500Failed to fetch audio jobs
Request
curl -X GET "https://app.ummahspot.com/audio/jobs" \
  -H "Authorization: Bearer $SHARH_TOKEN"
Response · 200
{
  "success": true,
  "jobs": [
    {
      "_id": "66f1c2a9e4b0a1d2c3f4a5b6",
      "bookId": {
        "_id": "665f1b2c3d4e5f6a7b8c9d0e",
        "title": "Al-Ajrumiyyah",
        "author": "Ibn Ajurrum",
        "category": "Grammar",
        "progress": "Complete",
        "difficulty": "Beginner"
      },
      "voice": "alloy",
      "status": "completed",
      "progress": 100,
      "createdAt": "2025-01-15T10:30:00.000Z",
      "updatedAt": "2025-01-15T10:52:41.000Z",
      "__v": 0
    }
  ]
}
POST/audio/:bookId/lines/:lineId/batch

Generate or fetch audio for several fields of a line

Auth requirededitor / admin only

For each requested field, returns the cached MP3 from S3 if one exists for the current line content and voice, otherwise generates it synchronously with OpenAI TTS, uploads it to S3 and removes stale audio for that field. Each result contains the S3 key, a presigned URL valid for 1 hour, and whether it came from the cache. A failure for a single field (for example the field has no text, or the voice is rejected by the TTS provider) does not fail the request: it is reported under errors keyed by field name, and errors is omitted when every field succeeded. Field names are validated against arabic, english and commentary; the voice is not validated up front. Because generation costs money, only editors and admins may call this endpoint; only the role is checked, there is no book ownership check.

Path parameters

bookId
string
Book ObjectId
lineId
string
ObjectId of the line inside the book

Body parameters

fields
string[]
Fields to generate audio for. Allowed values: arabic, english, commentary.Default: ["arabic", "english"]
voice
string
TTS voice id: alloy, echo, fable, onyx, nova or shimmerDefault: alloy

Errors

  • 400Invalid fields; body lists invalidFields and availableFields
  • 401No bearer token provided (plain-text body)
  • 403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)
  • 404Book not found, or Line not found in that book (or, as plain text, the user behind the token no longer exists)
  • 500Failed to generate batch audio (for example fields is not an array of strings, or a malformed id); body includes details
Request
curl -X POST "https://app.ummahspot.com/audio/:bookId/lines/:lineId/batch" \
  -H "Authorization: Bearer $SHARH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fields":[]}'
Response · 200
{
  "success": true,
  "bookId": "665f1b2c3d4e5f6a7b8c9d0e",
  "lineId": "665f1b2c3d4e5f6a7b8c9d11",
  "voice": "alloy",
  "results": {
    "arabic": {
      "s3Key": "665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/arabic/alloy_9f2c4e1a7b3d5c60.mp3",
      "audioUrl": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/arabic/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=3600&X-Amz-Signature=...",
      "cached": true
    }
  },
  "errors": {
    "english": "No content found for field: english"
  }
}
POST/audio/:bookId/lines/:lineId/:field

Generate or fetch audio for one field of a line

Auth requirededitor / admin only

Returns the cached MP3 for this line field and voice if one exists for the current line content, otherwise generates it synchronously with OpenAI TTS (long text is split into chunks of up to 4000 characters and concatenated), uploads it to S3 and deletes stale audio for that field. The response carries the S3 key and a presigned URL valid for 1 hour; request GET .../url again for a fresh URL once it expires. Both field and voice are validated. An empty field results in a 500. Because generation costs money, only editors and admins may call this endpoint; only the role is checked, there is no book ownership check.

Path parameters

bookId
string
Book ObjectId
lineId
string
ObjectId of the line inside the book
field
string
Line field: arabic, english or commentary (case-insensitive)

Body parameters

voice
string
TTS voice id. Must be one of alloy, echo, fable, onyx, nova, shimmer.Default: alloy

Errors

  • 400Invalid field (body lists availableFields) or Invalid voice (body lists availableVoices)
  • 401No bearer token provided (plain-text body)
  • 403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)
  • 404Book not found, or Line not found in that book (or, as plain text, the user behind the token no longer exists)
  • 500Failed to generate audio (field has no text, TTS or S3 failure, malformed id); body includes details
Request
curl -X POST "https://app.ummahspot.com/audio/:bookId/lines/:lineId/:field" \
  -H "Authorization: Bearer $SHARH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"voice":"string"}'
Response · 200
{
  "success": true,
  "bookId": "665f1b2c3d4e5f6a7b8c9d0e",
  "lineId": "665f1b2c3d4e5f6a7b8c9d11",
  "field": "english",
  "voice": "alloy",
  "s3Key": "665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3",
  "audioUrl": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=3600&X-Amz-Signature=...",
  "cached": false,
  "message": "New audio generated"
}
GET/audio/:bookId/lines/:lineId/:field/stream

Stream the MP3 audio for one field of a line

Auth optional

Looks up the MP3 for the line field and voice in the S3 cache and pipes the S3 object through the API as the response body. For anonymous callers and members this endpoint only serves audio that has ALREADY been generated for the line's current content and voice; if there is none (never generated, or the line was edited since) it answers 404 and nothing is generated. Only when the bearer token belongs to an editor or admin is missing audio generated and uploaded on demand. field must be arabic, english or commentary and voice must be one of the ids from GET /audio/voices, otherwise a 400 is returned. The response is sent with Content-Type: audio/mpeg, Accept-Ranges: bytes and Cache-Control: max-age=31536000. Although Accept-Ranges is advertised, the handler always sends the complete file with status 200 and does not honour Range headers. Book read access is NOT checked on this route, because media players (an audio element src) cannot send an Authorization header; use GET .../url when access control matters. An invalid token is silently treated as anonymous.

Path parameters

bookId
string
Book ObjectId
lineId
string
ObjectId of the line inside the book
field
string
Line field: arabic, english or commentary (case-insensitive)

Query parameters

voice
string
TTS voice id. Must be one of alloy, echo, fable, onyx, nova, shimmer.Default: alloy

Errors

  • 400Invalid field or voice ({ "error": "Invalid field or voice" })
  • 404Book not found, Line not found in that book, or — for callers who are not editors or admins — { "error": "Audio has not been generated for this line" }
  • 500Failed to stream audio (S3 failure, malformed id, or for editors/admins an empty field or TTS failure during generation)
Request
curl -X GET "https://app.ummahspot.com/audio/:bookId/lines/:lineId/:field/stream" \
  -H "Authorization: Bearer $SHARH_TOKEN"
Response · 200
Binary MP3 stream (Content-Type: audio/mpeg), not JSON. Suitable as the src of an audio element.
GET/audio/:bookId/lines/:lineId/:field/url

Get a presigned URL for the audio of one field of a line

Auth optional

Looks up the MP3 for the line field and voice in the S3 cache and returns a fresh S3 presigned GET URL with a caller-chosen lifetime. The URL points directly at S3 (served as audio/mpeg with inline disposition) and stops working after expiresIn seconds. As with the stream endpoint, anonymous callers and members only get audio that has ALREADY been generated for the line's current content and voice (cached is then always true) and receive a 404 otherwise; only editors and admins trigger generation on demand. field must be arabic, english or commentary and voice must be one of the ids from GET /audio/voices, otherwise a 400 is returned. Read access to the book is enforced exactly like GET /books/:bookId: audio of public books is readable by anyone, while a bearer token is needed to read audio of private books (owner, contributors and admins only — everyone else gets a 404) and of subscriber-only books (accessModel "creator_subscription": callers who cannot manage the book need an active subscription to its creator at the required tier, otherwise a 402 is returned). An invalid token is silently treated as anonymous.

Path parameters

bookId
string
Book ObjectId
lineId
string
ObjectId of the line inside the book
field
string
Line field: arabic, english or commentary (case-insensitive)

Query parameters

voice
string
TTS voice id. Must be one of alloy, echo, fable, onyx, nova, shimmer.Default: alloy
expires
number
Lifetime of the presigned URL in seconds (parsed as an integer)Default: 3600

Errors

  • 400Invalid field or voice ({ "error": "Invalid field or voice" })
  • 402Creator subscription required to read this book. JSON body: { requiresSubscription: true, message, contentType: "book", contentId, requiredSubscriptionTier, creator }
  • 404Book not found (it does not exist, is deleted, or is not public and the caller is not its owner, a contributor or an admin), Line not found in that book, or — for callers who are not editors or admins — { "error": "Audio has not been generated for this line" }
  • 500Failed to get audio URL (S3 failure, malformed id, or for editors/admins an empty field or TTS failure during generation); body includes details
Request
curl -X GET "https://app.ummahspot.com/audio/:bookId/lines/:lineId/:field/url" \
  -H "Authorization: Bearer $SHARH_TOKEN"
Response · 200
{
  "success": true,
  "bookId": "665f1b2c3d4e5f6a7b8c9d0e",
  "lineId": "665f1b2c3d4e5f6a7b8c9d11",
  "field": "english",
  "voice": "alloy",
  "audioUrl": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=600&X-Amz-Signature=...",
  "expiresIn": 600,
  "cached": true
}
GET/audio/:bookId/lines/:lineId

List stored audio files for a line

Auth optional

Lists every audio object stored in S3 for the line, across all fields and voices, each with a presigned URL valid for 1 hour, together with the text content of the line. This endpoint never generates audio. Read access to the book is enforced exactly like GET /books/:bookId: audio of public books is readable by anyone, while a bearer token is needed to read audio of private books (owner, contributors and admins only — everyone else gets a 404) and of subscriber-only books (accessModel "creator_subscription": callers who cannot manage the book need an active subscription to its creator at the required tier, otherwise a 402 is returned). An invalid token is silently treated as anonymous.

Path parameters

bookId
string
Book ObjectId
lineId
string
ObjectId of the line inside the book

Errors

  • 402Creator subscription required to read this book. JSON body: { requiresSubscription: true, message, contentType: "book", contentId, requiredSubscriptionTier, creator }
  • 404Book not found (it does not exist, is deleted, or is not public and the caller is not its owner, a contributor or an admin), or Line not found in that book
  • 500Failed to list audio files (S3 error or malformed id); body includes details
Request
curl -X GET "https://app.ummahspot.com/audio/:bookId/lines/:lineId" \
  -H "Authorization: Bearer $SHARH_TOKEN"
Response · 200
{
  "success": true,
  "bookId": "665f1b2c3d4e5f6a7b8c9d0e",
  "lineId": "665f1b2c3d4e5f6a7b8c9d11",
  "audioFiles": [
    {
      "s3Key": "665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3",
      "field": "english",
      "voice": "alloy",
      "contentHash": "9f2c4e1a7b3d5c60",
      "size": 48213,
      "lastModified": "2025-01-15T10:31:07.000Z",
      "url": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=3600&X-Amz-Signature=..."
    }
  ],
  "lineContent": {
    "arabic": "الكلام هو اللفظ المركب المفيد بالوضع",
    "english": "Speech is a composed utterance that conveys meaning by convention.",
    "commentary": "",
    "rootwords": ""
  }
}
DELETE/audio/:bookId/lines/:lineId/:field

Delete stored audio for one field of a line

Auth requirededitor / admin only

With a voice query parameter, deletes the single S3 object for that voice and the current line content (deletedFiles is always reported as 1, whether or not the object existed). Without voice, deletes every stored audio file for the field, across all voices (deletedFiles is the string "all"). Only the role is checked: there is no book ownership check, so any editor or admin can delete audio of any book. The field value is not validated.

Path parameters

bookId
string
Book ObjectId
lineId
string
ObjectId of the line inside the book
field
string
Line field whose audio should be deleted: arabic, english or commentary

Query parameters

voice
string
Voice id to delete. When omitted, audio for all voices of the field is deleted.

Errors

  • 401No bearer token provided (plain-text body)
  • 403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)
  • 404Book not found, or Line not found in that book (or, as plain text, the user behind the token no longer exists)
  • 500Failed to delete audio (S3 error or malformed id); body includes details
Request
curl -X DELETE "https://app.ummahspot.com/audio/:bookId/lines/:lineId/:field" \
  -H "Authorization: Bearer $SHARH_TOKEN"
Response · 200
{
  "success": true,
  "message": "Deleted audio for english with voice alloy",
  "deletedFiles": 1
}
DELETE/audio/:bookId/lines/:lineId

Delete all stored audio for a line

Auth requirededitor / admin only

Deletes every audio object stored in S3 for the line, across all fields and voices, and returns how many files were removed. Only the role is checked; there is no book ownership check.

Path parameters

bookId
string
Book ObjectId
lineId
string
ObjectId of the line inside the book

Errors

  • 401No bearer token provided (plain-text body)
  • 403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)
  • 404Book not found, or Line not found in that book (or, as plain text, the user behind the token no longer exists)
  • 500Failed to delete line audio (S3 error or malformed id); body includes details
Request
curl -X DELETE "https://app.ummahspot.com/audio/:bookId/lines/:lineId" \
  -H "Authorization: Bearer $SHARH_TOKEN"
Response · 200
{
  "success": true,
  "message": "Deleted all audio for line 665f1b2c3d4e5f6a7b8c9d11",
  "deletedFiles": 3
}
DELETE/audio/:bookId

Delete all stored audio for a book

Auth requirededitor / admin only

Deletes every audio object stored in S3 under the book, for all lines, fields and voices, and returns how many files were removed. The book itself is not modified. Only the role is checked; there is no book ownership check.

Path parameters

bookId
string
Book ObjectId

Errors

  • 401No bearer token provided (plain-text body)
  • 403Invalid or expired token, or the user has neither the editor nor the admin role (plain-text body)
  • 404Book not found (or, as plain text, the user behind the token no longer exists)
  • 500Failed to delete book audio (S3 error or malformed bookId); body includes details
Request
curl -X DELETE "https://app.ummahspot.com/audio/:bookId" \
  -H "Authorization: Bearer $SHARH_TOKEN"
Response · 200
{
  "success": true,
  "message": "Deleted all audio for book 665f1b2c3d4e5f6a7b8c9d0e",
  "deletedFiles": 126
}
GET/audio/:bookId/all

List all stored audio for a book, grouped by line

Auth optional

Lists every audio object stored in S3 for the book, optionally filtered by field and voice, and groups them by line and field. Only lines that have at least one matching audio file are returned, in book order, each with its text content and, per field, an array of audio files with presigned URLs valid for 2 hours. totalFiles is the number of matching files. This endpoint never generates audio. Read access to the book is enforced exactly like GET /books/:bookId: audio of public books is readable by anyone, while a bearer token is needed to read audio of private books (owner, contributors and admins only — everyone else gets a 404) and of subscriber-only books (accessModel "creator_subscription": callers who cannot manage the book need an active subscription to its creator at the required tier, otherwise a 402 is returned). An invalid token is silently treated as anonymous.

Path parameters

bookId
string
Book ObjectId

Query parameters

fields
string
Comma-separated list of fields to include. Allowed values: arabic, english, commentary (case-insensitive).Default: arabic,english,commentary
voice
string
Only include audio for this voice id (case-insensitive). When omitted, all voices are returned.

Errors

  • 400Invalid fields; body lists invalidFields and availableFields
  • 402Creator subscription required to read this book. JSON body: { requiresSubscription: true, message, contentType: "book", contentId, requiredSubscriptionTier, creator }
  • 404Book not found: it does not exist, is deleted, or is not public and the caller is not its owner, a contributor or an admin
  • 500Failed to get book audio list (S3 error or malformed bookId); body includes details
Request
curl -X GET "https://app.ummahspot.com/audio/:bookId/all" \
  -H "Authorization: Bearer $SHARH_TOKEN"
Response · 200
{
  "success": true,
  "bookId": "665f1b2c3d4e5f6a7b8c9d0e",
  "bookTitle": "Al-Ajrumiyyah",
  "requestedFields": ["arabic", "english", "commentary"],
  "totalFiles": 84,
  "lines": [
    {
      "lineId": "665f1b2c3d4e5f6a7b8c9d11",
      "content": {
        "arabic": "الكلام هو اللفظ المركب المفيد بالوضع",
        "english": "Speech is a composed utterance that conveys meaning by convention.",
        "commentary": "",
        "rootwords": ""
      },
      "audio": {
        "english": [
          {
            "s3Key": "665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3",
            "voice": "alloy",
            "size": 48213,
            "lastModified": "2025-01-15T10:31:07.000Z",
            "url": "https://sharh-app-audio.s3.amazonaws.com/665f1b2c3d4e5f6a7b8c9d0e/665f1b2c3d4e5f6a7b8c9d11/english/alloy_9f2c4e1a7b3d5c60.mp3?X-Amz-Expires=7200&X-Amz-Signature=..."
          }
        ]
      }
    }
  ]
}