Used by thousands of students worldwide - help us reach 150 members
OCR & Digitized Books
/ocrUpload PDFs or page images for asynchronous OCR, poll job status, and read, search, edit, import and export the resulting digitized books ("BookTexts"). The flow is: POST /ocr uploads the file to S3, creates a pending BookText and enqueues an SQS job; an OCR worker posts pages and the final result back through the /ocr/result callbacks; clients poll GET /ocr/status/:jobId and then fetch the text with GET /ocr/text/:jobId or GET /ocr/books/:bookTextId. Page credits are deducted (and overage billed at $0.02/page) only when a job is reported as completed.
/ocr/result/pageSubmit a single OCR page result (worker callback)POST/ocr/resultSubmit the final OCR result for a job (worker callback)POST/ocrUpload a document and create an OCR jobGET/ocr/status/:jobIdGet the status of an OCR jobGET/ocr/text/:jobIdGet the extracted text for an OCR jobGET/ocr/jobsList the current user's OCR jobsGET/ocr/books/searchSearch digitized books or pages within one bookGET/ocr/books/:bookTextIdGet a digitized book with its text and pagesPATCH/ocr/books/:bookTextIdUpdate a digitized bookGET/ocr/booksList digitized booksPOST/ocr/books/:bookTextId/importImport a digitized book as a translation BookGET/ocr/books/:bookTextId/export/docxExport a digitized book as a Word documentSubmit a single OCR page result (worker callback)
Callback used by the OCR worker to push incremental, per-page results while a job is still being processed. It is not intended for end-user clients. The page is upserted into the BookTextPage collection keyed by (BookText, pageNumber), and the parent BookText is updated: status is set to "processing", metadata.pagesProcessed and metadata.lastPageUpdate are written, and pageCount is updated when provided. If no BookText exists for the jobId (an orphaned job) one is created with status "processing" and language "ar". When status is "failed" and an error string is supplied, the error is recorded in the BookText metadata.pageErrors array (replacing any existing entry for the same page number). No page credits are recorded by this endpoint.
Body parameters
jobIdrequiredstring | OCR job id (UUID) returned by POST /ocr and carried in the SQS message. |
fileIdrequiredstring | ObjectId of the uploaded File record. Only used when a BookText has to be created for an orphaned job. |
userIdrequiredstring | ObjectId of the user who owns the job. Only used when a BookText has to be created for an orphaned job. |
pageNumberrequirednumber | Page number this result belongs to. 0 is accepted; only undefined is rejected. |
statusstring | Page-level status. Only the value "failed" (together with error) has an effect: it records a page error. |
textstring | Extracted text for the page.Default: '' |
errorstring | Error message for the page; stored in metadata.pageErrors when status is "failed". |
s3Keystring | S3 key of the rendered page image.Default: null |
pageCountnumber | Total number of pages in the document. Updates BookText.pageCount when truthy. |
pagesProcessednumber | Number of pages processed so far; stored in metadata.pagesProcessed. |
Errors
400Missing jobId, fileId, userId or pageNumber401No bearer token provided403Invalid token, or the user is neither editor nor admin500Error processing page update
curl -X POST "https://app.ummahspot.com/ocr/result/page" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jobId":"string","fileId":"string","userId":"string","pageNumber":0}'{
"success": true
}Submit the final OCR result for a job (worker callback)
Callback used by the OCR worker to post the final result of a job once processing finishes (or fails). It is not intended for end-user clients. The BookText for the jobId is updated (or created if the job is orphaned). If extractedText is longer than 10,000 characters it is uploaded to S3 as ocr-results/<jobId>.txt, metadata.textS3Key and metadata.textLength are set, and only a 1,000-character preview is kept in the database; shorter text is stored inline. Supplied metadata is merged into the existing metadata. Each entry in pages is upserted into the BookTextPage collection by pageNumber. Page-credit accounting happens here: when status is "completed" and the page count (pageCount, else pages.length, else the stored pageCount) is greater than 0, usage is recorded against the userId given in the body. Pages are first taken from the remaining monthly tier credits; any pages beyond that are billed immediately at $0.02/page through a Stripe invoice, and a usage log is written. Owners with the editor or admin role are never charged. The free/overage breakdown is saved to metadata.usageInfo. A failure while recording usage does not fail the request: it is stored in metadata.usageError and usageInfo is returned as null. Usage is recorded on every call that reports status "completed".
Body parameters
jobIdrequiredstring | OCR job id (UUID). |
fileIdrequiredstring | ObjectId of the uploaded File record. Only used when a BookText has to be created for an orphaned job. |
userIdrequiredstring | ObjectId of the user who owns the job. Page usage is recorded against this user. |
extractedTextstring | Full extracted text of the document. Stored in S3 when longer than 10,000 characters. |
languagestring | Language code. Keeps the existing value when omitted ("ar" for newly created records). |
pageCountnumber | Total number of pages. Keeps the existing value when omitted. |
pagesarray | Array of page objects: { pageNumber: number, text?: string, s3Key?: string, isAIGenerated?: boolean (default true) }. Each is upserted by pageNumber. |
metadataobject | Arbitrary metadata merged into BookText.metadata (only merged when extractedText is also provided, or used as the initial metadata of an orphaned job). |
statusstring | New job status: pending, processing, completed, failed or submission_failed. "completed" triggers page-usage recording and sets completedAt. |
errorstring | Job-level error message. |
processingTimenumber | Processing time in milliseconds. |
Errors
400Missing jobId, fileId or userId401No bearer token provided403Invalid token, or the user is neither editor nor admin500Error saving OCR result (database, S3 upload or validation failure)
curl -X POST "https://app.ummahspot.com/ocr/result" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jobId":"string","fileId":"string","userId":"string"}'{
"message": "OCR result saved successfully",
"bookTextId": "66f1c0a7e4b0a1d2c3f4a5b6",
"status": "completed",
"usageInfo": {
"freePages": 12,
"overagePages": 0,
"overageChargeCents": 0,
"totalPagesUsed": 212,
"remainingCredits": 788,
"paymentStatus": "not_required"
}
}Upload a document and create an OCR job
Starts the asynchronous OCR flow. Requires an active subscription (users with the editor or admin role bypass the subscription check); the check runs before the upload is parsed. The uploaded file is stored in S3 under ocr/<uuid>-<originalname>, a File record is created, a BookText is created with status "pending", and a job message (jobId, fileId, userId, S3 location, file name/type/size, language, customPrompt, metadata) is sent to the OCR SQS queue. The call returns immediately with the jobId: poll GET /ocr/status/:jobId until status is "completed" or "failed", then fetch the text with GET /ocr/text/:jobId or GET /ocr/books/:bookTextId. Having remaining credits is not required to upload: no credits are deducted at upload time. Pages are counted when the worker reports the job as completed; pages beyond the monthly tier credits are billed at $0.02/page. usageInfo in the response is a snapshot of the caller's credits at upload time (for editors/admins remainingCredits and tierCredits are null, meaning unlimited). Note that visibility is stored on the File record only; the BookText is always created private and can be made public with PATCH /ocr/books/:bookTextId. If the SQS send fails the request returns 500 although the File and pending BookText records have already been created.
Send the body as multipart/form-data.
Body parameters
filerequiredfile | The document to OCR (single file). Accepted MIME types: application/pdf, image/png, image/jpeg, image/jpg, image/tiff, image/bmp, image/webp. Maximum size 1000 MB. |
fileNamestring | Display name for the document.Default: original file name |
languagestring | Language code of the document, passed to the worker and stored on the BookText.Default: ar |
authorstring | Author stored on the File record.Default: Unknown |
tagsstring | Comma-separated list of tags.Default: '' |
categoriesstring | Comma-separated list of categories. When empty the File record gets the category "ocr" and the BookText gets none.Default: '' |
visibilitystring | Visibility of the File record: "public" or "private". Does not affect the BookText visibility.Default: private |
customPromptstring | Custom prompt forwarded to the OCR worker in the job message. |
jobMetadatastring | JSON-encoded object stored as the BookText metadata and forwarded to the worker. Must be valid JSON. |
Errors
400No file uploaded401No bearer token provided403Invalid token, or no active subscription (error: "SUBSCRIPTION_REQUIRED", subscriptionRequired: true)500Failed to verify subscription status; unsupported file type or file too large (rejected by the upload parser and returned as a plain-text 500); invalid jobMetadata JSON; or S3/database/SQS failure ("Error creating OCR job")
curl -X POST "https://app.ummahspot.com/ocr" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-F "file=@/path/to/file"{
"message": "OCR job created successfully",
"jobId": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41",
"bookTextId": "66f1c0a7e4b0a1d2c3f4a5b6",
"fileId": "66f1c0a6e4b0a1d2c3f4a5b1",
"fileName": "matn-al-ajrumiyyah.pdf",
"s3Key": "ocr/5d2f8a90-3c1b-4e7f-8a21-9b0c4d6e7f10-matn-al-ajrumiyyah.pdf",
"sqsMessageId": "c2a1f6d4-8e7b-4a39-b5d0-1f2e3d4c5b6a",
"status": "pending",
"usageInfo": {
"remainingCredits": 800,
"tierCredits": 1000,
"pagesUsed": 200,
"billingPeriodEnd": "2026-10-01T00:00:00.000Z"
}
}Get the status of an OCR job
Poll this endpoint after POST /ocr. Returns the job state without any text. status is one of pending, processing, completed, failed or submission_failed. hasFullText is true when the full text was large enough to be stored in S3. Only the user who created the job can view it; admins and editors can view any job.
Path parameters
jobIdstring | Job id (UUID) returned by POST /ocr |
Errors
401No bearer token provided403Invalid token, or the job belongs to another user ("Unauthorized to view this job")404OCR job not found500Error retrieving OCR job status
curl -X GET "https://app.ummahspot.com/ocr/status/:jobId" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"job": {
"jobId": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41",
"fileId": "66f1c0a6e4b0a1d2c3f4a5b1",
"fileName": "matn-al-ajrumiyyah.pdf",
"status": "completed",
"language": "ar",
"pageCount": 12,
"processingTime": 48213,
"createdAt": "2026-09-18T10:15:00.000Z",
"updatedAt": "2026-09-18T10:16:02.000Z",
"completedAt": "2026-09-18T10:16:02.000Z",
"hasFullText": true
}
}Get the extracted text for an OCR job
Returns the full extracted text and every page of a job in a single response (pages are not paginated; use GET /ocr/books/:bookTextId for paginated pages with image URLs). If the full text was stored in S3 it is fetched and returned in extractedText instead of the truncated preview. Pages come from the BookTextPage collection sorted by pageNumber, falling back to the legacy embedded pages array for old documents. The endpoint does not check the job status, so text may be empty or partial while the job is still processing. Only the user who created the job can read it; admins and editors can read any job.
Path parameters
jobIdstring | Job id (UUID) returned by POST /ocr |
Errors
401No bearer token provided403Invalid token, or the job belongs to another user ("Unauthorized to view this text")404Extracted text not found for this job500Error retrieving full text from storage (S3) or error retrieving extracted text
curl -X GET "https://app.ummahspot.com/ocr/text/:jobId" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"data": {
"jobId": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41",
"fileId": "66f1c0a6e4b0a1d2c3f4a5b1",
"fileName": "matn-al-ajrumiyyah.pdf",
"extractedText": "الكلام هو اللفظ المركب المفيد بالوضع",
"language": "ar",
"pageCount": 12,
"pages": [
{
"_id": "66f1c0e2e4b0a1d2c3f4a5c0",
"pageNumber": 1,
"text": "الكلام هو اللفظ المركب المفيد بالوضع",
"s3Key": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41/page-1.png",
"isAIGenerated": true
}
],
"status": "completed",
"completedAt": "2026-09-18T10:16:02.000Z"
}
}List the current user's OCR jobs
Returns the OCR jobs created by the authenticated user, newest first, with offset pagination. Always scoped to the caller, including for admins and editors.
Query parameters
statusstring | Filter by job status: pending, processing, completed, failed or submission_failed. |
limitnumber | Maximum number of jobs to return.Default: 20 |
offsetnumber | Number of jobs to skip.Default: 0 |
Errors
401No bearer token provided403Invalid token500Error retrieving OCR jobs
curl -X GET "https://app.ummahspot.com/ocr/jobs" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"total": 7,
"limit": 20,
"offset": 0,
"jobs": [
{
"jobId": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41",
"bookTextId": "66f1c0a7e4b0a1d2c3f4a5b6",
"fileId": "66f1c0a6e4b0a1d2c3f4a5b1",
"fileName": "matn-al-ajrumiyyah.pdf",
"status": "completed",
"language": "ar",
"tags": ["nahw"],
"categories": ["grammar"],
"pageCount": 12,
"processingTime": 48213,
"createdAt": "2026-09-18T10:15:00.000Z",
"updatedAt": "2026-09-18T10:16:02.000Z",
"completedAt": "2026-09-18T10:16:02.000Z"
}
]
}Search digitized books or pages within one book
Full-text search over OCR results, in two modes. Without bookTextId it searches across books (both the book-level extracted text and individual pages) and returns matching books, each with up to 5 match excerpts (pageNumber is null for a match in the book-level text). With bookTextId it searches the pages of that single book and returns the matching pages, including their full text, an excerpt, a highlightedExcerpt (search-engine highlight, or the excerpt with matches wrapped in <mark> tags) and a presigned page image URL valid for 1 hour. Visibility rules: anonymous callers only see public books; signed-in members see public books plus their own; admins and editors see everything. In single-book mode an inaccessible book returns 403 and a missing one 404. OpenSearch is used when enabled, with a case-insensitive MongoDB regex search as fallback when it is disabled or returns nothing. limit is capped at 50. In single-book mode total is the number of results in the returned page, not the overall match count.
Query parameters
qrequiredstring | Search text. In the MongoDB fallback it is treated as a case-insensitive regular expression. |
limitnumber | Maximum number of results (capped at 50).Default: 20 |
offsetnumber | Number of results to skip.Default: 0 |
bookTextIdstring | BookText ObjectId. When set, searches pages inside this book only and the response shape changes to { success, total, query, results }. |
Errors
400Missing query parameter "q"403bookTextId refers to a private book the caller cannot access ("Unauthorized to search this book")404bookTextId does not exist ("Book not found")500Error searching books / Error searching within book (including a malformed bookTextId)
curl -X GET "https://app.ummahspot.com/ocr/books/search" \
-H "Authorization: Bearer $SHARH_TOKEN"// Without bookTextId (search across books)
{
"success": true,
"total": 1,
"limit": 20,
"offset": 0,
"books": [
{
"bookTextId": "66f1c0a7e4b0a1d2c3f4a5b6",
"jobId": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41",
"fileId": "66f1c0a6e4b0a1d2c3f4a5b1",
"fileName": "matn-al-ajrumiyyah.pdf",
"author": "Ibn Ajurrum",
"userId": "65a0f3b2e4b0a1d2c3f4a111",
"username": "abdullah",
"language": "ar",
"tags": ["nahw"],
"categories": ["grammar"],
"visibility": "public",
"status": "completed",
"pageCount": 12,
"matches": [
{
"pageNumber": 1,
"excerpt": "الكلام هو اللفظ المركب المفيد بالوضع"
}
],
"updatedAt": "2026-09-18T10:16:02.000Z",
"createdAt": "2026-09-18T10:15:00.000Z"
}
]
}
// With bookTextId (search inside one book)
{
"success": true,
"total": 1,
"query": "الكلام",
"results": [
{
"_id": "66f1c0e2e4b0a1d2c3f4a5c0",
"pageNumber": 1,
"text": "الكلام هو اللفظ المركب المفيد بالوضع",
"excerpt": "الكلام هو اللفظ المركب المفيد بالوضع",
"highlightedExcerpt": "<mark>الكلام</mark> هو اللفظ المركب المفيد بالوضع",
"s3Key": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41/page-1.png",
"imageUrl": "https://sharh-app-ocr-cache.s3.amazonaws.com/arabic-vision/...signed...",
"isAIGenerated": true
}
]
}Get a digitized book with its text and pages
Returns one digitized book with file details, owner, optional full text and a paginated slice of its pages. Public books are readable by anyone, including anonymous callers; private books only by their owner, admins and editors. The owner's userEmail, the source file's s3Key (the book-level s3Key) and the internal metadata object are only returned to the book's owner, admins and editors; for every other caller — including anonymous readers of a public book — those three keys are absent from the response. The example below shows the owner's view. The page-level s3Key inside pages[] is returned to every caller. When includeText is "true" the full text is returned in extractedText (fetched from S3 if it was stored there); any other value omits the field. Pages are sorted by pageNumber and paginated with pageLimit/pageOffset; totalPages and hasMore describe the pagination. Each page with an s3Key gets imageUrl, a presigned URL of the page image valid for 1 hour (null if there is no image or signing fails). Old documents without BookTextPage records fall back to the legacy embedded pages array.
Path parameters
bookTextIdstring | BookText ObjectId |
Query parameters
pageLimitnumber | Maximum number of pages to return.Default: 50 |
pageOffsetnumber | Number of pages to skip.Default: 0 |
includeTextstring | Set to anything other than "true" (e.g. "false") to omit extractedText and avoid loading the full text.Default: true |
Errors
403Book is private and the caller is not its owner, an editor or an admin404Book not found500Error retrieving full text from storage (S3) or error retrieving book (including a malformed bookTextId)
curl -X GET "https://app.ummahspot.com/ocr/books/:bookTextId" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"book": {
"bookTextId": "66f1c0a7e4b0a1d2c3f4a5b6",
"jobId": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41",
"fileId": "66f1c0a6e4b0a1d2c3f4a5b1",
"fileName": "matn-al-ajrumiyyah.pdf",
"author": "Ibn Ajurrum",
"fileType": "application/pdf",
"fileSize": 1843200,
"s3Key": "ocr/5d2f8a90-3c1b-4e7f-8a21-9b0c4d6e7f10-matn-al-ajrumiyyah.pdf",
"userId": "65a0f3b2e4b0a1d2c3f4a111",
"username": "abdullah",
"userEmail": "abdullah@example.com",
"extractedText": "الكلام هو اللفظ المركب المفيد بالوضع",
"language": "ar",
"tags": ["nahw"],
"categories": ["grammar"],
"pageCount": 12,
"totalPages": 12,
"pageLimit": 50,
"pageOffset": 0,
"hasMore": false,
"pages": [
{
"pageNumber": 1,
"text": "الكلام هو اللفظ المركب المفيد بالوضع",
"s3Key": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41/page-1.png",
"imageUrl": "https://sharh-app-ocr-cache.s3.amazonaws.com/arabic-vision/...signed...",
"isAIGenerated": true
}
],
"status": "completed",
"processingTime": 48213,
"metadata": {
"pagesProcessed": 12,
"usageInfo": {
"freePages": 12,
"overagePages": 0,
"overageChargeCents": 0
}
},
"createdAt": "2026-09-18T10:15:00.000Z",
"updatedAt": "2026-09-18T10:16:02.000Z",
"completedAt": "2026-09-18T10:16:02.000Z",
"visibility": "public"
}
}Update a digitized book
Updates book details, visibility, text and/or pages. Only the owner of the book, editors and admins may update it. All fields are optional and only supplied fields change. fileName and author are written to the linked File record. visibility is only applied when it is "public" or "private" (other values are silently ignored). tags and categories accept an array or a comma-separated string. pages can be an array of page objects, each upserted by pageNumber (text defaults to empty, s3Key to null and isAIGenerated to true when omitted; pages not listed are left untouched), or an object keyed by page number for partial updates where only the supplied fields of each page change. The response returns a summary of the book without text or pages.
Path parameters
bookTextIdstring | BookText ObjectId |
Body parameters
fileNamestring | New display name (stored on the File record). |
authorstring | New author (stored on the File record). |
languagestring | Language code. |
extractedTextstring | Replaces the book-level extracted text stored in the database. |
pagesarray | object | Either an array of { pageNumber, text?, s3Key?, isAIGenerated? } upserted by pageNumber, or an object keyed by page number, e.g. { "3": { "text": "...", "isAIGenerated": false } }, updating only the supplied fields. |
visibilitystring | "public" or "private". |
tagsstring[] | string | Array of tags or a comma-separated string. Replaces the existing tags. |
categoriesstring[] | string | Array of categories or a comma-separated string. Replaces the existing categories. |
Errors
401No bearer token provided403Invalid token, or the caller is not the owner, an editor or an admin ("Unauthorized to update this book")404Book not found500Error updating book
curl -X PATCH "https://app.ummahspot.com/ocr/books/:bookTextId" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"fileName":"string"}'{
"success": true,
"message": "Book updated successfully",
"book": {
"bookTextId": "66f1c0a7e4b0a1d2c3f4a5b6",
"jobId": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41",
"fileId": "66f1c0a6e4b0a1d2c3f4a5b1",
"fileName": "متن الآجرومية",
"author": "Ibn Ajurrum",
"visibility": "public",
"language": "ar",
"tags": ["nahw"],
"categories": ["grammar"],
"pageCount": 12,
"status": "completed",
"createdAt": "2026-09-18T10:15:00.000Z",
"updatedAt": "2026-09-18T11:02:40.000Z"
}
}List digitized books
Lists digitized books with offset pagination, without text or pages. Visibility rules: anonymous callers only see public books; signed-in members see public books plus their own; admins and editors see all books. Each item includes the owner (userId, username) and hasFullText (true when the full text is stored in S3), which are always present. userEmail and the internal metadata object are only included on items whose book the caller owns, or on every item when the caller is an admin or editor; for other callers — including anonymous ones — those two keys are absent from the item. The example below shows an item as seen by its owner. Default order is newest first. The name_asc/name_desc sort options sort on the referenced file name at the database level, where the book only holds a reference to the file, so they do not reliably order by name.
Query parameters
statusstring | Filter by job status: pending, processing, completed, failed or submission_failed. |
languagestring | Filter by language code, e.g. "ar". |
limitnumber | Maximum number of books to return.Default: 20 |
offsetnumber | Number of books to skip.Default: 0 |
sortstring | One of date_asc, date_desc, name_asc, name_desc, pages_asc, pages_desc. Unknown values fall back to date_desc.Default: date_desc |
tagsstring | Comma-separated tags (or repeated parameter); matches books having any of them. |
categoriesstring | Comma-separated categories (or repeated parameter); matches books having any of them. |
Errors
500Error retrieving books
curl -X GET "https://app.ummahspot.com/ocr/books" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"total": 42,
"limit": 20,
"offset": 0,
"books": [
{
"bookTextId": "66f1c0a7e4b0a1d2c3f4a5b6",
"jobId": "0b9d6c1e-6f0a-4d5e-9a57-2f3d1c8e7a41",
"fileId": "66f1c0a6e4b0a1d2c3f4a5b1",
"fileName": "matn-al-ajrumiyyah.pdf",
"author": "Ibn Ajurrum",
"fileType": "application/pdf",
"fileSize": 1843200,
"userId": "65a0f3b2e4b0a1d2c3f4a111",
"username": "abdullah",
"userEmail": "abdullah@example.com",
"status": "completed",
"language": "ar",
"tags": ["nahw"],
"categories": ["grammar"],
"pageCount": 12,
"processingTime": 48213,
"metadata": {
"pagesProcessed": 12
},
"visibility": "public",
"hasFullText": true,
"createdAt": "2026-09-18T10:15:00.000Z",
"updatedAt": "2026-09-18T10:16:02.000Z",
"completedAt": "2026-09-18T10:16:02.000Z"
}
]
}Import a digitized book as a translation Book
Creates a new Book (the bilingual, line-based format used by the /books API) from a digitized book. Every OCR page becomes one line with the page text in the Arabic field and empty English, commentary and rootwords fields. The new book takes its title from the file name and its author from the file, is owned by the caller, and records metadata.importedFromVision = bookTextId. Only the owner of the digitized book, editors and admins may import it. Callers who are not editors or admins must additionally have an active Stripe subscription on the $50/month tier, otherwise 403 with error "subscription_required" is returned. The request has no body.
Path parameters
bookTextIdstring | BookText ObjectId of the digitized book to import |
Errors
400This book has no pages to import401No bearer token provided403Invalid token; caller is not the owner, an editor or an admin ("Unauthorized to import this book"); or no active $50/month subscription (error: "subscription_required")404Book not found500Unable to verify subscription status (error: "subscription_check_failed") or error importing book
curl -X POST "https://app.ummahspot.com/ocr/books/:bookTextId/import" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"success": true,
"message": "Book imported successfully",
"bookId": "66f1d4b3e4b0a1d2c3f4a7d9"
}Export a digitized book as a Word document
Generates a .docx file from all pages of a digitized book (sorted by page number) and returns it as a download. Only the owner of the book, editors and admins may export it; a public book cannot be exported by other users. Callers who are not editors or admins must also have an active subscription (checked before the book is looked up). The document is laid out right-to-left with an Arabic font when the book language is ar, ur, fa or he, otherwise left-to-right. Options control an optional title page (file name, author, language, page count, creation date), a "Page N" heading before each page, whether each page starts on a new page, and the body font size.
Path parameters
bookTextIdstring | BookText ObjectId |
Query parameters
includeTitlePagestring | "true" to add a title page; any other value disables it.Default: true |
includePageHeadingsstring | "true" to add a "Page N" heading before each page; any other value disables it.Default: true |
pageBreaksstring | "true" to start every OCR page on a new document page; any other value produces one continuous section.Default: true |
fontSizenumber | Body font size in half-points (24 = 12pt). Clamped to the range 16-40; invalid values fall back to 24.Default: 24 |
Errors
401No bearer token provided403Invalid token; no active subscription (error: "SUBSCRIPTION_REQUIRED", subscriptionRequired: true); or caller is not the owner, an editor or an admin ("Unauthorized to export this book")404Book not found500Error exporting book
curl -X GET "https://app.ummahspot.com/ocr/books/:bookTextId/export/docx" \
-H "Authorization: Bearer $SHARH_TOKEN"Binary file download, not JSON.
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Content-Disposition: attachment; filename="<url-encoded sanitized file name>.docx"
The body is the generated Word document.