Skip to main content

Transactions

Manage double-entry ledger transactions.

Transactions are the atomic units of accounting in SpeyBooks. Every financial event ultimately results in one or more transactions.

Authentication

All endpoints require a valid API key. See Authentication.

Overview

Every transaction in SpeyBooks follows double-entry accounting:

  • Each transaction contains two or more lines
  • Debits must equal credits
  • The sum of all line amounts must equal zero

Amount convention

  • Positive amounts = debits
  • Negative amounts = credits

This convention is enforced consistently across the API.

The transaction object

{
"id": "txn_52",
"date": "2026-01-31",
"description": "Sales invoice INV-2026-0001",
"reference": "INV-2026-0001",
"sourceType": "invoice",
"sourceId": "inv_42",
"status": "posted",
"isReconciled": false,
"reconciledAt": null,
"createdBy": "usr_1",
"createdByName": "William Murray",
"createdAt": "2026-01-31T10:00:00Z",
"updatedAt": "2026-01-31T10:00:00Z",
"totalDebit": 120000,
"totalCredit": 120000,
"accountCodes": "1100, 4000, 2200",
"lines": [
{
"id": "line_99",
"accountId": "acc_1100",
"accountCode": "1100",
"accountName": "Accounts Receivable",
"contactId": "cont_17",
"amount": 120000,
"vatAmount": null,
"description": "Sales invoice INV-2026-0001"
},
{
"id": "line_100",
"accountId": "acc_4000",
"accountCode": "4000",
"accountName": "Sales",
"contactId": null,
"amount": -100000,
"vatAmount": null,
"description": "Consulting services"
},
{
"id": "line_101",
"accountId": "acc_2200",
"accountCode": "2200",
"accountName": "VAT Output",
"contactId": null,
"amount": -20000,
"vatAmount": null,
"description": "VAT @ 20%"
}
]
}

Transaction attributes

FieldTypeDescription
idstringUnique identifier (txn_*)
datestringTransaction date (YYYY-MM-DD)
descriptionstringDescription (max 500 chars)
referencestringExternal reference
sourceTypestringmanual, invoice, payment, import
sourceIdstringSource document ID (if applicable)
statusstringdraft or posted
isReconciledbooleanWhether reconciled to bank
reconciledAtstringReconciliation timestamp
createdBystringUser ID
createdByNamestringUser name
totalDebitintegerTotal debits (minor units)
totalCreditintegerTotal credits (minor units)
accountCodesstringComma-separated account codes
linesarrayTransaction lines

Transaction line attributes

FieldTypeDescription
idstringLine identifier (line_*)
accountIdstringAccount ID (acc_*)
accountCodestringAccount code
accountNamestringAccount name
contactIdstringContact reference (optional)
amountintegerMinor units (positive = debit)
vatAmountintegerVAT portion (optional)
descriptionstringLine description

Create a transaction

Create a manual journal entry.

POST /v1/transactions
curl -X POST https://api.speybooks.com/v1/transactions \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"date": "2026-01-31",
"description": "Office supplies purchase",
"reference": "AMZN-12345",
"status": "posted",
"lines": [
{ "accountId": "acc_5100", "amount": 4167, "description": "Office supplies" },
{ "accountId": "acc_2200", "amount": 833, "description": "VAT @ 20%" },
{ "accountId": "acc_1200", "amount": -5000, "description": "Payment from bank" }
]
}'

Create parameters

FieldTypeRequiredDescription
datestringYesTransaction date (YYYY-MM-DD)
descriptionstringYesDescription (max 500 chars)
referencestringNoExternal reference (max 100 chars)
statusstringNodraft or posted (default: posted)
linesarrayYesAt least 2 lines required

Line parameters

FieldTypeRequiredDescription
accountIdstringYesAccount ID (acc_*)
amountintegerYesMinor units (positive = debit, negative = credit)
descriptionstringNoLine description
contactIdstringNoContact reference
vatRateintegerNoVAT percentage (0-100)
vatTreatmentstringNonone, inclusive, or exclusive

Response

{
"success": true,
"data": {
"id": "txn_53",
"date": "2026-01-31",
"description": "Office supplies purchase",
"reference": "AMZN-12345",
"status": "posted",
"totalDebit": 5000,
"totalCredit": 5000,
"lines": [
{ "id": "line_102", "accountId": "acc_5100", "accountCode": "5100", "accountName": "Office Supplies", "amount": 4167 },
{ "id": "line_103", "accountId": "acc_2200", "accountCode": "2200", "accountName": "VAT Output", "amount": 833 },
{ "id": "line_104", "accountId": "acc_1200", "accountCode": "1200", "accountName": "Bank Account", "amount": -5000 }
]
}
}

Error: Unbalanced transaction

{
"success": false,
"error": {
"code": "validation_error",
"message": "Transaction must balance (debits must equal credits)",
"field": "lines"
}
}

Retrieve a transaction

GET /v1/transactions/:id
curl https://api.speybooks.com/v1/transactions/txn_52 \
-H "Authorization: Bearer sk_live_your_api_key"

Returns the full transaction object including all lines.


List transactions

GET /v1/transactions
curl "https://api.speybooks.com/v1/transactions?from=2026-01-01&to=2026-01-31&status=posted" \
-H "Authorization: Bearer sk_live_your_api_key"

Query parameters

ParameterTypeDefaultDescription
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)
accountIdstringFilter by account (acc_*)
contactIdstringFilter by contact (cont_*)
statusstringdraft or posted
reconciledbooleanFilter by reconciliation status
searchstringSearch description or reference
pageinteger1Page number
limitinteger50Results per page (max 100)

Response

{
"success": true,
"data": [
{
"id": "txn_52",
"date": "2026-01-31",
"description": "Sales invoice INV-2026-0001",
"reference": "INV-2026-0001",
"status": "posted",
"isReconciled": false,
"totalDebit": 120000,
"totalCredit": 120000,
"accountCodes": "1100, 4000, 2200"
}
],
"meta": {
"page": 1,
"limit": 50,
"total": 127,
"hasMore": true
}
}

Update a transaction

Only draft transactions can be updated. Updating replaces all lines.

PUT /v1/transactions/:id
curl -X PUT https://api.speybooks.com/v1/transactions/txn_53 \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"description": "Updated description",
"lines": [
{ "accountId": "acc_5100", "amount": 5000, "description": "Office equipment" },
{ "accountId": "acc_2200", "amount": 1000, "description": "VAT @ 20%" },
{ "accountId": "acc_1200", "amount": -6000, "description": "Payment from bank" }
]
}'

Response

{
"success": true,
"data": {
"id": "txn_53",
"updatedAt": "2026-01-31T15:00:00Z"
}
}

Error: Cannot update posted transaction

{
"success": false,
"error": {
"code": "unprocessable_entity",
"message": "Cannot update posted transaction"
}
}

Change transaction status

Post a draft transaction or revert to draft (if not reconciled).

PATCH /v1/transactions/:id/status
curl -X PATCH https://api.speybooks.com/v1/transactions/txn_53/status \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"status": "posted"
}'

Parameters

FieldTypeRequiredDescription
statusstringYesdraft or posted

Response

{
"success": true,
"data": {
"id": "txn_53",
"status": "posted"
}
}
warning

Reverting is not allowed if the transaction has been reconciled.


Reconcile a transaction

Mark a posted transaction as reconciled to the bank statement.

PATCH /v1/transactions/:id/reconcile
curl -X PATCH https://api.speybooks.com/v1/transactions/txn_52/reconcile \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"reconciled": true
}'

Parameters

FieldTypeRequiredDescription
reconciledbooleanYesReconciliation status

Response

{
"success": true,
"data": {
"id": "txn_52",
"isReconciled": true,
"reconciledAt": "2026-02-01T10:00:00Z"
}
}

Only posted transactions may be reconciled.


Delete a transaction

DELETE /v1/transactions/:id
curl -X DELETE https://api.speybooks.com/v1/transactions/txn_53 \
-H "Authorization: Bearer sk_live_your_api_key"

Deletion rules

  • Only draft manual transactions can be deleted
  • Posted transactions cannot be deleted
  • System-generated transactions cannot be deleted

To remove system transactions, delete the source document instead.

Response

{
"success": true,
"data": {
"deleted": true
}
}

Double-entry examples

Sale with VAT

{
"description": "Sales invoice to customer",
"lines": [
{ "accountId": "acc_1100", "amount": 1200, "description": "Accounts Receivable" },
{ "accountId": "acc_4000", "amount": -1000, "description": "Sales Revenue" },
{ "accountId": "acc_2200", "amount": -200, "description": "VAT Output @ 20%" }
]
}

Payment received

{
"description": "Payment from customer",
"lines": [
{ "accountId": "acc_1200", "amount": 1200, "description": "Bank" },
{ "accountId": "acc_1100", "amount": -1200, "description": "Accounts Receivable" }
]
}

Expense with VAT

{
"description": "Software subscription",
"lines": [
{ "accountId": "acc_5200", "amount": 5000, "description": "Software Costs" },
{ "accountId": "acc_2200", "amount": 1000, "description": "VAT Input @ 20%" },
{ "accountId": "acc_1200", "amount": -6000, "description": "Bank" }
]
}

Common errors

CodeHTTPDescription
validation_error400Transaction does not balance (debits ≠ credits)
validation_error400Fewer than 2 lines provided
invalid_id400Malformed ID (expected txn_*, acc_*, or cont_*)
not_found404Transaction does not exist
unprocessable_entity422Cannot update/delete posted transaction
unprocessable_entity422Cannot delete system-generated transaction
unprocessable_entity422Cannot revert reconciled transaction to draft

Key invariants

  • Debits always equal credits
  • Transactions are immutable once posted
  • Reconciled transactions cannot be reverted
  • System transactions derive from source documents
  • Ledger history is append-only
Amount format

All amounts are in minor units. Positive = debit, negative = credit. Sum of all line amounts must equal zero.