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_numberorpo_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
| Setting | Key | Default | What it does |
|---|---|---|---|
| Tables | extract_tables | true | Include tables in the result (rows are found either way) |
| Rows from | table_index | first table | Which 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 values | patterns | none | A 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) |
| Find | finders | none | Any of emails, urls, phones, dates, amounts, percentages |
| Date order | date_order | DMY | How 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 table | to_table | false | Write rows into a data table |
| Data table | table_id | — (required with to_table) | The table to insert into |
| Column mapping | column_mapping | automatic | A 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 |
| Sheet | sheet | all sheets | Spreadsheets: a sheet name or number |
| Pages | pages | all | PDF pages |
| OCR | ocr, ocr_languages | off, eng | Scanned PDFs and images (see OCR) |
| Output variable | output_var | extracted |
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
| Variable | Contents |
|---|---|
{{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:
{
"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:
{ "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 asUSD: writeNZ$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 as2026-0042in 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
{
"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
- Capture invoice PDFs into a table and Import an Excel file into a data table.
- Redact Document to share a copy without the details you just extracted — see Extract contact details and redact a copy.
- A Condition on
{{extracted_invoice_number}}(is empty) to send unreadable documents to a person. - Run Agent for layouts too varied for patterns.