Attach invoices and receipts to ledger transactions

Store an evidence record for each invoice or receipt and link it to the transaction it supports. Checks, exports, and year-end reviews can then point at the document behind every number. Stored evidence remains under encrypted local custody. Google Drive pull commands reach your authorized account before storing the downloaded bytes locally.

Before you start

You need:

  • An active taxpayer profile. Evidence is stored under the active profile; if none is set, the command refuses. See Set up your taxpayer profile.

  • A master-key passphrase. The tool prompts for it the first time it opens your encrypted storage in a session.

  • Transactions in your ledger. If your ledger is empty, see Import and manage transactions first.

  • The invoice or receipt as a PDF or image file. Cadrumo copies the file’s bytes into encrypted storage together with the facts you type, plus a content fingerprint and the original location as provenance. Your original file is never needed again after add.

Add an evidence record

Record the invoice file and its details, then view the stored record. The file path is the only required part; every metadata flag is optional. --iva-rate 21 means 21 %:

1Record the invoice file and its details as encrypted evidence.
aeat --format json app ledger evidence add fixtures/factura-material-oficina.pdf \
  --supplier Papelería Sol SL --invoice-number 2026-0142 --invoice-date 2026-03-10 \
  --taxable-base 100.00 --iva-rate 21 --iva-amount 21.00 --notes Office supplies, March
aeat --format json app ledger evidence add fixtures/factura-material-oficina.pdf `
  --supplier Papelería Sol SL --invoice-number 2026-0142 --invoice-date 2026-03-10 `
  --taxable-base 100.00 --iva-rate 21 --iva-amount 21.00 --notes Office supplies, March
{
  "active_profile": "docs-sequence-sandbox",
  "command": "ledger.evidence.add",
  "notices": [],
  "result": {
    "attachment_id": "725afbe3c64d4e96faaed8b63b83ed2e7df161334681d98d8b877bd7cddbfc16",
    "bucket_event_ids": [
      "55fe6f63d8763568849f8ca8f05b45fc7a714987593755df7695f448a4b48b92"
    ],
    "bucket_id": "<bucket-id>",
    "created_at": "2026-04-01T09:00:00Z",
    "evidence_id": "ee678fa9b328d79d",
    "invoice_date": "2026-03-10",
    "invoice_number": "sha256:d3693bea",
    "iva_amount": "21.00",
    "iva_rate": "21",
    "media_kind": "pdf",
    "notes": "Office supplies, March",
    "source_path": "fixtures/factura-material-oficina.pdf",
    "source_sha256": "725afbe3c64d4e96faaed8b63b83ed2e7df161334681d98d8b877bd7cddbfc16",
    "supplier": "Papelería Sol SL",
    "taxable_base": "100.00",
    "updated_at": "2026-04-01T09:00:00Z"
  },
  "schema_version": "2",
  "status": "success"
}
2Confirm the stored record carries the details you typed, with the invoice number shown as a short digest.
aeat --format json app ledger evidence view {evidence_id}
aeat --format json app ledger evidence view {evidence_id}
{
  "active_profile": "docs-sequence-sandbox",
  "command": "ledger.evidence.view",
  "notices": [],
  "result": {
    "attachment_id": "725afbe3c64d4e96faaed8b63b83ed2e7df161334681d98d8b877bd7cddbfc16",
    "bucket_event_ids": [],
    "bucket_id": "<bucket-id>",
    "created_at": "2026-04-01T09:00:00Z",
    "evidence_id": "ee678fa9b328d79d",
    "invoice_date": "2026-03-10",
    "invoice_number": "sha256:d3693bea",
    "iva_amount": "21.00",
    "iva_rate": "21",
    "media_kind": "pdf",
    "notes": "Office supplies, March",
    "source_path": "fixtures/factura-material-oficina.pdf",
    "source_sha256": "725afbe3c64d4e96faaed8b63b83ed2e7df161334681d98d8b877bd7cddbfc16",
    "supplier": "Papelería Sol SL",
    "taxable_base": "100.00",
    "updated_at": "2026-04-01T09:00:00Z"
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the evidence record stored the supplier and invoice details.

The command prints the evidence ID. Note the full ID down - later commands need it. Add what you know now; update the rest later.

The stored record keeps the invoice number you typed, but evidence add, view, list, update, and remove show only a short digest of it (sha256: followed by eight hexadecimal characters), not the number itself.

Attach an evidence record to a transaction

Attach the evidence record to the transaction it supports. The sequence records an evidence file and an expense, then attaches one to the other:

1Attach the evidence record to the transaction it supports.
aeat app ledger attach {transaction_id} --purchase-invoice-evidence-id {evidence_id}
aeat app ledger attach {transaction_id} --purchase-invoice-evidence-id {evidence_id}
ID	0cd656fe8ae4c7387bef5c335a4fcec3e8ffe314d34269147920f60a72052eec
Date	2026-03-10
Amount	121
Description	Material de oficina
Review status	pending
2Confirm the transaction now carries the purchase-invoice evidence.
aeat --format json app ledger view {transaction_id}
aeat --format json app ledger view {transaction_id}
{
  "active_profile": "docs-sequence-sandbox",
  "command": "ledger.view",
  "notices": [],
  "result": {
    "bucket_id": "<bucket-id>",
    "review_status": "pending",
    "transaction": {
      "amount": "121",
      "attachment_ids": [],
      "booked_date": "2026-03-10",
      "business_classification": "NOT_YET_PROCESSED",
      "business_pct": null,
      "category_id": "material_oficina",
      "classification_confidence": null,
      "classification_reason": "",
      "classified_at": null,
      "classified_by": "auto",
      "counterparty": "",
      "counterparty_country": null,
      "counterparty_identification_state": null,
      "created_at": "2026-04-01T09:00:00+00:00",
      "currency": "EUR",
      "date": "2026-03-10",
      "description": "Material de oficina",
      "direction": "OUTGOING",
      "fx_rate": null,
      "invoice_id": null,
      "irpf_category": null,
      "iva_amount": "21",
      "iva_category": null,
      "iva_rate": "0.21",
      "lifecycle_state": "ACTIVE",
      "m210_income_classification": null,
      "modified_at": "2026-04-01T09:00:00+00:00",
      "notes": "",
      "prorrata_reference": null,
      "purchase_invoice_evidence_id": "4fbeef4e52f5c0a8",
      "source_jurisdiction": "ES",
      "taxable_base": "100",
      "transaction_id": "0cd656fe8ae4c7387bef5c335a4fcec3e8ffe314d34269147920f60a72052eec",
      "usage_ratio_id": null,
      "value_date": null,
      "value_in_eur": null
    },
    "transaction_id": "0cd656fe8ae4c7387bef5c335a4fcec3e8ffe314d34269147920f60a72052eec"
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the purchase-invoice evidence attached to the transaction.

A transaction carries at most one purchase-invoice evidence record. The command refuses a second one, and refuses re-attaching the same one.

Do not reach for aeat app ledger link here. attach and link are different operations on the same transaction: attach carries the evidence document, while link binds the transaction to an invoice and requires --invoice-id. That id comes from an imported, reconciled, or manually added invoice - the id aeat app ledger invoice add prints is exactly the one to pass.

For most receipts and invoices, --purchase-invoice-evidence-id above is the path to use; the evidence id comes straight from evidence add.

The attach command also has an --attachment-id option (repeatable) for a generic secure attachment that does not carry the purchase-invoice role. It expects the 64-character content id of a blob already in encrypted attachment storage, and it refuses any id that has no stored blob (attachment_ids must reference existing secure attachment manifests and blobs). No operator command currently prints that 64-character id - the evidence_id from evidence add is a different, shorter id and is not accepted here. Until a command surfaces the attachment id, use --purchase-invoice-evidence-id or evidence pull instead.

Pull a document from Google Drive instead

When the document lives in Google Drive, pull it straight into encrypted evidence storage. This command reaches Google Drive, so it runs against your own authorized account rather than in the documentation sandbox:

1Pull the Drive document straight into encrypted evidence storage.
aeat app ledger evidence pull <transaction-id> --source GOOGLE_DRIVE --reference \
  <drive-file-id> --note Supplier invoice
aeat app ledger evidence pull <transaction-id> --source GOOGLE_DRIVE --reference `
  <drive-file-id> --note Supplier invoice

The command downloads the Drive file, stores its bytes encrypted with the transaction, and keeps the original link as provenance. Evidence always carries the document itself, never a bare link: Gmail links, arbitrary URLs, and Drive files outside the granted scope are refused. For a refused source, download the document yourself and attach it with aeat app ledger evidence add or aeat app ledger attach --attachment-id.

Bulk-fetch every invoice in a Drive folder

Fetch every PDF and image invoice in one Drive folder at once, instead of one document at a time. Like evidence pull, this command reaches Google Drive and runs against your own authorized account:

1Fetch every PDF and image invoice in one Drive folder at once.
aeat app ledger evidence pull-all --folder <drive-folder-id-or-url> --note \
  Q1 supplier invoices
aeat app ledger evidence pull-all --folder <drive-folder-id-or-url> --note `
  Q1 supplier invoices

The command lists the folder’s contents, downloads each PDF or image, and stores every file as encrypted evidence. Fetched files are not linked to a transaction yet; bind each one afterward with aeat app ledger attach --attachment-id <attachment-id>.

Re-run the same command any time. A file already fetched is recognized by its content and is not stored twice. A file outside the granted Drive scope is refused individually and does not stop the rest of the sweep; download it yourself and attach it with aeat app ledger attach --attachment-id.

Gmail bulk-fetch is not available yet.

Invoice records are a separate feature

An invoice record - who owes whom, for what amount - is not a stored document. Manage business invoices owns invoice records: registering issued and received invoices, the reconciliation catalogue, and linking a catalogue invoice to the transaction that settles it. This page is about the documents - PDFs and images - you store as evidence and link to transactions.

List, view, update, and remove evidence records

List every stored record, view one in full, update its details, and remove one you no longer need. The sequence records an evidence file, lists and inspects it, changes the supplier, and confirms the change:

1List every stored evidence record.
aeat app ledger evidence list
aeat app ledger evidence list
evidence_id	media_kind	supplier	invoice_number	invoice_date	taxable_base	notes
4fbeef4e52f5c0a8	pdf	Papelería Sol SL	sha256:d3693bea	2026-03-10	100.00	-
2View one evidence record in full.
aeat app ledger evidence view {evidence_id}
aeat app ledger evidence view {evidence_id}
evidence_id	4fbeef4e52f5c0a8
bucket_id	<bucket-id>
source_path	fixtures/factura-material-oficina.pdf
source_sha256	725afbe3c64d4e96faaed8b63b83ed2e7df161334681d98d8b877bd7cddbfc16
media_kind	pdf
supplier	Papelería Sol SL
invoice_number	sha256:d3693bea
invoice_date	2026-03-10
taxable_base	100.00
iva_rate	21
iva_amount	21.00
notes	-
created_at	2026-04-01T09:00:00Z
updated_at	2026-04-01T09:00:00Z
3Update a detail on the record - the same optional flags as add.
aeat app ledger evidence update {evidence_id} --supplier Papelería Central SL
aeat app ledger evidence update {evidence_id} --supplier Papelería Central SL
evidence_id	4fbeef4e52f5c0a8
bucket_id	<bucket-id>
source_path	fixtures/factura-material-oficina.pdf
source_sha256	725afbe3c64d4e96faaed8b63b83ed2e7df161334681d98d8b877bd7cddbfc16
media_kind	pdf
supplier	Papelería Central SL
invoice_number	sha256:d3693bea
invoice_date	2026-03-10
taxable_base	100.00
iva_rate	21
iva_amount	21.00
notes	-
created_at	2026-04-01T09:00:00Z
updated_at	2026-04-01T09:00:00Z
bucket_event_ids	8f439cce2a9b8972cff6a94ed9e944438548dcd2af320e2f2f4039d9b1c2d99f
4Confirm the supplier now reads the updated value.
aeat --format json app ledger evidence view {evidence_id}
aeat --format json app ledger evidence view {evidence_id}
{
  "active_profile": "docs-sequence-sandbox",
  "command": "ledger.evidence.view",
  "notices": [],
  "result": {
    "attachment_id": "725afbe3c64d4e96faaed8b63b83ed2e7df161334681d98d8b877bd7cddbfc16",
    "bucket_event_ids": [],
    "bucket_id": "<bucket-id>",
    "created_at": "2026-04-01T09:00:00Z",
    "evidence_id": "4fbeef4e52f5c0a8",
    "invoice_date": "2026-03-10",
    "invoice_number": "sha256:d3693bea",
    "iva_amount": "21.00",
    "iva_rate": "21",
    "media_kind": "pdf",
    "notes": "",
    "source_path": "fixtures/factura-material-oficina.pdf",
    "source_sha256": "725afbe3c64d4e96faaed8b63b83ed2e7df161334681d98d8b877bd7cddbfc16",
    "supplier": "Papelería Central SL",
    "taxable_base": "100.00",
    "updated_at": "2026-04-01T09:00:00Z"
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the evidence record's supplier was updated.

Remove an evidence record you no longer need, addressing it by id:

1Remove an evidence record by id, confirming with --yes.
aeat app ledger evidence remove {evidence_id} --yes
aeat app ledger evidence remove {evidence_id} --yes
evidence_id	4fbeef4e52f5c0a8
bucket_id	<bucket-id>
source_path	fixtures/factura-material-oficina.pdf
source_sha256	725afbe3c64d4e96faaed8b63b83ed2e7df161334681d98d8b877bd7cddbfc16
media_kind	pdf
supplier	Papelería Sol SL
invoice_number	sha256:d3693bea
invoice_date	2026-03-10
taxable_base	100.00
iva_rate	21
iva_amount	21.00
notes	-
created_at	2026-04-01T09:00:00Z
updated_at	2026-04-01T09:00:00Z
bucket_event_ids	e0cd1c6d0c07c20521672f7d19db3f8a22429bba593e3b33876e52d429a53c13
2Confirm the removed evidence id is no longer addressable, regardless of other evidence already recorded on this page.
aeat --format json app ledger evidence view {evidence_id}
aeat --format json app ledger evidence view {evidence_id}
{
  "active_profile": "docs-sequence-sandbox",
  "command": "ledger.evidence.view",
  "error": {
    "action": {
      "action": null,
      "argument_bindings": [],
      "conditionality": "not_applicable",
      "evidence": [
        {
          "condition_id": "ledger.evidence.reference_resolves",
          "evidence_id": "ledger.evidence.reference_resolves.observation",
          "provenance": "application_state",
          "values": {
            "evidence_record_present": false
          }
        }
      ],
      "failed_condition_id": "ledger.evidence.reference_resolves",
      "missing_argument_names": [],
      "no_recovery_outcome": "operator_decision"
    },
    "category": "REFUSED",
    "code": "REFUSED_LEDGER_EVIDENCE_NOT_FOUND",
    "context": null,
    "message": "No purchase-invoice evidence exists with the identifier provided.",
    "retryable": false,
    "runbook_id": null,
    "trace_id": null
  },
  "notices": [],
  "schema_version": "2",
  "status": "error"
}

Confirm the evidence record no longer appears in the catalogue.

Removing applies to evidence records, not transactions. To fix a transaction row itself, see Correct mistakes in your ledger.

After you correct a row

If you merge or split transactions, check the new rows and re-attach evidence where needed. Correct mistakes in your ledger covers row changes.

Where evidence shows up

Ledger exports include the evidence link with each transaction. Ledger-derived calculations carry the evidence trail through to filing artifacts.

Read Import, export, and evidence for the difference between linked ledger evidence, an AEAT upload file, official filing proof, and an audit bundle.

Where to get help

  • If a command fails or refuses, see Troubleshooting.

  • If a term is unfamiliar, see the Glossary.

  • Before sharing command output with anyone, strip tax identifiers such as your NIF, CIF, DNI, NIE, or NII.

Next steps