> Source: https://txtfetch.com/solutions/document-workflows > Plain-text twin — every page on txtfetch.com has one. https://txtfetch.com/text --- # Turn incoming documents into structured action. Invoices, inbound email, and scanned forms all start the same way. Extract the text first, then classify, route, or act on it downstream. the-problem Document-driven workflows share the same first step: turn an incoming file into text before any business logic runs. That step is usually the least reliable part of the pipeline. The inputs are the least controlled: a photographed form, a forwarded email thread, a scan from a fax gateway. Stitching together OCR, a mail parser, and a document library often breaks on the one case nobody tested. how-txtfetch-solves-it txtfetch is the extraction step, not a workflow engine. POST the incoming file, or its URL, and get back plain text, whatever the source. Async mode with a webhook\_url means the workflow never blocks on OCR. The callback fires when the text is ready, and your automation, a queue worker, or a Lambda, picks up from there. - OCR runs automatically on scanned forms and photographed documents, with no separate vision service to wire in. - Email extraction (.eml/.msg/.mbox) includes headers and attachment text, useful for routing rules. - Webhook callbacks (webhook\_url) mean a long-running OCR job never holds a workflow connection open. - Webhook payloads are HMAC-signed and verifiable with the SDK's verifyWebhook or verify\_webhook helper. - Idempotency-Key support stops a retried webhook delivery from reprocessing the same document twice. - The same endpoint handles a PDF, a scanned form, and an email thread with no branch in your workflow code. - A queue worker, a Lambda function, or a Zapier automation can each pick up the extracted text the same way. curl ```curl curl -X POST https://api.txtfetch.com/v1/extract \ -H "Authorization: Bearer $TXTFETCH_KEY" \ -F file=@scanned-form.png ``` Python ```python import os import requests with open("scanned-form.png", "rb") as f: r = requests.post( "https://api.txtfetch.com/v1/extract", headers={"Authorization": f"Bearer {os.environ['TXTFETCH_KEY']}"}, files={"file": f}, ) print(r.json()["extracted_text"]) ``` JavaScript ```javascript import { readFile } from "node:fs/promises"; const file = new Blob([await readFile("scanned-form.png")]); const form = new FormData(); form.append("file", file, "scanned-form.png"); const res = await fetch("https://api.txtfetch.com/v1/extract", { method: "POST", headers: { Authorization: `Bearer ${process.env.TXTFETCH_KEY}` }, body: form, }); const { extracted_text } = await res.json(); console.log(extracted_text); ``` Go ```go package main import ( "bytes" "encoding/json" "fmt" "io" "mime/multipart" "net/http" "os" ) type extractResponse struct { Status string `json:"status"` ExtractedText string `json:"extracted_text"` } func main() { f, err := os.Open("scanned-form.png") if err != nil { panic(err) } defer f.Close() var body bytes.Buffer writer := multipart.NewWriter(&body) part, err := writer.CreateFormFile("file", "scanned-form.png") if err != nil { panic(err) } if _, err := io.Copy(part, f); err != nil { panic(err) } writer.Close() req, err := http.NewRequest("POST", "https://api.txtfetch.com/v1/extract", &body) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("TXTFETCH_KEY")) req.Header.Set("Content-Type", writer.FormDataContentType()) resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() var result extractResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { panic(err) } fmt.Println(result.ExtractedText) } ``` ``` { "status": "success", "extracted_text": "..." } ``` faq **Can txtfetch OCR a photographed form for an automation pipeline?**: Yes. POST the image (PNG/JPG/TIFF) to /v1/extract, and Tesseract OCR runs automatically. The response is the same { status, extracted_text } shape as any other format. **How do I avoid blocking a workflow on a slow OCR job?**: Pass async=true and supply a webhook_url. txtfetch calls it back with the result when extraction finishes, so the workflow can move on. **Are webhook deliveries verifiable, so I know they came from txtfetch?**: Yes. Each webhook is HMAC-SHA256 signed. Verify it with the SDK's verifyWebhook (JS) or verify_webhook (Python) helper. You can also reproduce the raw HMAC check documented at /docs/async. related-reading - [OCR scanned documents through one API →](https://txtfetch.com/blog/ocr-scanned-documents-api) - [Parsing Office docs into clean text →](https://txtfetch.com/blog/parse-office-docs-docx-pptx-xlsx-for-llms) - [Batch and large-document ingestion →](https://txtfetch.com/blog/batch-and-large-document-ingestion) - [Async jobs & webhooks →](https://txtfetch.com/docs/async) - [Error reference →](https://txtfetch.com/docs/errors) - [Wire this into a Zap with no txtfetch app →](https://txtfetch.com/integrations/zapier) - [Get an API key →](https://app.txtfetch.com/signup) other-solutions - [RAG & LLM ingestion →](https://txtfetch.com/solutions/rag-ingestion) - [Search indexing →](https://txtfetch.com/solutions/search-indexing) - [Invoice & receipt processing →](https://txtfetch.com/solutions/invoice-and-receipt-processing) - [Contract & legal review →](https://txtfetch.com/solutions/contract-and-legal-review) - [Resume & CV parsing →](https://txtfetch.com/solutions/resume-and-cv-parsing) - [Research & academic papers →](https://txtfetch.com/solutions/research-and-academic-papers) ## Start on the free plan. Run your own documents through it before you commit. The Hobby plan needs no card. [Get started →](https://app.txtfetch.com/signup) [See the pricing →](https://txtfetch.com/pricing)