Key takeaways
- Preserve the original upload and record its checksum, MIME type, filename, and source context before processing.
- Route PDFs to /document/ocr. Consider /image/ocr for short text in images, and convert supported office inputs to PDF before document OCR.
- Treat OCR output as derived data that needs application validation, review rules, and a link to the source document or image.
Document intake is a routing problem before it is an OCR problem. A reliable pipeline first identifies what arrived, preserves the original, converts only where necessary, extracts text with the right Robot, and records enough provenance to explain every searchable result later.
What matters most
- Separate Assembly completion from search-index publication, and verify required exported files before starting downstream indexing.
- Use idempotent identifiers and versioned output paths when the same source may be uploaded again or corrected later.
Start with an intake contract
A document pipeline becomes difficult when the first step accepts anything and the last step assumes everything was understood. Define the intake contract before choosing Robots: which sources are allowed, what the application calls a document, which metadata is required, how large an upload may be, and which outputs downstream systems expect. Enforce a page-count limit in the application before creating the Assembly; the sample below does not cap pages. Large or page-heavy PDFs can exceed the Robot’s fixed polling window while waiting for an asynchronous provider job, even if that job later finishes. Retrying unchanged input does not widen this window; handle repeated timeouts as a failure and reduce or split the input before another attempt.
Keep the uploaded original as the immutable reference. A normalized PDF, an OCR text file, and a search record are derivatives with different lifetimes and failure modes. Give them a shared application identity so a reviewer can move from a search hit to the source bytes that produced it. Page-specific navigation requires a separate page mapping.
Identify the source
Record a stable document ID, checksum, tenant, uploader, filename, MIME type, and receipt time.
Name the outputs
Define whether the workflow returns a normalized PDF, OCR text, structured metadata, thumbnails, or all of them.
Separate state
Track uploaded, normalized, extracted, reviewed, indexed, and failed states independently.
Route each input to the right Robot
A filename extension does not tell you whether a PDF contains selectable text or only scanned pixels. Use Transloadit’s resolved ${file.mime} and the application’s policy to route inputs. MIME resolution combines embedded metadata, extension-based information, and content sniffing; the extension can win when embedded metadata is unavailable. This gate is not proof of the file’s contents. If your intake contract requires strict content validation, validate the stored bytes in your application. /file/filter can route files with accepts or declines conditions on ${file.mime}; use error_on_decline when an unsupported input should fail the Assembly.
PDFs can go directly to /document/ocr. Use /image/ocr for short text in photographs and screenshots; its documentation warns that dense document images may be less accurate. For office inputs such as DOCX, use /document/convert to convert to PDF, checking its documented input formats first. This workflow uses its PDF output; other output formats are available, but PDF input is not supported. A Robot can skip inputs it does not accept without failing the Assembly, so add an explicit MIME gate and verify required outputs.
PDF branch
Run /document/ocr for PDFs, including scanned PDFs with no usable text layer; choose json or text to emit a separate artifact. The meta format instead stores the recognition payload as ${file.meta.recognized_text} without creating an OCR file. Use list granularity for strings; the default full granularity stores text and polygon objects.
Image branch
Run /image/ocr for short text in images. Its input preparation re-encodes to PNG when an image exceeds 2000 pixels per side, is not JPEG/PNG, or has orientation metadata; it fits within that size and auto-rotates when needed. Test dense scans carefully; image-to-PDF preparation is outside this guide.
Normalization branch
Convert supported office or source formats before OCR when the original type is not accepted by the OCR Robot.
Preserve provenance through derived outputs
Text without provenance is difficult to trust. Store the source document ID, output version, and extraction timestamp with the extracted text. The AWS sample below returns document-level JSON without page attribution. For page-specific search hits, the application must split PDFs before upload and record the original page numbers; this preparation is outside the guide. The sample allows one input per Assembly and uses fixed object names. A page pipeline needs one Assembly per page with page-qualified object keys and a mapping to the parent document. Reusing the same document_id and output_version for multiple pages would overwrite their objects. Never infer the mapping from OCR array order. If the source is replaced, create a new version and let the indexer retire the old record only after the new output is verified.
Keep OCR text separate from the normalized document. A reviewer may need the original scan, the converted PDF, and the extracted text side by side. That relationship is also useful when a different OCR provider produces a better result.
Use stable identity
The source ID should survive retries while each derived output receives a version.
Page context is not in the output
Keep source links for every result. AWS output has no page numbers; page navigation needs the mapping recorded when single-page inputs were created.
Do not overwrite corrections
A corrected upload should create a new version and an explicit replacement event.
Make review and indexing explicit
The Assembly Status ok value ASSEMBLY_COMPLETED, observed through polling or notifications, means no unignored error stopped the Assembly and its declared jobs, including storage jobs, finished. A Step can skip an input, and ignore_errors can suppress a Step failure, so completion alone does not prove that the required artifacts exist. It also does not mean your application’s search index accepted the text or a human approved a sensitive record. Publish a downstream event only after the outputs you require exist and have passed their checks.
/document/ocr does not expose confidence scores, so this Template cannot route on a provider confidence threshold. Use application checks such as missing output files, empty extracted text, implausibly little text for the reported page count, missing expected phrases, or a high-consequence document class to trigger review. For ordinary documents, an automated path can still record the checks and retain a way to reprocess the source.
Verify outputs
Check expected files, page counts, non-empty text, and metadata before indexing.
Review exceptions
Use a human queue for ambiguous or high-consequence results instead of silently publishing them.
Index idempotently
Use the source ID and output version as the index key so retries do not duplicate records.
Version corrections without losing evidence
OCR corrections and source replacements should create a new derived version. Keep the previous source, output, and review decision long enough for your retention policy so an index result can be explained after a correction.
Use the document ID and output version as the indexing key. Reuse a version only when retrying the same processing attempt; allocate a new version for corrections, changed providers, or deliberate reprocessing. Serialize writes for a version so concurrent retries cannot overwrite a newer accepted result.
Retain the source
Keep the original bytes available for review and future OCR settings or provider changes.
Replace atomically
Publish the new index version only after its required files and review state have been verified.
Start with a bounded PDF Template
The example below implements only the PDF branch: it allows at most one upload, rejects non-PDF MIME types, requests AWS OCR JSON, and exports the source and JSON to separate fixed keys under one application-controlled document version. Save this JSON as stored Template content, not as complete Assembly request parameters. Your server supplies the Auth Key and signature when creating the Assembly. Test representative PDFs for output shape, usable text, and failure behavior before extending the recipe. auth.max_size limits total expected upload bytes, including request overhead: 104857600 bytes is 100 MiB, not a per-file allowance. It does not guarantee rejection before a resumable upload transfers bytes, or that OCR will finish.
Create a Transloadit account and save the Assembly Instructions as a Template. Create an S3 Template Credential named document-output with key, secret, bucket, and bucket_region; see Template Credentials for setup. For this recipe, the server must construct and submit the entire Assembly request, including the upload, rather than hand signed params to a browser. It must supply and validate non-empty, authorized, path-safe document_id and output_version values in the request’s fields. These become the ${fields.document_id} and ${fields.output_version} variables in the storage paths. Multipart fields also contribute Assembly fields and are not covered by the params signature, so signing alone does not authorize a storage prefix. Do not forward client-supplied fields. Verify returned storage keys against the server’s expected prefix before indexing. Save allow_steps_override: false in the stored Template to prevent runtime Step overrides when using template_id. Keep the bucket private with Block Public Access enabled and check reader authorization; acl: bucket-default omits an object ACL and relies on the bucket’s access policy.
This sample pins provider: aws to make the provider choice explicit, not to guarantee identical responses. The default is auto. Use AWS Textract’s current limits as the authority for AWS language support. Check provider support before choosing AWS or GCP; their JSON structures differ. Test your document languages and layouts with the chosen provider, and expect responses to change as provider models evolve.
The sample uses format: "json" with granularity: "list" to export an array of recognized line strings for indexing and text-volume checks. This avoids the default full-granularity artifact’s mixture of page, line, and word entries, which can repeat text. format: "text" instead exports plain text and forces list granularity. Neither output includes page attribution or confidence. If you choose full granularity, its optional polygon points use one width and height from PDF metadata, becoming zeros when those dimensions are missing. Validate them before overlays, and never infer page identity from polygons or array order.
The AWS implementation builds this artifact from one provider response and does not follow pagination tokens. Long or dense PDFs can therefore have incomplete output even when OCR succeeds. Check both exported objects and extracted text volume against the reported page count, and send suspicious results to review. Those checks flag problems but do not prove complete coverage; compare against known text in representative source documents before publishing.
The fixed source.pdf and ocr.json object keys make retries target the same storage locations for a document version. They do not deduplicate Assemblies or make OCR deterministic; the application must serialize retries and keep the accepted result mapping. Compare the providers’ charges using Transloadit pricing, including page volume and other processing in the workflow. OCR’s provider-specific page charge is calculated from the input’s reported page_count; compare current charges before estimating the complete workflow.
{
"allow_steps_override": false,
"auth": {
"max_number_of_files": 1,
"max_size": 104857600
},
"steps": {
":original": {
"robot": "/upload/handle"
},
"pdf_only": {
"use": ":original",
"robot": "/file/filter",
"accepts": [["${file.mime}", "==", "application/pdf"]],
"error_on_decline": true,
"error_msg": "Upload a PDF document"
},
"extracted_text": {
"use": "pdf_only",
"robot": "/document/ocr",
"provider": "aws",
"format": "json",
"granularity": "list",
"result": true
},
"stored_source": {
"use": "pdf_only",
"robot": "/s3/store",
"credentials": "document-output",
"path": "documents/${fields.document_id}/${fields.output_version}/source.pdf",
"acl": "bucket-default",
"result": true
},
"stored_text": {
"use": "extracted_text",
"robot": "/s3/store",
"credentials": "document-output",
"path": "documents/${fields.document_id}/${fields.output_version}/ocr.json",
"acl": "bucket-default",
"result": true
}
}
}Test representative inputs
Verify both exported objects, non-empty extracted text, and plausible character volume for the reported page count before indexing. Test non-PDF rejection and retry handling.
Add branches deliberately
Extend the PDF recipe with conversion and image OCR branches only when their input contracts are explicit.
Technical details worth knowing
- /document/ocr accepts PDF input. /document/convert can produce PDFs from documented office formats, but cannot take a PDF as input.
- With format: json or format: text, /document/ocr returns a separate artifact. format: meta creates no OCR artifact and stores the recognition payload as ${file.meta.recognized_text}; its shape depends on granularity. No format adds a searchable text layer to the source PDF.
- For AWS, /document/ocr’s full-granularity artifact contains mapped text and optional bounding polygons, dropping the provider’s page numbers, block types, and confidence scores. It mixes page, line, and word entries, so text can recur; list granularity returns line strings instead.
- The AWS implementation maps one provider response without following pagination tokens, so a long or dense document’s OCR artifact may be incomplete even when the job succeeds.
- A Robot can skip an input it does not accept. Successful Assembly completion waits for declared jobs, including storage jobs, but skipped inputs and ignored errors still require output verification.
- /image/ocr re-encodes to PNG, fitting within 2000 pixels per side, when the image is not JPEG/PNG, exceeds that dimension, or has orientation metadata. Dense document images may be less accurate than short text in photos.
- auth.max_size caps the Assembly’s total expected upload bytes including request overhead, rather than each file separately; it does not guarantee rejection before a resumable transfer starts.
- The provider-specific document OCR charge is calculated using the input’s reported page_count. Include expected page volume when comparing providers and estimating the workflow’s cost.
A practical approach
- 1
Save the PDF Template, then have your server construct and submit the complete Assembly request with authorized document_id and output_version fields.
- 2
Reject non-PDF MIME types with the sample’s filter; enforce any page-count limit in the application before submission.
- 3
Store the OCR text or JSON artifact with the source under a shared document version, retaining their object keys in the application.
- 4
Send empty text, missing expected phrases, or structurally ambiguous results to a human review path before making them searchable.
When Transloadit is useful
The provided Template covers server-submitted PDF intake, OCR, and storage. The guide also describes conversion and image branches, but does not supply those Templates; add them only after testing their input contracts.
Architecture boundary
Transloadit can normalize uploaded files, extract text from supported inputs, and export the resulting artifacts. It does not provide a document-management system, search index, human review queue, or legal guarantee that OCR is accurate.
Frequently asked questions
Can OCR make every PDF searchable?
No. /document/ocr extracts separate text; it does not add a text layer to the source PDF. Recognition quality depends on scan quality, language, layout, and the document itself. Keep the original and review results where an error matters.
Should I convert every document to PDF first?
No. Convert only formats that need normalization for the next Robot. Unnecessary conversion adds work and can discard source-specific structure.
Where should the search index live?
In the application or search service that owns discovery. Transloadit can prepare and export the source and derived data, but it is not the document catalogue or search index.
How do I avoid duplicate documents after a retry?
Give the source a stable application ID and each derived result a version. Make the indexing operation replace that version idempotently.