Guide

Edit, void and reverse transactions

Correct a transaction in place, void it while keeping a record, undo it with a reversing transaction, or delete it; attach a document link to a receipt.

Mistakes happen, and a bookkeeper has four tools for them, from lightest to heaviest. Which to use depends on whether you want the history to show the mistake.

You want toUse
Fix a typo or a wrong date or account, as if it had never been wrongtransaction.update
Keep the transaction in the book but count it as nothingtransaction.void
Cancel its effect with an equal and opposite transaction on a later datetransaction.reverse
Remove it altogethertransaction.delete

Steps

  1. Find the transaction and its GUID with transaction.list or transaction.find (see Find transactions).
  2. Edit it with transaction.update: description, notes, date, num, currency and splits. The result must still balance, or the change is refused.
  3. Void it with transaction.void, giving a reason. The splits' values are zeroed and the old values kept in slots, so the book remembers what was voided and why. transaction.unvoid restores it. A voided transaction is read-only until then.
  4. Reverse it with transaction.reverse: it posts a new transaction on the date you give, with every value negated. Use it when the original belongs to a closed period and must stay as it was. A transaction can be reversed once; a second call is refused as a conflict.
  5. Delete it with transaction.delete when it should never have existed. Deleting cannot be undone, short of restoring a backup.
  6. Link a document. To keep a receipt with its transaction, set the book's path head once with docLink.setPathHead (the folder your documents live in), then put a docLink on the transaction with transaction.update. A relative link is resolved against the path head; docLink.transactions lists the links and the full paths they resolve to.

A transaction older than the book's auto read-only threshold, may be refused; the error says why.

Example

Example: with a copy of the reports/test-data example book open as book.gnucash, at 2024-07-01T12:00:00Z.

JSON-RPC

Request:

{"jsonrpc":"2.0","id":1,"method":"transaction.update","params":{"guid":"c0000000000000000000000000000005","description":"Fuel","notes":"corrected"}}

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "description": "Fuel",
    "notes": "corrected",
    "balanced": true,
    "…": "…"
  }
}

Request:

{"jsonrpc":"2.0","id":2,"method":"transaction.void","params":{"guid":"c0000000000000000000000000000000","reason":"Duplicate"}}

Response:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "void": true,
    "voidReason": "Duplicate",
    "…": "…"
  }
}

Request:

{"jsonrpc":"2.0","id":3,"method":"transaction.unvoid","params":{"guid":"c0000000000000000000000000000000"}}

Response:

{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "void": false,
    "…": "…"
  }
}

Request:

{"jsonrpc":"2.0","id":4,"method":"transaction.reverse","params":{"guid":"c0000000000000000000000000000003","date":"2024-07-01"}}

Response:

{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "datePosted": "2024-07-01",
    "splits": [
      {
        "accountName": "Root:Asset:Bank",
        "value": {
          "num": "1500",
          "denom": "100"
        },
        "…": "…"
      },
      {
        "accountName": "Root:Expenses",
        "value": {
          "num": "-1500",
          "denom": "100"
        },
        "…": "…"
      }
    ],
    "…": "…"
  }
}

Request:

{"jsonrpc":"2.0","id":5,"method":"docLink.setPathHead","params":{"pathHead":"/home/me/receipts"}}

Response:

{
  "jsonrpc": "2.0",
  "id": 5,
  "result": "…"
}

Request:

{"jsonrpc":"2.0","id":6,"method":"transaction.update","params":{"guid":"c0000000000000000000000000000005","docLink":"2024/fuel.pdf"}}

Response:

{
  "jsonrpc": "2.0",
  "id": 6,
  "result": "…"
}

Request:

{"jsonrpc":"2.0","id":7,"method":"docLink.transactions","params":{}}

Response:

{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "links": [
      {
        "transaction": "c0000000000000000000000000000005",
        "docLink": "2024/fuel.pdf",
        "relative": true,
        "…": "…"
      }
    ],
    "…": "…"
  }
}

Request:

{"jsonrpc":"2.0","id":8,"method":"transaction.delete","params":{"guid":"c0000000000000000000000000000006"}}

Response:

{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {}
}

Command line

red-ale exec --book book.gnucash --script - <<'EOF'
transaction.update {"guid":"c0000000000000000000000000000005","description":"Fuel","notes":"corrected"}
transaction.void {"guid":"c0000000000000000000000000000000","reason":"Duplicate"}
transaction.unvoid {"guid":"c0000000000000000000000000000000"}
transaction.reverse {"guid":"c0000000000000000000000000000003","date":"2024-07-01"}
docLink.setPathHead {"pathHead":"/home/me/receipts"}
transaction.update {"guid":"c0000000000000000000000000000005","docLink":"2024/fuel.pdf"}
docLink.transactions {}
transaction.delete {"guid":"c0000000000000000000000000000006"}
EOF

It prints:

[
  {
    "description": "Fuel",
    "notes": "corrected",
    "balanced": true,
    "…": "…"
  },
  {
    "void": true,
    "voidReason": "Duplicate",
    "…": "…"
  },
  {
    "void": false,
    "…": "…"
  },
  {
    "datePosted": "2024-07-01",
    "splits": [
      {
        "accountName": "Root:Asset:Bank",
        "value": {
          "num": "1500",
          "denom": "100"
        },
        "…": "…"
      },
      {
        "accountName": "Root:Expenses",
        "value": {
          "num": "-1500",
          "denom": "100"
        },
        "…": "…"
      }
    ],
    "…": "…"
  },
  "…",
  "…",
  {
    "links": [
      {
        "transaction": "c0000000000000000000000000000005",
        "docLink": "2024/fuel.pdf",
        "relative": true,
        "…": "…"
      }
    ],
    "…": "…"
  },
  {}
]

MCP tool call

Tool call:

{"name":"transaction.update","arguments":{"guid":"c0000000000000000000000000000005","description":"Fuel","notes":"corrected"}}

Result:

{
  "structuredContent": {
    "description": "Fuel",
    "notes": "corrected",
    "balanced": true,
    "…": "…"
  }
}

Tool call:

{"name":"transaction.void","arguments":{"guid":"c0000000000000000000000000000000","reason":"Duplicate"}}

Result:

{
  "structuredContent": {
    "void": true,
    "voidReason": "Duplicate",
    "…": "…"
  }
}

Tool call:

{"name":"transaction.unvoid","arguments":{"guid":"c0000000000000000000000000000000"}}

Result:

{
  "structuredContent": {
    "void": false,
    "…": "…"
  }
}

Tool call:

{"name":"transaction.reverse","arguments":{"guid":"c0000000000000000000000000000003","date":"2024-07-01"}}

Result:

{
  "structuredContent": {
    "datePosted": "2024-07-01",
    "splits": [
      {
        "accountName": "Root:Asset:Bank",
        "value": {
          "num": "1500",
          "denom": "100"
        },
        "…": "…"
      },
      {
        "accountName": "Root:Expenses",
        "value": {
          "num": "-1500",
          "denom": "100"
        },
        "…": "…"
      }
    ],
    "…": "…"
  }
}

Tool call:

{"name":"docLink.setPathHead","arguments":{"pathHead":"/home/me/receipts"}}

Result:

{
  "content": [
    {
      "type": "text",
      "text": "\"…\""
    }
  ]
}

Tool call:

{"name":"transaction.update","arguments":{"guid":"c0000000000000000000000000000005","docLink":"2024/fuel.pdf"}}

Result:

{
  "content": [
    {
      "type": "text",
      "text": "\"…\""
    }
  ]
}

Tool call:

{"name":"docLink.transactions","arguments":{}}

Result:

{
  "structuredContent": {
    "links": [
      {
        "transaction": "c0000000000000000000000000000005",
        "docLink": "2024/fuel.pdf",
        "relative": true,
        "…": "…"
      }
    ],
    "…": "…"
  }
}

Tool call:

{"name":"transaction.delete","arguments":{"guid":"c0000000000000000000000000000006"}}

Result:

{
  "structuredContent": {}
}