transaction.post
Post a balanced transaction; refused if unbalanced.
Records a transaction: a date, a description and two or more splits, each moving a value into or out of one
account. The splits' values must sum to exactly zero, or the transaction is refused with unbalanced and the book is
left as it was. Values are in the transaction's currency; a split in an account of another commodity also needs its
amount in that commodity, which is how a purchase of shares or foreign currency records its price.
Without currency, the transaction's currency is the commodity of the first split's account. That matters when the first
split is in a foreign-currency account: the values are then in that currency, and every split in an account of another
commodity needs an amount. To record the transaction in your own currency, pass currency, or put an account in that
currency first. If the first split's account holds no currency (shares, say), currency is required and the call is
refused as invalid without it.
Positive values are debits, negative values credits: paying 750.00 of rent from a bank account is -750.00 on the
bank account and 750.00 on the expense account.
Examples
Example: with a new, empty book open as book.gnucash.
JSON-RPC
Request:
{"jsonrpc":"2.0","id":1,"method":"account.create","params":{"name":"Checking","type":"BANK","commodity":"USD"}}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": "…"
}
Request:
{"jsonrpc":"2.0","id":2,"method":"account.create","params":{"name":"Rent","type":"EXPENSE","commodity":"USD"}}
Response:
{
"jsonrpc": "2.0",
"id": 2,
"result": "…"
}
Request:
{"jsonrpc":"2.0","id":3,"method":"transaction.post","params":{"datePosted":"2024-07-01","description":"Rent","splits":[{"account":"Checking","value":"-750.00"},{"account":"Rent","value":"750.00"}]}}
Response:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"guid": "…",
"currency": "CURRENCY::USD",
"description": "Rent",
"datePosted": "2024-07-01",
"balanced": true,
"splits": [
{
"account": "…",
"accountName": "Rent",
"value": {
"num": "75000",
"denom": "100"
},
"amount": {
"num": "75000",
"denom": "100"
},
"…": "…"
},
{
"accountName": "Checking",
"value": {
"num": "-75000",
"denom": "100"
},
"…": "…"
}
],
"…": "…"
}
}
Command line
red-ale exec --book book.gnucash --script - <<'EOF'
account.create {"name":"Checking","type":"BANK","commodity":"USD"}
account.create {"name":"Rent","type":"EXPENSE","commodity":"USD"}
transaction.post {"datePosted":"2024-07-01","description":"Rent","splits":[{"account":"Checking","value":"-750.00"},{"account":"Rent","value":"750.00"}]}
EOF
It prints:
[
"…",
"…",
{
"guid": "…",
"currency": "CURRENCY::USD",
"description": "Rent",
"datePosted": "2024-07-01",
"balanced": true,
"splits": [
{
"account": "…",
"accountName": "Rent",
"value": {
"num": "75000",
"denom": "100"
},
"amount": {
"num": "75000",
"denom": "100"
},
"…": "…"
},
{
"accountName": "Checking",
"value": {
"num": "-75000",
"denom": "100"
},
"…": "…"
}
],
"…": "…"
}
]
MCP tool call
Tool call:
{"name":"account.create","arguments":{"name":"Checking","type":"BANK","commodity":"USD"}}
Result:
{
"content": [
{
"type": "text",
"text": "\"…\""
}
]
}
Tool call:
{"name":"account.create","arguments":{"name":"Rent","type":"EXPENSE","commodity":"USD"}}
Result:
{
"content": [
{
"type": "text",
"text": "\"…\""
}
]
}
Tool call:
{"name":"transaction.post","arguments":{"datePosted":"2024-07-01","description":"Rent","splits":[{"account":"Checking","value":"-750.00"},{"account":"Rent","value":"750.00"}]}}
Result:
{
"structuredContent": {
"guid": "…",
"currency": "CURRENCY::USD",
"description": "Rent",
"datePosted": "2024-07-01",
"balanced": true,
"splits": [
{
"account": "…",
"accountName": "Rent",
"value": {
"num": "75000",
"denom": "100"
},
"amount": {
"num": "75000",
"denom": "100"
},
"…": "…"
},
{
"accountName": "Checking",
"value": {
"num": "-75000",
"denom": "100"
},
"…": "…"
}
],
"…": "…"
}
}
Splits that don't balance are refused, and nothing is recorded:
Example: with a new, empty book open as book.gnucash.
JSON-RPC
Request:
{"jsonrpc":"2.0","id":1,"method":"account.create","params":{"name":"Checking","type":"BANK","commodity":"USD"}}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": "…"
}
Request:
{"jsonrpc":"2.0","id":2,"method":"account.create","params":{"name":"Rent","type":"EXPENSE","commodity":"USD"}}
Response:
{
"jsonrpc": "2.0",
"id": 2,
"result": "…"
}
Request:
{"jsonrpc":"2.0","id":3,"method":"transaction.post","params":{"description":"Rent","splits":[{"account":"Checking","value":"-750.00"},{"account":"Rent","value":"705.00"}]}}
Response:
{
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": 2002,
"message": "…",
"data": {
"imbalance": "-45.00",
"currency": "USD",
"help": "error/unbalanced"
}
}
}
Command line
red-ale exec --book book.gnucash --script - <<'EOF'
account.create {"name":"Checking","type":"BANK","commodity":"USD"}
account.create {"name":"Rent","type":"EXPENSE","commodity":"USD"}
transaction.post {"description":"Rent","splits":[{"account":"Checking","value":"-750.00"},{"account":"Rent","value":"705.00"}]}
EOF
It fails with exit status 2, printing on standard error:
{
"error": {
"code": 2002,
"message": "…",
"data": {
"imbalance": "-45.00",
"currency": "USD",
"help": "error/unbalanced"
}
}
}
MCP tool call
Tool call:
{"name":"account.create","arguments":{"name":"Checking","type":"BANK","commodity":"USD"}}
Result:
{
"content": [
{
"type": "text",
"text": "\"…\""
}
]
}
Tool call:
{"name":"account.create","arguments":{"name":"Rent","type":"EXPENSE","commodity":"USD"}}
Result:
{
"content": [
{
"type": "text",
"text": "\"…\""
}
]
}
Tool call:
{"name":"transaction.post","arguments":{"description":"Rent","splits":[{"account":"Checking","value":"-750.00"},{"account":"Rent","value":"705.00"}]}}
Result:
{
"isError": true,
"structuredContent": {
"error": {
"code": 2002,
"name": "unbalanced",
"message": "…",
"data": {
"imbalance": "-45.00",
"currency": "USD",
"help": "error/unbalanced"
}
}
}
}
Effect: write, on the Book.
Params
Params of transaction.post.
currency(CommodityId or null): The transaction currency. Defaults to the first split's account commodity, when that is a currency.datePosted(Date or null): The posted date; today by default. Stored at its day-neutral time with adate-postedslot, as GnuCash does.dateEntered(Timestamp or null): When it was entered; now by default.num(string or null): Check or reference number.description(string or null): Description.notes(string or null): Notes; empty means none.docLink(string or null): A document link: a URL, afile://URI, or a path relative to the path head. Empty means none.splits(array of SplitParams, required): The splits. Their values must sum to zero.recordPrice(boolean): Record each priced split's value/amount in the price database, as GnuCash's register does (xaccTransRecordPricewith sourceuser:split-register).
Result
A transaction, as returned by every transaction method.
guid(Guid, required): The transaction's GUID.currency(CommodityId, required): The transaction currency, which values are in.num(string or null): Check or reference number.description(string or null): Description.notes(string or null): Notes.datePosted(Date or null): The posted date.datePostedTime(Timestamp, required): The posted instant as stored (the posted date's day-neutral time for transactions GnuCash or red-ale enters).dateEntered(Timestamp, required): When it was entered.void(boolean, required): Void, by GnuCash 5.17's rule: the void timestamp is set.voidReason(string or null): Why it was voided.voidTime(Timestamp or null): When it was voided.readOnly(string or null): Why it may not be changed, when read-only (voided, or generated by a business document).reversedBy(Guid or null): The transaction that reverses this one.closing(boolean, required): A book-closing transaction.balanced(boolean, required): The split values sum to zero; with trading accounts on, trading values and each commodity's amounts too.docLink(string or null): The document link (assoc_urislot), as stored: a URL, afile://URI, or a path relative to the path head.splits(array of SplitInfo, required): Its splits, in stored order (debits first).