Documents

Read Document

Turn a Word, Excel, PowerPoint, PDF, OpenDocument, Markdown, HTML, CSV, JSON, XML, YAML or text file into text, Markdown, HTML, metadata, structure and rows.

Read Document (doc_read) reads one file and gives the rest of the workflow everything in it: plain text, Markdown, sanitised HTML, metadata (title, author, dates, page / slide / sheet counts, word and character counts, reading time), a structure specific to the format, and rows — the first sheet of a workbook or the first table of a document — ready for a Loop.

The Data panel listing Read Document's outputs: text, markdown, html, rows, metadata and structure
A later step picks Read Document's outputs from the Data panel — text, rows, metadata and structure.

Inputs

Document (source): a file object (a form's Attachments field, a file from another step), a list of files (the first is read), a storage:// reference, a URL, base64 content or text. See File inputs and Supported formats.

Options

SettingKeyDefaultWhat it does
Pages / slidespagesallPDF pages or PowerPoint slides: 3, 2-5 or 4- (to the end)
Sheetsheetall sheetsA sheet name or 1-based number. With none, every sheet is read and rows holds the first
Rangerangethe used rangeAn A1 range such as B2:F40 or Orders!B2:F40
Header rowheader_rowautoauto detects the header (title and blank rows above the table are skipped), first uses the first non-empty row, none names the columns column_1, column_2…, or a 1-based row number within the range
Max rowsmax_rowsallData rows kept per sheet
CSV delimiterdelimiterdetectedFor CSV and TSV files (not shown in the designer; set it in the JSON)
Tablesinclude_tablestrueTables of Word, HTML and Markdown files, and tables found in text PDFs
List embedded imagesinclude_imagesfalseNames and sizes of images in Word and PowerPoint files (structure.images)
Tracked changes (Word)tracked_changesacceptaccept reads the document as if every change were accepted; show keeps deletions and insertions in the text (struck through and underlined in html)
Max charactersmax_chars2,000,000Characters kept in text, markdown and html (at least 1,000); truncated is true when it cut
OCRocroffoff, auto (pages without a text layer, and images) or force (every page). See OCR
OCR languagesocr_languagesengTesseract language codes, eng+deu for several (up to 5)
Output variableoutput_vardocumentWhere the result goes

Outputs

VariableContents
{{document}}The whole result (below)
{{document_text}}Plain text
{{document_rows}}Rows of the first (or chosen) sheet, or of the first table: a list of objects keyed by the header
{{document_count}}Number of rows
{{document_metadata}}The metadata, also as {{<node label>_document_metadata}}

With another Output variable, document is replaced by that name ({{invoice}}, {{invoice_text}}, …).

Reading a Word contract between Northwind Traders and Exotic Liquids:

{{document}}
{
  "file": { "name": "supply-agreement.docx", "size": 12840, "mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
  "type": { "format": "docx", "mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "extension": "docx", "label": "Word document", "detected_by": "content", "is_text": false, "extension_mismatch": false },
  "text": "Supply Agreement\nBetween Northwind Traders and Exotic Liquids. See the terms.\nTerm\n- The term is 24 months.\n- Contact: orders@northwind.example, +64 9 555 0100\nProduct\tPrice\nChai\t18.00\nChang\t19.00",
  "markdown": "# Supply Agreement\n\nBetween **Northwind Traders** and *Exotic Liquids*. See [the terms](https://northwind.example/terms).\n\n## Term\n\n1. The term is 24 months.\n1. Contact: orders@northwind.example, +64 9 555 0100\n\n| Product | Price |\n| --- | --- |\n| Chai | 18.00 |\n| Chang | 19.00 |\n",
  "html": "<h1>Supply Agreement</h1>\n<p>Between <strong>Northwind Traders</strong> and <em>Exotic Liquids</em>. See <a href=\"https://northwind.example/terms\" rel=\"noopener noreferrer\" target=\"_blank\">the terms</a>.</p>\n<h2>Term</h2>\n…",
  "metadata": {
    "title": "Supply Agreement", "author": "Andrew Fuller", "subject": null, "keywords": null,
    "created": "2026-09-01T10:00:00Z", "modified": "2026-10-02T11:00:00Z", "last_modified_by": "Nancy Davolio",
    "pages": 2, "slides": null, "sheets": null, "format": "docx", "format_label": "Word document",
    "words": 30, "characters": 196, "characters_no_spaces": 166, "lines": 8, "paragraphs": 1, "sentences": 7, "reading_time_minutes": 1
  },
  "structure": {
    "headings": [{ "level": 1, "text": "Supply Agreement" }, { "level": 2, "text": "Term" }],
    "links": [{ "text": "the terms", "href": "https://northwind.example/terms" }],
    "tables": [{ "header": ["Product", "Price"], "rows": [["Chai", "18.00"], ["Chang", "19.00"]], "records": [{ "Product": "Chai", "Price": "18.00" }, { "Product": "Chang", "Price": "19.00" }] }],
    "comments": [{ "id": "0", "author": "Nancy Davolio", "date": "2026-10-01T09:00:00Z", "text": "Check the term length" }],
    "tracked_changes": { "insertions": 1, "deletions": 1, "items": [{ "type": "deletion", "author": "Nancy Davolio", "date": "2026-10-02T10:00:00Z", "text": "12" }, { "type": "insertion", "author": "Nancy Davolio", "date": "2026-10-02T10:00:00Z", "text": "24" }] }
  },
  "rows": [{ "Product": "Chai", "Price": "18.00" }, { "Product": "Chang", "Price": "19.00" }],
  "truncated": false,
  "warnings": []
}

What structure holds depends on the format:

Formatstructure
Wordheadings, links, tables, comments, tracked_changes, images (when listed)
Excel, OpenDocument spreadsheet, CSVsheets (each with name, range, row_count, column_count, header_row, headers, rows, values), sheet_names; Excel also defined_names
PowerPointslides (number, title, text, notes), images (when listed)
PDFpages (number, text, characters, has_text_layer, image_count, width, height, ocr), outline (bookmarks), links, form_fields, tables
Markdownfront_matter, headings, links, tables
JSON, YAML, XMLdata (the parsed value; a JSON list of objects is also rows)

Spreadsheet values are typed: numbers stay numbers, dates become YYYY-MM-DD, formulas give their computed value. PDF table cells are text.

The run history shows a summary, not the text:

json
{ "variable": "document", "file": "supply-agreement.docx", "format": "docx", "pages": 2, "words": 30, "characters": 196, "rows": 2, "tables": 1, "truncated": false, "text_preview": "Supply Agreement Between Northwind Traders and Exotic Liquids. See the terms. Term - The term is 24 months. - Contact: orders@northwind.example, +64 9 555 0100 Product Price Chai 18.00 Chang 19.00" }

On the canvas

The node shows what it reads into, with the OCR mode, sheet and pages when set: → document, sheet Orders → orders, OCR auto → invoice.

Limits

  • The file: DOCUMENT_MAX_MB (25 MB); unpacked Office files DOCUMENT_MAX_UNPACKED_MB (250 MB); 2,000,000 spreadsheet cells; DOCUMENT_MAX_PDF_PAGES (500) pages — a warning says when only part was read.
  • Time: DOCUMENT_TIMEOUT_SECONDS (60), or DOCUMENT_OCR_TIMEOUT_SECONDS (120) when it is longer and OCR is on.
  • Output: 2,000,000 characters of text, Markdown and HTML each (lower with Max characters).
  • Word 97–2003 (.doc), Excel 97–2003 (.xls) and PowerPoint 97–2003 (.ppt) files cannot be read: save them in the current format, or convert them to PDF with the external converter first.
  • PDF tables are found from the positions of text on the page — reliable for simple grids, not for merged cells or tables without aligned columns. A PDF page without a text layer is listed with has_text_layer: false and a warning until you turn OCR on.

Security notes

The file is read for this workspace only, Office files are checked for ZIP bombs, XML parts with a DOCTYPE are refused, html is sanitised, and PDFs are read with eval off. See Security.

Example

Read the Orders sheet of an uploaded workbook
{
  "type": "doc_read",
  "label": "Read orders",
  "config": {
    "source": "{{form.workbook}}",
    "sheet": "Orders",
    "header_row": "auto",
    "max_rows": 5000,
    "output_var": "orders"
  }
}

Canvas: sheet Orders → orders. Then loop over {{orders_rows}}; inside the loop {{item.OrderID}}, {{item.Customer}}… are the current row.

Works well with