Documents

Extract from Document

Pull tables, named values (regular expressions), emails, URLs, phone numbers, dates, amounts and percentages out of a document, and optionally insert the rows into a data table.

Extract from Document (doc_extract) reads a file and picks out structured data:

  • Tables — the tables of a Word, HTML, Markdown or text PDF file, or the sheets of a workbook or CSV — and their rows.
  • Key values — named regular expressions, such as invoice_number or po_number.
  • Finders — every email address, URL, phone number, date, money amount and percentage, normalised (ISO dates, numbers and currency codes).
  • Optionally, it inserts the rows into a data table in the same step.

Inputs

Document (source): a file object, a list (the first file), a storage:// reference, a URL, base64 content or text. See File inputs.

Options

SettingKeyDefaultWhat it does
Tablesextract_tablestrueInclude tables in the result (rows are found either way)
Rows fromtable_indexfirst tableWhich table rows comes from: empty for the first, a 1-based number (2), or all for the rows of every table one after the other
Key valuespatternsnoneA list of { "name", "pattern" }, optionally "all": true (every match, as a list), "group" (a capture group number or name) and "flags" (i, m, s, u)
FindfindersnoneAny of emails, urls, phones, dates, amounts, percentages
Date orderdate_orderDMYHow 03/04/2026 is read: DMY (3 April) or MDY (4 March). Unambiguous dates (31/12/2026, 2026-12-31, 1 March 2026, March 1, 2026) are read either way
Insert the rows into a data tableto_tablefalseWrite rows into a data table
Data tabletable_id— (required with to_table)The table to insert into
Column mappingcolumn_mappingautomaticA list of { "column": "<table column key>", "field": "<source header>" }. Without one, source headers that match a column's key or label (ignoring case, spaces and punctuation) are used
Sheetsheetall sheetsSpreadsheets: a sheet name or number
PagespagesallPDF pages
OCRocr, ocr_languagesoff, engScanned PDFs and images (see OCR)
Output variableoutput_varextracted

Patterns return the first capture group, or the whole match when there is none. They match within one line (a match never spans a line break) and are case-sensitive unless you add "flags": "i" (flags and groups are set in the JSON; the designer edits the name, the pattern and Every match). Each pattern goes through the ReDoS guard: one that could take exponential time (nested quantifiers such as (a+)+) is rejected, logged as a warning, and its value is null.

Outputs

VariableContents
{{extracted}}The whole result (below)
{{extracted_rows}}The rows: a list of objects keyed by the header
{{extracted_count}}Number of rows
{{extracted_<name>}}One per pattern, such as {{extracted_invoice_number}}

From an invoice PDF from Exotic Liquids, with a pattern for the invoice number and the email, date and amount finders:

{{extracted}}
{
  "file": "INV-2026-0042.pdf",
  "tables": [
    { "header": ["Product", "Qty", "Amount"], "rows": [["Chai", "10", "$180.00"], ["Chang", "5", "$95.00"]],
      "records": [{ "Product": "Chai", "Qty": "10", "Amount": "$180.00" }, { "Product": "Chang", "Qty": "5", "Amount": "$95.00" }] }
  ],
  "rows": [{ "Product": "Chai", "Qty": "10", "Amount": "$180.00" }, { "Product": "Chang", "Qty": "5", "Amount": "$95.00" }],
  "values": { "invoice_number": "INV-2026-0042" },
  "found": {
    "emails": ["accounts@exotic.example"],
    "dates": [{ "text": "15 October 2026", "date": "2026-10-15" }, { "text": "2026-11-14", "date": "2026-11-14" }],
    "amounts": [
      { "text": "$180.00", "value": 180, "currency": "USD" },
      { "text": "$95.00", "value": 95, "currency": "USD" },
      { "text": "NZ$275.00", "value": 275, "currency": "NZD" }
    ]
  },
  "table_rows_inserted": 2
}

found has one list per finder you chose: emails, urls and phones are strings; dates are { text, date } (date is YYYY-MM-DD); amounts are { text, value, currency }; percentages are { text, value }. Duplicates are removed. table_rows_inserted is there when Insert the rows into a data table is on.

The run history counts what was found:

json
{ "variable": "extracted", "file": "INV-2026-0042.pdf", "tables": 1, "rows": 2, "values": 1, "found": { "emails": 1, "dates": 2, "amounts": 3 }, "inserted": 2, "table_columns": ["product", "quantity", "amount"] }

About the finders

  • Amounts recognise currency symbols ($, €, £, ¥, ₹, NZ$, A$, AU$, C$, CA$, HK$, S$, US$) and ISO codes before or after the number (NZD 1,250.00, 1.250,00 EUR), with either decimal separator. A plain $ is read as USD: write NZ$ or the code when it matters.
  • Phone numbers are found by pattern (7–15 digits with spaces, dots, dashes, brackets or a leading +), so they can also pick up other grouped numbers, such as 2026-0042 in an invoice number. Check them, or use a pattern for the phone line instead.
  • Dates cover ISO (2026-10-15), numeric (15/10/2026, 15.10.26) and written forms (15 October 2026, Oct 15, 2026).

Inserting rows into a data table

With Insert the rows into a data table, rows are inserted into the chosen table in the same step (in batches of 500), and each new row fires the table's row-created workflows like any other insert. Values are copied as they are: PDF and Word table cells are text, spreadsheet cells keep their type. Mapping to a column the table doesn't have, or no matching columns at all, fails the step with a message naming the columns. Only tables of the run's workspace can be written to.

On the canvas

What it extracts and where rows go: tables · 1 pattern · emails, dates, amounts → Invoice lines.

Limits

  • At most 5,000 rows are written to a data table per run (the rest are left out, with a warning).
  • 100 patterns; 1,000 matches per pattern (with Every match); 1,000 results per finder.
  • Reading follows the read limits. The step's deadline is DOCUMENT_TIMEOUT_SECONDS (60) including any OCR: for long scans, read the file with Read Document and OCR first.
  • PDF tables are reconstructed from text positions (best effort).

Security notes

User patterns go through the ReDoS guard and run line by line on at most 100,000 characters per line. Rows are written only to a data table of this workspace (another workspace's table is "Data table not found"). See Security.

Example

json
{
  "type": "doc_extract",
  "label": "Extract invoice lines",
  "config": {
    "source": "{{form.invoice}}",
    "patterns": [{ "name": "invoice_number", "pattern": "Invoice\\s+(INV-[\\d-]+)" }],
    "finders": ["emails", "dates", "amounts"],
    "to_table": true,
    "table_id": "<invoice-lines-table-id>",
    "table_name": "Invoice lines",
    "column_mapping": [
      { "column": "product", "field": "Product" },
      { "column": "quantity", "field": "Qty" },
      { "column": "amount", "field": "Amount" }
    ]
  }
}

Canvas: tables · 1 pattern · emails, dates, amounts → Invoice lines.

Works well with