Home › Blog › Xero Journal API Limitations: How to Filter Voided, Reversal, and Manual Journal EntriesXero Journal API Limitations: How to Filter Voided, Reversal, and Manual Journal Entries Chintan Prajapati April 22, 2025 12 min read IntroductionWhen working with accounting software like Xero, transparency and accuracy in financial reporting are paramount.However, Xero’s Journal API presents certain limitations when dealing with updates to transactions, especially when multiple journal entries are created for the same source transaction.These limitations, while ensuring accurate records, can present challenges for API developers looking to integrate or work with Xero’s general ledger data.We’ve previously explored similar challenges with Xero’s invoice synchronization.In this article, we’ll explore the limitations developers face with Xero’s Journal API, offer a deep dive into the underlying reasons for the multiple journal entries, and provide practical solutions to make it easier to manage this data.Why Xero Journal API Returns Duplicate and Reversal Journal EntriesWhile consulting a SaaS client who is aiming to develop a Real-Time AI reporting, analysis & forecasting solution that works with multiple Accounting and ERP software.The client’s objective is to get the entire general ledger, chart of accounts, and generate the AI-driven insights from Xero, and drill down to the transaction history.The problem occurred when Xero’s journal API returned duplicate transactions, which included voided, deleted, and reversed the original journal.Duplicate !! Really? How is it even possible?It doesn’t make any sense that the Xero journal report will send duplicate entries.Let’s understand if they are duplicates or something else.Ideally Xero journal API should be returning only records that are actively present in Xero.Not the deleted one or the modified transaction’s reversal journal entries.(Source: Developer Xero Forum)Why Xero Journal API Shows Voided, Deleted, and Reversal JournalsXero’s approach to journal entries ensures that every transaction is auditable and traceable.However, when a transaction, such as an invoice or payment, is updated, Xero doesn’t simply modify the original journal entry.Instead, it reverses the original journal and creates a new journal reflecting the updated transaction details.Kelly, a 7-year-old user from Xero Central, summed it up quite simply when she explained the workflow:“In Xero, journals are created for every transaction. If a transaction is then edited, deleted, or recoded, a journal is created to reverse the original transaction with a new journal created showing the revisions.”(Source: Xero Forum)For example, if you enter a sales invoice and then edit it, you will see the following entries in the Journal report: The original journal from when the invoice was first entered. A reversal of the previous journal. A new journal for the updated transaction.Kelly’s explanation highlights the key idea, Xero ensures that each change to a transaction creates a complete history, preserving transparency and auditability.Key Takeaway: Xero’s system is designed for complete auditability, not just current state reportingKey Xero Journal API Limitations Developers Should KnowIt is a very common use case where building a tool for Accountants and Financial Advisors to analyze the underlying data within Xero and displaying to them only records that are currently present within the system.The Xero Journal API endpoint officially doesn’t have any way to filter out the voided, deleted, or modified transactions journal entries.While Xero’s approach ensures the integrity of financial records, it creates several challenges for Xero developers working with the Xero API.Why Multiple Journals Appear for the Same SourceIDAs seen in the screenshot below, multiple journal entries are created for a single source transaction (e.g., an invoice that got modified by amending the delivery charge from $10 to $20).Although this redundancy is necessary for tracking transaction audit and history management, it can become problematic for database storage, efficient querying, and Data analysis.Check the above screenshot of an invoice, which got modified.Invoice modification in Xero created a reversal journal entry for the original invoice and created a new journal entry with updated amounts.For better viewing, I copied the JSON output from Xero’s API explorer to the JSON viewer and highlighted the source ID and journal numbers.The screenshot shows multiple journal entries with the same SourceID, illustrating the redundancy in the data created by the reversal and updated transactions i.e. invoice.The Key Problem: Xero Journal API Does Not Directly Filter Inactive Journals How would you identify which journals to keep and which ones to delete from the database?The Possible Solution Is Xero Developers need to store all these journal entries (including reversals) in their databases.But It Will Create Another Problem The database can quickly become cluttered with unnecessary data, leading to performance issues, especially when the number of journal entries increases over time. Even though the net effect of the journals is correct (the total debits and credits balance out), the large volume of journal entries for a single source transaction can complicate financial reconciliation and reporting.Xero Developers Must Track and store every journal entry (original, reversal, and updated). Reconciling multiple journal entries for the same transaction is cumbersome and error-prone without the right tools. Optimize storage and performance to handle this added complexity.Key Takeaway: Without proper filtering, Xero’s journal data creates database inefficiency and analytical complexity.How to Filter Voided, Deleted, and Reversal Journals in Xero APINow that we understand the challenge, let’s look at practical solutions for developers who need to work with the Xero Journal API and filter out unwanted journal entries efficiently.To filter out reversal Xero journal API entries for the same source transaction, one effective solution is to use an intermediary table. Here’s how you can do it:Step 1: Store Xero Journal Entries in an Intermediary TableCreate an intermediary table where all journal entries (original, reversal, and updated) are stored temporarily.Each journal entry contains two important identifiers: SourceType SourceIDThis way, all related journals can be grouped together by their SourceID.Step 2: Group Journal Entries by SourceID and SourceTypeOnce journals are in the intermediary table, you can easily group them by SourceID to track all changes made to a single transaction.Step 3: Move the Latest Valid Xero Journal Entry to the Final TableAfter identifying the most recent Xero journal (reflecting the updated transaction), move it to the final table, leaving the reversal and original journals in the intermediary table for auditing purposes.The above approach will not solve the entire problem, because what if the most recent journal is deleted or voided?Best Workaround: Cross-Reference Xero Journals with Transaction TablesYou need to pull the following data from the individual API endpoints and store them in a database table.Then cross-reference data from these key transactional tables: BankTransactions: https://api.xero.com/api.xro/2.0/BankTransactions BankTransfers: https://api.xero.com/api.xro/2.0/BankTransfers CreditNotes: https://api.xero.com/api.xro/2.0/CreditNotes ExpenseClaims: https://api.xero.com/api.xro/2.0/ExpenseClaims Invoices (Accounts Receivable & Payable): https://api.xero.com/api.xro/2.0/Invoices ManualJournals: https://api.xero.com/api.xro/2.0/ManualJournals Payments: https://api.xero.com/api.xro/2.0/Payments Journals: https://api.xero.com/api.xro/2.0/JournalsWhy Xero Manual Journals to Accounts Payable May Not Be AllowedWhen working with Xero journal data, developers often assume that every accounting movement can be handled through manual journals.However, Xero treats Accounts Payable and Accounts Receivable differently from standard general ledger accounts.Accounts Payable and Accounts Receivable balances are usually controlled through source transactions such as bills, invoices, credit notes, payments, and contact-level balances.Because of this, Xero may not allow direct manual journal posting to certain system-controlled AP or AR accounts in the same way it allows posting to normal expense, income, asset, liability, or equity accounts.This is important when analyzing Xero Journal API data because a journal entry related to Accounts Payable may not always originate from the ManualJournals endpoint.In many cases, it will come from a bill, supplier payment, credit note, or another payable-related transaction.For example, if the SourceType is ACCPAY, the related source transaction should be checked against the Invoices endpoint because Xero treats supplier bills as Accounts Payable invoices.If the SourceType is ACCPAYPAYMENT, the related transaction should be checked against the Payments endpoint.This is why relying only on the Journals endpoint can create confusion when trying to identify active, voided, deleted, or reversed payable-related journal entries. For example:Xero Journal SourceTypeRelated Transaction TableWhat It Usually RepresentsACCPAYInvoicesSupplier bill / Accounts Payable invoiceACCPAYPAYMENTPaymentsPayment made against a supplier billACCPAYCREDITCreditNotesSupplier credit noteMANUALJOURNALManualJournalsManually created journal entrySo, if a user is trying to create or analyze a manual journal that impacts Accounts Payable, they should first confirm whether the transaction should actually be created as a bill, credit note, payment, or manual journal.From an integration perspective, this distinction is critical because each source type has a different endpoint, status field, and validation logic.For developers, the right approach is not to treat every AP-related journal as a manual journal. Instead, use SourceType and SourceID to identify the original transaction table and validate the current status of that transaction.The safest way to handle this in a Xero API integration is: Read journal records from the Journals endpoint. Group records by SourceID and SourceType. Identify the correct transaction endpoint based on SourceType. Validate whether the source transaction is active, voided, deleted, paid, or updated. Keep only the journal entries that represent the current valid state for reporting.This avoids incorrect reporting where AP-related reversal journals, deleted bills, or updated supplier transactions are treated as active manual journals.Key Takeaway: Xero manual journals and Accounts Payable transactions should not be treated as the same thing. If a journal has an AP-related SourceType, developers should validate it against the original payable transaction instead of assuming it came from the ManualJournals endpoint.Mapping Xero Journal SourceType to Transaction TablesBefore filtering Xero journal entries, it is important to understand that SourceType defines where the journal came from.A payable-related journal may come from a bill, supplier payment, or credit note, while a true manual journal will use the MANUALJOURNAL source type.This distinction helps avoid treating all journal entries as manual journals and improves the accuracy of GL reporting.We will map SourceType to their respective transactional tables as follows:SourceTypeTarget TableTransaction TypeACCPAYInvoicesAccounts PayableACCPAYCREDITCreditNotesAP Credit NoteACCPAYPAYMENTPaymentsAP PaymentACCRECInvoicesAccounts ReceivableACCRECCREDITCreditNotesAR Credit NoteACCRECPAYMENTPaymentsAR PaymentCASHPAIDBankTransactionsCash Paid (Bank Txn)EXPCLAIMExpenseClaimsExpense ClaimEXPPAYMENTPaymentsExpense PaymentBANKTRANSFERBankTransfersBank TransfersMANUALJOURNALManualJournalsManual Journal EntriesValidation Method to Identify Active vs Reversed Journal EntriesUsing SourceID, we will SQL join journal records with SourceType tables to validate the status and ensure: Voided/Deleted transactions are excluded. Only the most recent journal (based on JournalNumber) is retained.For example, here’s what a deleted invoice looks like in JSON (Xero JSON Integration): { "Type": "ACCREC", "Contact": { "ContactID": "eaa28f49-6028-4b6e-bb12-d8f6278073fc" }, "DueDate": "/Date(1518685950940+0000)/", "LineItems": [ { "Description": "Services as agreed", "Quantity": "4", "UnitAmount": "100.00", "AccountCode": "200" } ], "Status": "DELETED" } An example of a SQL Joining query with InvoicesFilter Journal Entry Records by SourceID, SourceType, and StatusSourceType = ‘ACCREC’ (Accounts Receivable Invoice) SourceID = ‘9b731dec-77cf-4c83-998e-c11861e5cf4b’Match Xero Journal Entries with the Invoices TableInvoiceID = ‘9b731dec-77cf-4c83-998e-c11861e5cf4b’To handle the increase in journal entries, you can optimize your database design: Index the intermediary table to speed up queries. Partition the table to handle large volumes of data effectively. Consider archiving older journal entries in a separate database or table to keep the working dataset manageable.Conclusion: Build Accurate Xero Journal Reports Without Duplicate or Reversal EntriesBy following Xero API best practices, the Xero Journal API offers robust features that ensure transparency and auditability in financial records.However, the way it handles transaction updates by creating deleted or deleted journal entries can complicate things for API developers.By grouping journal entries, using intermediary tables, joining with the transaction table, and upgrading database storage.Developers can efficiently manage this complexity without sacrificing the benefits of Xero’s transparent approach.For more Xero API integration strategies, see our guides on XERO invoice synchronization and trial balance data extraction.With the Multiple Journal Entries solutions provided in this article, Xero developers can overcome the challenges of working with multiple journal entries and ensure their integrations with Xero remain smooth, efficient, and scalable.Final Thoughts on Xero Journal API LimitationsIf you’re working with Xero API and facing challenges managing journal entries, the key is to group, store, and optimize.By implementing the solutions outlined above, you can handle transaction updates effectively while maintaining data integrity and performance.FAQCan Xero Journal API filter voided or deleted journal entries?No, the Xero Journal API does not provide a direct filter to exclude voided, deleted, or reversal journal entries. The practical approach is to use SourceID and SourceType to trace each journal back to its original transaction, then validate the transaction status from the related endpoint such as Invoices, Payments, Credit Notes, Bank Transactions, or Manual Journals.Why does Xero Journal API show duplicate journal entries?Xero may show multiple journal entries for the same transaction because it keeps an audit trail when a transaction is updated, deleted, voided, or reversed. These entries may look like duplicates in API data, but they usually represent different accounting events connected to the same SourceID.How do I identify the latest journal entry in Xero API?To identify the latest journal entry, group the journal records by SourceID and SourceType, then compare fields such as journal number, journal date, or created date, depending on your data model. For better accuracy, also validate the current status of the original source transaction from the related Xero endpoint.What is SourceID in Xero Journals API?SourceID is the reference that connects a journal entry to the original Xero transaction. For example, a journal may be linked to an invoice, payment, credit note, bank transaction, bank transfer, or manual journal. Using SourceID helps developers group related journal entries and identify which source transaction created them.What is SourceType in Xero Journals API?SourceType tells you which type of transaction created the journal entry. For example, ACCPAY usually relates to supplier bills, ACCREC relates to sales invoices, ACCPAYPAYMENT relates to supplier payments, and MANUALJOURNAL relates to manual journals. This field is useful when deciding which Xero endpoint should be checked for transaction validation.Can Xero manual journals post directly to Accounts Payable?In many cases, Accounts Payable is handled through bills, supplier credit notes, payments, and contact-level balances rather than direct manual journal posting. For API integrations, developers should check whether the journal came from ACCPAY, ACCPAYPAYMENT, ACCPAYCREDIT, or MANUALJOURNAL before deciding how to process it.What is the difference between ACCPAY and MANUALJOURNAL in the Xero Journals API?ACCPAY usually represents a supplier bill or Accounts Payable invoice, while MANUALJOURNAL represents a manually created journal entry. Both can appear in journal data, but they should be validated against different endpoints. ACCPAY should usually be checked against the Invoices endpoint, while MANUALJOURNAL should be checked against the Manual Journals endpoint.How can developers filter reversal journals in the Xero API?Developers can filter reversal journals by grouping journal entries using SourceID and SourceType, then identifying the latest valid transaction state. The safest method is to cross-check the source transaction status from the relevant endpoint and exclude journals connected with voided, deleted, or reversed transactions from the final reporting table.