Correct mistakes in your ledger¶
Fix wrong transactions in your ledger without losing track of what changed. Every correction leaves a visible history entry, so you can always see what a transaction looked like before and after. Everything happens on your computer - nothing is sent anywhere.
Before you start¶
You need:
An active taxpayer profile. Every command below works on 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.
A ledger with transactions in it.
To find the transaction you want to fix, list your transactions and view one in detail. The sequence below records an example expense, lists the ledger, and inspects that row:
aeat --format json app ledger add --date 2026-03-15 --amount 210.00 --direction OUTGOING \
--description Silla de oficina --idempotency-key correct-findaeat --format json app ledger add --date 2026-03-15 --amount 210.00 --direction OUTGOING `
--description Silla de oficina --idempotency-key correct-find{ "active_profile": "docs-sequence-sandbox", "command": "ledger.add", "notices": [], "result": { "bucket_event_ids": [ "a9ef0aa32d83010e35c72d35d6cc793ade13766f38979a9ae358646f1064352f" ], "bucket_id": "<bucket-id>", "review_status": "pending", "transaction": { "amount": "210", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Silla de oficina", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "f83add280832e5cbcbfc1da6432f5e6a7e02eb78ba74fc265eb483b805ee7afb", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "f83add280832e5cbcbfc1da6432f5e6a7e02eb78ba74fc265eb483b805ee7afb" }, "schema_version": "2", "status": "success" }
aeat app ledger listaeat app ledger listACCOUNTING LEDGER TRANSACTIONS id full_id date amount description IVA category review_status f83add28 f83add280832e5cbcbfc1da6432f5e6a7e02eb78ba74fc265eb483b805ee7afb 2026-03-15 210 Silla de oficina pending
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": "210", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Silla de oficina", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "f83add280832e5cbcbfc1da6432f5e6a7e02eb78ba74fc265eb483b805ee7afb", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "f83add280832e5cbcbfc1da6432f5e6a7e02eb78ba74fc265eb483b805ee7afb" }, "schema_version": "2", "status": "success" }
Confirm the inspected transaction is the one you want to fix.
The view command reports the id, amount, direction, description, and lifecycle state. The transactions guide covers listing and filtering in depth.
Pick your fix¶
If one transaction has a wrong amount, date, or text, update it.
If a transaction should not exist at all, remove it.
If the same purchase was imported twice, remove the duplicate - or archive it to keep a deliberate trace of it.
If one payment covers two different things, split it.
If you split something and want it back together, merge the parts.
If you’re unsure about a transaction and want it out of the way, stash it.
If you want to keep a transaction in history but out of everyday lists, archive it.
If you stashed or archived a transaction by mistake, restore it to active.
Update fields on a transaction¶
Change one or more fields directly. The sequence records a chair at the wrong price, corrects the amount and description, and confirms the correction:
aeat --format json app ledger add --date 2026-03-15 --amount 100.00 --direction OUTGOING \
--description Office chair --idempotency-key correct-updateaeat --format json app ledger add --date 2026-03-15 --amount 100.00 --direction OUTGOING `
--description Office chair --idempotency-key correct-update{ "active_profile": "docs-sequence-sandbox", "command": "ledger.add", "notices": [], "result": { "bucket_event_ids": [ "47c494e7c2a1631efad4835b3c50110ccfda63015482d65fef4f0b83f8dd1c61" ], "bucket_id": "<bucket-id>", "review_status": "pending", "transaction": { "amount": "100", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Office chair", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "1a9106f8f6783eaf5ba09478eeb3bcec6188caf02d136a7cdd9770bcd6e7830d", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "1a9106f8f6783eaf5ba09478eeb3bcec6188caf02d136a7cdd9770bcd6e7830d" }, "schema_version": "2", "status": "success" }
aeat app ledger update {transaction_id} --amount 121.00 --description \
Office chair, corrected priceaeat app ledger update {transaction_id} --amount 121.00 --description `
Office chair, corrected priceID 59316515102ea30c11d09b68fccd42fd4ed6b385f8fe1324412724fb1827dfe5 Date 2026-03-15 Amount 121 Description Office chair, corrected price Review status pending
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-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Office chair, corrected price", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "59316515102ea30c11d09b68fccd42fd4ed6b385f8fe1324412724fb1827dfe5", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "59316515102ea30c11d09b68fccd42fd4ed6b385f8fe1324412724fb1827dfe5" }, "schema_version": "2", "status": "success" }
Confirm the update replaced the amount and description.
Each flag fully replaces that field - write the complete new value, not an addition to the old one. Write the amount as a positive figure - the direction field carries whether money came in or went out, and a negative amount is refused. The updatable fields are: date, value-date, amount, direction, currency, counterparty, description, taxable-base, iva-rate, iva-amount, irpf-category, notes, and group.
An update gives the transaction a new ID - the update output prints it. You don’t have to track the change for read commands: an ID you wrote down before the update still answers in history, view, and track, resolving to the corrected transaction, exactly as the view above does. For a further mutation - another update, classify, or archive - use the current ID from the update output or from list, because those commands act on the live transaction.
Update works on active transactions only. Archived and stashed transactions refuse it, as does a split parent - the active parts of a split can be updated normally.
Remove a transaction¶
Remove deletes a transaction from your active records. Preview it first with --dry-run, then remove it for real. The removed id no longer resolves, which is how the sequence confirms the deletion:
aeat --format json app ledger add --date 2026-03-15 --amount 60.50 --direction OUTGOING \
--description Wrong import --idempotency-key correct-removeaeat --format json app ledger add --date 2026-03-15 --amount 60.50 --direction OUTGOING `
--description Wrong import --idempotency-key correct-remove{ "active_profile": "docs-sequence-sandbox", "command": "ledger.add", "notices": [], "result": { "bucket_event_ids": [ "8b33ed95365478e1093bdc08e4960210b7eb3fda5fe8f7de1144c1a9c47a8688" ], "bucket_id": "<bucket-id>", "review_status": "pending", "transaction": { "amount": "60.5", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Wrong import", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "1caacc8087e969e8648cc8618626fb93510b89a62186b1854f9a1893813869eb", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "1caacc8087e969e8648cc8618626fb93510b89a62186b1854f9a1893813869eb" }, "schema_version": "2", "status": "success" }
aeat app ledger remove {transaction_id} --reason wrong file imported --dry-runaeat app ledger remove {transaction_id} --reason wrong file imported --dry-runBucket <profile-id> ID 1caacc8087e969e8648cc8618626fb93510b89a62186b1854f9a1893813869eb Removed False DRY RUN MODE True
aeat app ledger remove {transaction_id} --reason wrong file imported --yesaeat app ledger remove {transaction_id} --reason wrong file imported --yesBucket <profile-id> ID 1caacc8087e969e8648cc8618626fb93510b89a62186b1854f9a1893813869eb Removed True DRY RUN MODE False
aeat --format json app ledger view {transaction_id}aeat --format json app ledger view {transaction_id}{ "active_profile": "docs-sequence-sandbox", "command": "ledger.view", "error": { "action": { "action": null, "argument_bindings": [], "conditionality": "not_applicable", "evidence": [ { "condition_id": "cli.ledger.transaction_id.resolves", "evidence_id": "cli.ledger.transaction_id.resolves.observation", "provenance": "runtime_observation", "values": { "transaction_id_resolves": false } } ], "failed_condition_id": "cli.ledger.transaction_id.resolves", "missing_argument_names": [], "no_recovery_outcome": "operator_decision" }, "category": "REFUSED", "code": "REFUSED_FINANCIAL_LEDGER_TRANSACTION_ID_PREFIX", "context": { "prefix": "'1caacc8087e969e8648cc8618626fb93510b89a62186b1854f9a1893813869eb'" }, "message": "no transaction matches id prefix '1caacc8087e969e8648cc8618626fb93510b89a62186b1854f9a1893813869eb'", "retryable": false, "runbook_id": null, "trace_id": null }, "notices": [], "schema_version": "2", "status": "error" }
Confirm the removed transaction no longer resolves.
A removed transaction is gone from your active records: the final view refuses because the id no longer names an active row.
Split one transaction into parts¶
When one payment covers two different things - for example, a card payment that mixes business and personal items - split it into parts. Amounts and descriptions pair up one per part: the first amount goes with the first description, and so on:
aeat --format json app ledger add --date 2026-03-15 --amount 121.00 --direction OUTGOING \
--description Mixed receipt --idempotency-key correct-splitaeat --format json app ledger add --date 2026-03-15 --amount 121.00 --direction OUTGOING `
--description Mixed receipt --idempotency-key correct-split{ "active_profile": "docs-sequence-sandbox", "command": "ledger.add", "notices": [], "result": { "bucket_event_ids": [ "9f5e7d2af0b80902321e3b1202a64f33db90510f94d37b6ac8a1faae0c89d25c" ], "bucket_id": "<bucket-id>", "review_status": "pending", "transaction": { "amount": "121", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Mixed receipt", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "e55d6600ead7d9490cb4774a0de8f8e9254866b0015b5c6898c68569e8d7a6e9", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "e55d6600ead7d9490cb4774a0de8f8e9254866b0015b5c6898c68569e8d7a6e9" }, "schema_version": "2", "status": "success" }
aeat --format json app ledger split {transaction_id} --child-amount 100.00 \
--child-description office supplies --child-amount 21.00 --child-description \
personal items --reason mixed receipt --yesaeat --format json app ledger split {transaction_id} --child-amount 100.00 `
--child-description office supplies --child-amount 21.00 --child-description `
personal items --reason mixed receipt --yes{ "active_profile": "docs-sequence-sandbox", "command": "ledger.split", "notices": [], "result": { "bucket_event_id": "2960d40c4f90dbaa25b1c462ce52210f4b2cc4ab1904c6c78c08739e821fbf1b", "bucket_id": "<bucket-id>", "child_transaction_ids": [ "9cc7a2639a30fcead52f62683cdc1c627a8b47cb43ff333f01b19e7f37245b49", "0bba86e710bdb3ccf97cd1cf82d62a5011a7107989ee76648f8ef8932c28ba0e" ], "child_transactions": [ { "display_id": "9cc7a263", "full_id": "9cc7a2639a30fcead52f62683cdc1c627a8b47cb43ff333f01b19e7f37245b49" }, { "display_id": "0bba86e7", "full_id": "0bba86e710bdb3ccf97cd1cf82d62a5011a7107989ee76648f8ef8932c28ba0e" } ], "classified_child_count": null, "llm": null, "parent_amount": null, "parent_transaction_id": "e55d6600ead7d9490cb4774a0de8f8e9254866b0015b5c6898c68569e8d7a6e9", "persisted": null, "proposed_children": null, "provenance": null, "provider": null, "reason": null, "split_group_id": "49e46bf12630b536b609999a5502582c94bc0faefcbba41cf784f89552823662" }, "schema_version": "2", "status": "success" }
aeat --format json app ledger view {first_part}aeat --format json app ledger view {first_part}{ "active_profile": "docs-sequence-sandbox", "command": "ledger.view", "notices": [], "result": { "bucket_id": "<bucket-id>", "review_status": "pending", "transaction": { "amount": "100", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "office supplies", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "9cc7a2639a30fcead52f62683cdc1c627a8b47cb43ff333f01b19e7f37245b49", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "9cc7a2639a30fcead52f62683cdc1c627a8b47cb43ff333f01b19e7f37245b49" }, "schema_version": "2", "status": "success" }
Confirm the split produced two active child parts.
The original transaction becomes the split parent, and the parts carry the balance from then on. The split output prints one child_transactions row per part, each with a short display_id and a full full_id. Copy those ids - the merge command needs them to undo the split.
Merge split parts back¶
To undo a split, merge the parts back together using the child ids the split printed. Name every sibling part - the command refuses a partial merge. The sequence splits a payment, then merges the parts into one fresh transaction:
aeat --format json app ledger merge --child-id {first_child} --child-id {second_child} \
--reason undo split --yesaeat --format json app ledger merge --child-id {first_child} --child-id {second_child} `
--reason undo split --yes{ "active_profile": "docs-sequence-sandbox", "command": "ledger.merge", "notices": [], "result": { "bucket_event_id": "24fa3aac51e5315bbb008dcbc57b80fdc41b292a4d2e504303e54e6c21354591", "bucket_id": "<bucket-id>", "merged_transaction_id": "afc12ac601d050659efe0abace313167f275923e77ba6b0a4ead868f32d7cdd9", "parent_transaction_id": "3cda9f1229ee04269a70bb6705dafeff1de2f9b14f1bfcefacba4ae277b20a9d", "source_child_ids": [ "63c3f9245937ab54232f067804fc12248abcbb1a5f9d93e9433313f8bf74b23d", "e0504b924f0b4500f0be1c70fe2958279ff9d9d0e5f61c4c66615fe9ed65ae84" ], "split_group_id": "ea58900b8c1962062494a7fb1c80418936a96ae53f2e402af33f8b212fb055c0" }, "schema_version": "2", "status": "success" }
aeat --format json app ledger view {merged}aeat --format json app ledger view {merged}{ "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-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Split to undo", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "afc12ac601d050659efe0abace313167f275923e77ba6b0a4ead868f32d7cdd9", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "afc12ac601d050659efe0abace313167f275923e77ba6b0a4ead868f32d7cdd9" }, "schema_version": "2", "status": "success" }
Confirm the merge produced a single active transaction carrying the full amount.
The parts and the original parent move to history, and the merge creates a fresh transaction in their place. If you no longer have the split output, the parts are active rows: run aeat app ledger list and read their ids from the listing.
Stash a transaction you are unsure about¶
Stash sets a transaction aside for later. A stashed transaction is kept out of ordinary work. The sequence stashes a row and confirms its new lifecycle state:
aeat --format json app ledger add --date 2026-03-15 --amount 80.00 --direction OUTGOING \
--description Awaiting invoice --idempotency-key correct-stashaeat --format json app ledger add --date 2026-03-15 --amount 80.00 --direction OUTGOING `
--description Awaiting invoice --idempotency-key correct-stash{ "active_profile": "docs-sequence-sandbox", "command": "ledger.add", "notices": [], "result": { "bucket_event_ids": [ "78ad404abaedb72ff65685f682bb8eeb58032ac35d5553aabd48810cc6cbea06" ], "bucket_id": "<bucket-id>", "review_status": "pending", "transaction": { "amount": "80", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Awaiting invoice", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "f5c6e7a8fce9960b36a29df9e59c7954affef5dc4bf50cd6a0aaaebc3eed8c22", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "f5c6e7a8fce9960b36a29df9e59c7954affef5dc4bf50cd6a0aaaebc3eed8c22" }, "schema_version": "2", "status": "success" }
aeat app ledger stash {transaction_id} --reason waiting for invoice --yesaeat app ledger stash {transaction_id} --reason waiting for invoice --yesID f5c6e7a8fce9960b36a29df9e59c7954affef5dc4bf50cd6a0aaaebc3eed8c22 Date 2026-03-15 Amount 80 Description Awaiting invoice Review status pending
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": "80", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Awaiting invoice", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "STASHED", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "f5c6e7a8fce9960b36a29df9e59c7954affef5dc4bf50cd6a0aaaebc3eed8c22", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "f5c6e7a8fce9960b36a29df9e59c7954affef5dc4bf50cd6a0aaaebc3eed8c22" }, "schema_version": "2", "status": "success" }
Confirm the transaction moved to the stashed state.
Use stash for a row you have not resolved yet and archive for a row you have deliberately set aside, such as a confirmed duplicate. Both are reversible: restore returns the row to active. The stash command prints the transaction’s fields but not its new lifecycle state, so view is how you confirm the change took effect.
Archive a transaction¶
Archive keeps a transaction in history but out of ordinary work - it’s the right choice for duplicates you want to keep a deliberate trace of. The sequence archives a duplicate row and confirms the state change:
aeat --format json app ledger add --date 2026-03-15 --amount 80.00 --direction OUTGOING \
--description Duplicate import --idempotency-key correct-archiveaeat --format json app ledger add --date 2026-03-15 --amount 80.00 --direction OUTGOING `
--description Duplicate import --idempotency-key correct-archive{ "active_profile": "docs-sequence-sandbox", "command": "ledger.add", "notices": [], "result": { "bucket_event_ids": [ "23efbc39a82a4a3977431bce885841629c5b7721e681921b8b70c0b9d90586f1" ], "bucket_id": "<bucket-id>", "review_status": "pending", "transaction": { "amount": "80", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Duplicate import", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "a095fd3caed8beeb0a33806f3b456822e31378e214e89067227d9f2fc6d0d6b0", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "a095fd3caed8beeb0a33806f3b456822e31378e214e89067227d9f2fc6d0d6b0" }, "schema_version": "2", "status": "success" }
aeat app ledger archive {transaction_id} --reason duplicate imported row --yesaeat app ledger archive {transaction_id} --reason duplicate imported row --yesID a095fd3caed8beeb0a33806f3b456822e31378e214e89067227d9f2fc6d0d6b0 Date 2026-03-15 Amount 80 Description Duplicate import Review status pending
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": "80", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Duplicate import", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ARCHIVED", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "a095fd3caed8beeb0a33806f3b456822e31378e214e89067227d9f2fc6d0d6b0", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "a095fd3caed8beeb0a33806f3b456822e31378e214e89067227d9f2fc6d0d6b0" }, "schema_version": "2", "status": "success" }
Confirm the duplicate moved to the archived state.
Like stash, the command prints the transaction’s fields but not its new lifecycle state, so view confirms the row now reads ARCHIVED.
Restore a stashed or archived transaction¶
If you stashed or archived a transaction by mistake, restore it to active. Restore is the inverse of stash and archive: the row returns to your everyday lists and totals. The sequence stashes a row and then restores it:
aeat app ledger restore {transaction_id} --reason stashed by mistake --yesaeat app ledger restore {transaction_id} --reason stashed by mistake --yesID d74c37a7d906824ef0171222e6c84b31dbcbcf5ed11806ce7864ff3db1267332 Date 2026-03-15 Amount 80 Description Stashed by mistake Review status pending
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": "80", "attachment_ids": [], "booked_date": "2026-03-15", "business_classification": "NOT_YET_PROCESSED", "business_pct": null, "category_id": null, "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-15", "description": "Stashed by mistake", "direction": "OUTGOING", "fx_rate": null, "invoice_id": null, "irpf_category": null, "iva_amount": null, "iva_category": null, "iva_rate": null, "lifecycle_state": "ACTIVE", "m210_income_classification": null, "modified_at": "2026-04-01T09:00:00+00:00", "notes": "", "prorrata_reference": null, "purchase_invoice_evidence_id": null, "source_jurisdiction": "ES", "taxable_base": null, "transaction_id": "d74c37a7d906824ef0171222e6c84b31dbcbcf5ed11806ce7864ff3db1267332", "usage_ratio_id": null, "value_date": null, "value_in_eur": null }, "transaction_id": "d74c37a7d906824ef0171222e6c84b31dbcbcf5ed11806ce7864ff3db1267332" }, "schema_version": "2", "status": "success" }
Confirm the restored transaction is active again.
Restore accepts the same id prefix the other commands accept. To recover several rows stashed by mistake, restore each one by id - you do not need to reset the whole ledger. List does not have a stashed-only filter, so identify the stashed rows from the ids you stashed, or from each row’s lifecycle state shown by view.
Restore refuses a row that is already active, and it refuses a row whose period you have already filed - restoring it would change the inputs behind a return you have presented. Restore one of these only after you have corrected the filing through an amendment.
Review what changed¶
Every correction is recorded. To see every action on a transaction in order, run ledger history. The sequence splits a payment, then reads the history with the sibling flag to follow the whole split family:
aeat app ledger history {transaction_id}aeat app ledger history {transaction_id}Bucket <profile-id> ID 1fa07beedceb057f55a3aab56aa756e9dab983c332573ee6bdb1e77bb0e16570 Event count 2 2026-04-01T09:00:00+00:00 ledger.transaction.split 0dd721341b0758df0aa4baaab3e24ad7dd79c3a2f90f5f2fc17856dfe491a21f 2026-04-01T09:00:00+00:00 ledger.transaction.created 808c7c37fdc6678905928147c14d45cb7e28450f8c87c917c27a3b61f6406388
aeat --format json app ledger history {transaction_id} --include-split-siblingsaeat --format json app ledger history {transaction_id} --include-split-siblings{ "active_profile": "docs-sequence-sandbox", "command": "ledger.history", "notices": [], "result": { "bucket_id": "<bucket-id>", "event_count": 2, "events": [ { "actor": "<profile-id>", "bucket_id": "<bucket-id>", "event_id": "0dd721341b0758df0aa4baaab3e24ad7dd79c3a2f90f5f2fc17856dfe491a21f", "event_type": "ledger.transaction.split", "object_id": "1fa07beedceb057f55a3aab56aa756e9dab983c332573ee6bdb1e77bb0e16570", "object_type": "ledger_transaction", "occurred_at": "2026-04-01T09:00:00Z", "payload": { "child_count": "2", "parent_transaction_id": "1fa07beedceb057f55a3aab56aa756e9dab983c332573ee6bdb1e77bb0e16570", "reason": "mixed receipt", "source_command": "aeat app ledger split", "split_group_id": "4d2d96d6579ac45716e2561a4bc5e7c73876e0a76843db90f5045ebc6dc7a026" }, "payload_version": 1 }, { "actor": "<profile-id>", "bucket_id": "<bucket-id>", "event_id": "808c7c37fdc6678905928147c14d45cb7e28450f8c87c917c27a3b61f6406388", "event_type": "ledger.transaction.created", "object_id": "1fa07beedceb057f55a3aab56aa756e9dab983c332573ee6bdb1e77bb0e16570", "object_type": "ledger_transaction", "occurred_at": "2026-04-01T09:00:00Z", "payload": { "amount": "121.00", "currency": "EUR", "direction": "OUTGOING", "source_command": "aeat app ledger add" }, "payload_version": 1 } ], "transaction_id": "1fa07beedceb057f55a3aab56aa756e9dab983c332573ee6bdb1e77bb0e16570" }, "schema_version": "2", "status": "success" }
Confirm the history records the transaction's events in order.
The history lists each action in order with its timestamp and event reference. Details such as the reason and the new values are in the JSON output. To see a value before a change, read the earlier events in the history. The CLI reference covers every field the history shows.
Evidence and corrections¶
An attached evidence record - a receipt or invoice - is not deleted when you correct a transaction. After a split or merge, check the new transactions and re-attach evidence where needed. The evidence guide covers attaching and checking evidence.
Start over as a last resort¶
If the ledger is beyond repair - for example, after importing the wrong files repeatedly - clear it and rebuild. Preview first, then confirm. The sequence records a row, previews the reset, clears the ledger, and confirms nothing remains:
aeat app ledger reset --reason re-importing all statements --dry-runaeat app ledger reset --reason re-importing all statements --dry-runBucket <profile-id> Rows 1 Reset False DRY RUN MODE True
aeat app ledger reset --reason re-importing all statements --yesaeat app ledger reset --reason re-importing all statements --yesBucket <profile-id> Rows 1 Reset True DRY RUN MODE False
aeat --format json app ledger listaeat --format json app ledger list{ "active_profile": "docs-sequence-sandbox", "command": "ledger.list", "notices": [], "result": { "bucket_id": "<bucket-id>", "limit": null, "offset": 0, "rows": [], "shown": 0, "total": 0, "truncated": false }, "schema_version": "2", "status": "success" }
Confirm the reset cleared the active ledger.
Reset clears the whole ledger for the active profile. Use the transactions guide to rebuild it from your statements.
Where to get help¶
If a command refuses or fails, check the troubleshooting guide. Unfamiliar terms are explained in the glossary. Before sharing command output with anyone, strip tax identifiers such as your NIF, CIF, DNI, NIE, or NII.
Next steps¶
Import and manage transactions - bring in new transactions.
Attach evidence to transactions - back your corrections with receipts.
Classify transactions - prepare corrected rows for tax calculations.
CLI reference - full field detail for every ledger command.