Recued
Menu
← Back to recipes

Capture a receipt

by recued-core v2 8 views

Use “Capture a receipt” in Recued. Read one receipt locally — OCR for a photograph, text extraction for a PDF — pull out the merchant, date, total, VAT and payment method, and keep it as an expense.

Complete workflow

Expense Ledger 6 pinned recipes

Use Expense Ledger in Recued for expenses, receipts, bookkeeping, and ocr. It includes 7 built-in actions and 5 ready-to-run workflows. Actions that change data use Recued's approval controls.

You are viewing one recipe in this workflow. Installing it opens the complete pack so its other recipes and background automations arrive together.

Capture a receipt
This recipe

Use “Capture a receipt” in Recued. Read one receipt locally — OCR for a photograph, text extraction for a PDF — pull out the merchant, date, total, VAT and payment method, and keep it as an expense.

pack:expense-ledgerexpensesreceiptsocr
by recued-core pinned v2

Use “Review an expense” in Recued. Confirm or correct one captured expense. Whatever you leave blank keeps what was extracted; whatever you fill in replaces it.

pack:expense-ledgerexpensesreviewrecords
by recued-core pinned v3

Use “Show the expenses” in Recued. List captured expenses for a period with a running total, and count what still needs review. Read-only — it re-reads no receipt and re-runs no extraction.

pack:expense-ledgerexpensesboardrecords
by recued-core pinned v2

Use “Export a period” in Recued. Produce the CSV for one month — every reviewed expense, with totals. Only rows you have reviewed are included, so the export is what you stand behind rather than what the model read.

pack:expense-ledgerexpensesexportcsv
by recued-core pinned v2

Use “Import expenses (CSV)” in Recued. Reads a card or expense export you downloaded yourself, maps its columns the way you named them, and records every line as an expense. Re-importing an overlapping export adds only what is new.

pack:expense-ledgerexpensescsvimport
by recued-core pinned v1
View pack details, dependencies, and permissions →

How a receipt is read

A photograph (image/*) goes through tesseract OCR. A PDF goes through pdftotext. Both are small local tools; nothing is uploaded to a scanning service.

⛔⛔ A scanned PDF produces NO TEXT, and pdftotext reports that as SUCCESS. Verified live on 2026-07-30: a receipt image saved as a PDF came back exit 0 with zero characters. That is the ordinary case for anyone who scans rather than photographs, and it is the dangerous one — an empty string handed to the extractor returns a row of nulls that is indistinguishable from a real receipt with nothing on it.

⇒ This recipe checks the extracted text is non-empty before the model ever sees it. A PDF with no text layer is recorded no_text with a note telling you what to do — re-add it as a JPG or PNG and it will be OCR'd. It is never guessed at.

There is no local path from a scanned PDF to text. pdfimages can only LIST the images inside a PDF, not extract them, so the pack cannot pull the page out and OCR it for you. Photographing the receipt directly is the working path.

What is stored, and what is not

The extracted TEXT is kept as its own record so you can see what the model actually read. The receipt file itself is untouched — this recipe copies nothing and deletes nothing.

Every field is AI-extracted and unverified. The extractor is told not to invent a total, tax, date or merchant that is not printed, and to report its own confidence; a low-confidence row is still stored, marked needs_review. Nothing here is booked to your accounts — this is a ledger of what was read, which you confirm in Review an expense.

A likely duplicate is FLAGGED, never refused. If a receipt matching an existing merchant, date and total is captured again, the new row is kept and marked duplicate_suspect. Refusing it would be the wrong trade: photographing the same receipt twice is cheap to sort out, and a silently dropped receipt is an expense you never claim.

How it works 45 steps

Inspect the data fetches, transforms, gates, and output this recipe runs.

Process (45 steps)
record_id trim
Trim whitespace from setting record id
id_present compare
Check if record id has data
id_guard guard
Stop if id present equals
file_meta ?
defaults defaults
Apply defaults
is_image starts_with
Check if mime type starts with image/
is_pdf compare
Check if mime type equals application/pdf
readable_kind any
Check if any of the conditions are true
kind_guard guard
Stop if readable kind equals
existing_for_file ?
existing_rows default
Apply default
already_captured_count count
Count items in existing rows
already_captured compare
Check if already captured count is greater than 0
recapture_guard guard
Stop if already captured equals
ocr ?
skip: step.is_image equal false
pdf_text ?
skip: step.is_pdf equal false
text_ref coalesce
Use the first available value from: ocr file ref, pdf text file ref
text_produced compare
Check if text ref has data
text_guard guard
Stop if text produced equals
text_persist ?
text_record_id default
Apply default
text_read ?
skip: step.text_record_id is_empty
text_decoded decode_base64
Apply decode_base64
text_body trim
Trim whitespace from text decoded
text_length string_length
Apply string_length
has_text compare
Check if text length is greater than 20
captured_at date_add
Add 0 seconds to
no_text_note template
Generate text from a template
no_text_row ?
skip: step.has_text equal true
extract ?
skip: step.has_text equal false
defaults_2 defaults
Apply defaults
date_parts split
Split purchase date into parts
period_raw template
Generate text from a template
period ternary
Apply ternary
dup_candidates ?
skip: step.has_text equal false
dup_rows default
Apply default
same_total_rows filter
Filter by condition
dup_count count
Count items in same total rows
duplicate_suspect compare
Check if dup count is greater than 0
confident_enough compare
Check if confidence is at least setting min confidence
capture_status ternary
Apply ternary
dup_note ternary
Apply ternary
expense_row ?
skip: step.has_text equal false
expense_id coalesce
Use the first available value from: expense row record id, no text row record id
card to_summary
Format results as a summary card
Settings 3 configurable

Configurable at install. Defaults shown — change them anytime in Recued.

language setting = eng
record id setting = [object Object]
min confidence setting = 0.6

Trust & control

What installing this recipe would let it do. Recued grants these permissions at install — review them there before approving.

Data it touches

Warehousedata.file

About

Tags

pack:expense-ledgerexpensesreceiptsocrtesseractrecordsv2

Details

45 steps 3 configurable settings recipe_id: capture-receipt