Integrate with Xero: Complete Guide
Everything developers and finance teams need to connect software to Xero. OAuth 2.0 flow, tenant model, invoice types, rate limits, tracking categories, and Marketplace listing path explained.

A product manager at a UK accounting firm once described their Xero integration project like this: "We assumed it was just a REST API. It is, but it also has opinions about tenants, about what a bill is versus an invoice, about how you handle multi-currency, and about rate limits that will bite you the moment you go live with a real client." Two months later they had a working integration. This guide is what they wish they had read on day one.
Why Xero dominates in UK, AU, and NZ, and what that means for US buyers
Xero was founded in New Zealand in 2006. By 2026 it has roughly four million subscribers globally, with its heaviest penetration in the UK, Australia, and New Zealand. In those markets Xero competes directly with QuickBooks Online and often wins among accountants and bookkeepers who prefer its UI and its cloud-native architecture. In the US, QuickBooks still holds the larger market share, but Xero has made consistent inroads particularly among tech-forward companies, startups with international operations, and businesses whose accountants trained on Xero in other markets and brought it with them.
For developers and finance teams deciding whether to build a Xero integration, the regional distribution matters. If your customer base skews toward UK, AU, or NZ, Xero integration is not optional. If you sell to US-only small businesses, QuickBooks Online is a higher priority. For anything with international reach, you need both. The two APIs are conceptually similar, both expose accounting entities over REST, but the data models differ enough that you cannot write a single adapter layer and call it done.
For US buyers evaluating Xero, the practical difference is that Xero's accountant community in the UK and Australia is large and trained. If you bring Xero-connected bookkeeping software to a UK accounting firm, the accountant on the other side already knows Xero. You do not need to sell the platform.
OAuth 2.0 flow and the tenant model
Xero uses standard OAuth 2.0 authorization code flow. The entry point is developer.xero.com, where you register an application and receive a client ID and client secret. The flow from there:
- Redirect the user to
https://login.xero.com/identity/connect/authorizewith your client ID, requested scopes, a redirect URI, and a state parameter for CSRF protection. - After the user grants consent, Xero redirects to your callback with an authorization code.
- Exchange the code at
https://identity.xero.com/connect/tokenfor an access token (1-hour TTL) and a refresh token (valid for 60 days, single-use). - Use the access token to call
GET https://api.xero.com/connectionsto retrieve the list of tenant connections for this token. - Store the tenantId from each connection alongside the token. Pass xero-tenant-id as a header on every subsequent API call.
The tenant model is the detail most integrations get wrong first. Xero calls each organisation a "tenant." A single user may have access to multiple organisations, and the OAuth flow grants access to all of them that the user selects during consent. Your callback receives one token set, but that token covers N organisations. Every API request must specify which organisation it is addressing via the xero-tenant-id header. If you only store the first tenantId and the user has two companies, you will quietly write data to the wrong one.
Scopes
The Xero OAuth scopes documentation covers all available scopes. For a typical accounts payable or invoice sync integration, the scopes you need are:
accounting.transactions-- read/write access to invoices, bills, credit notes, overpayments, prepayments, and bank transactions.accounting.contacts-- read/write access to contacts. Required because every invoice references a Contact record.accounting.settings-- read access to chart of accounts, tax rates, currencies, and tracking categories. Needed if your integration lets users map categories.accounting.attachments-- read/write access to file attachments on accounting objects.offline_access-- required for refresh tokens. Without this, your access token expires in one hour and users must re-authenticate.openid profile email-- standard OIDC scopes for identifying the user in your system.
Request only the scopes you need. Xero's consent screen shows the user exactly what access they are granting, and unnecessary scopes create friction during sign-up.
Invoice types: ACCREC, ACCPAY, and BankTransactions
The Accounting API organises financial documents into a handful of entity types. Understanding which type maps to which accounting concept saves time during schema design.
Invoices (ACCREC and ACCPAY)
The /api.xro/2.0/Invoices endpoint handles both sales invoices and purchase bills. The Type field controls which you are working with:
ACCREC(Accounts Receivable): A sales invoice you issue to a customer. The contact is the customer. Payment received increases AR and credits revenue.ACCPAY(Accounts Payable): A bill you received from a supplier. The contact is the vendor. Payment made decreases AP and debits an expense account.
When your integration captures vendor invoices from email, those are always ACCPAY records in Xero. When you push them to the API, set Type: "ACCPAY" in the request body. The rest of the Invoice object structure is identical between the two types: Contact, LineItems, Date, DueDate, Status, CurrencyCode, Reference.
Invoice statuses follow a lifecycle: DRAFT is the default for newly created records. SUBMITTED is available for invoices requiring approval. AUTHORISED is the approved, payable state. PAID is set by Xero automatically when a payment record is attached and fully covers the balance. VOIDED removes the invoice from payables. For most integrations, you create invoices in DRAFT or AUTHORISED depending on whether your customer's workflow includes an approval step.
Bills (ACCPAY in practice)
Xero users often refer to ACCPAY invoices as "bills" in the UI, and this terminology appears in Xero's own help documentation. In the API they are the same Invoice object with Type: "ACCPAY". The Bills endpoint does not exist as a separate path.
BankTransactions
/api.xro/2.0/BankTransactions covers money that has moved through a bank account already, either received (RECEIVE) or spent (SPEND). Bank transactions differ from invoices in that they do not represent an obligation, they represent a completed movement. They are reconciled against bank statement lines. In an accounts payable workflow you typically create ACCPAY invoices when you receive the bill and then attach a Payment record when you actually pay it, rather than creating a BankTransaction directly.
Contacts
Every invoice, bill, and bank transaction references a Contact. Contacts serve as both customers (for ACCREC) and vendors/suppliers (for ACCPAY). The same Contact can appear in both roles. When pushing invoices from an external system, you need to either look up an existing Contact by name or email, or create one. A common pattern is to search for an existing contact by name first (GET /Contacts?where=Name=="Acme Corp"), create if not found, and cache the ContactID locally to avoid repeated lookups.
Common integration patterns
Most integrations with Xero fall into one of four patterns. The one you choose shapes your data flow, your error handling, and how much you rely on Xero as a system of record.
Push-only: external system to Xero
The simplest pattern. Your software captures or processes documents, extracts structured data, and writes records to Xero. The external system is the source of truth. Xero receives the final output. Invoice capture tools, expense management platforms, and receipt scanners use this model. You create Contacts and ACCPAY invoices (and optionally attachments) and leave reconciliation to the accountant inside Xero.
Bidirectional sync
Both systems write data and must stay in agreement. The classic challenge here is conflict resolution: if a record is edited in both Xero and your system between sync runs, which version wins. Xero's ifModifiedSince parameter on GET requests (available on most Accounting API endpoints) gives you a cursor-based incremental sync. Combined with the UpdatedDateUTC field on each record, you can detect changes since your last successful sync. Most integrations choose a primary system for each entity type and make the other a read-only mirror for that type, rather than truly bidirectional sync which is significantly harder to get right.
Event-driven via webhooks
Xero supports webhooks for a subset of accounting events, delivered as HTTP POST payloads signed with an HMAC-SHA256 signature you verify using your webhook key from the developer portal. Supported events include invoice created/updated and contact created/updated. Webhooks are useful when you need near-real-time notification that a Xero user has authorised or paid an invoice. Note that webhooks are a Certified App feature, meaning you need to complete the Xero certification process before your app can receive them in production. For uncertified apps, polling with ifModifiedSince is the alternative.
Read-only reporting
Some integrations only read from Xero for reporting, dashboards, or analytics. These need accounting.transactions.read and accounting.settings.read scopes rather than the full read/write versions. Read-only scopes reduce the surface area shown to users during consent. For an integration that syncs invoices into Inbox Ledger for reconciliation, see how our integrations feature handles the connection setup and token refresh.
Tracking categories and chart of accounts
Tracking categories are Xero's answer to cost centers, departments, or classes in other accounting systems. Each Xero organisation can define up to two tracking categories, each with its own set of named options.
A typical setup: one category called Department with options Finance, Engineering, Sales, Marketing. A second category called Project with options per active project. When posting a bill to Xero, you attach tracking options at the line item level. A single line item can carry up to two tracking elements, one per category. This means a single invoice line can be allocated to Department=Engineering and Project=Platform Upgrade simultaneously.
To read the available categories for a tenant call GET /TrackingCategories. The response includes each category's TrackingCategoryID and its array of options with individual TrackingOptionID values. When writing line items, reference the IDs directly rather than the names. Names can change. IDs are stable.
The chart of accounts follows the same pattern. Account codes in Xero (like 200 for Sales, 300 for Purchases, 400 for Overhead) are user-configurable. Never hardcode account codes in your integration. Call GET /Accounts at onboarding, present the list to the user for mapping, and store their selections. The mapping is per-tenant, because every organisation uses a different chart.
Currency handling in Xero uses the CurrencyCode field on invoice headers (ISO 4217, three-letter uppercase, e.g. GBP, USD, EUR). Multi-currency functionality requires that the Xero organisation has the relevant currency enabled. Your integration should gracefully handle the case where a currency is not enabled, ideally by surfacing the error from Xero's API rather than silently swallowing it.
Rate limits, per-minute quotas, and app tiers
Xero enforces rate limits at two levels. Per-minute limits apply per-tenant: the default is 60 API calls per minute for most endpoints. When you exceed this, you receive a 429 Too Many Requests response with a Retry-After header indicating how long to wait. Respect this header rather than implementing a fixed backoff interval.
Daily limits apply per-application across all tenants. Uncertified apps are capped at 5,000 API calls per day total. For an app with ten active tenant connections each doing incremental sync, 5,000 calls distributes to 500 calls per tenant per day, or roughly 20 per hour. That sounds generous until you have a customer with 10,000 invoices in their historical backlog and you are paginating through them in a one-time import.
Strategies for staying within limits:
- Use the
ifModifiedSinceheader on all polling requests. This returns only records modified after your cursor date rather than the full dataset on every call. - Batch create and update operations wherever possible. The
POST /Invoicesendpoint accepts an array of up to 50 invoices in a single request. - Cache reference data (accounts, contacts, tracking categories) locally with a refresh interval of several hours rather than looking them up on every write.
- Use webhook events (if certified) to trigger sync only when changes actually occur, rather than polling on a fixed interval.
Certified apps, those that have completed Xero's app review and listed on the Xero App Store, receive higher daily call volumes and access to webhook events. Xero does not publish the exact certified-tier limits publicly. The practical guidance from the developer community is that certified apps can handle production-scale workloads without hitting daily caps under normal operating conditions.
For payables automation integrations like those connecting email-captured invoices from platforms such as Stripe or Amazon Business, the typical steady-state write volume is well within uncertified limits. Historical imports of existing data are where you need to implement careful pacing.
Xero Marketplace listing path
The Xero App Store lists integrations that Xero has reviewed and certified. Listing gives your product distribution to Xero's four million subscribers, visibility in Xero's in-product app browser (called the App Store tab inside the product), and access to higher API rate limits and webhook events.
The certification process involves several stages:
- Register your app at developer.xero.com and complete basic details (description, logo, scopes, redirect URIs).
- Build and test against the sandbox environment Xero provides.
- Complete the Xero certification checklist, which covers OAuth implementation, error handling, scope minimisation, and user experience requirements.
- Submit for review via the Xero Developer portal. The Xero partnership team reviews the submission, tests the OAuth flow, checks UI/UX requirements, and evaluates whether the integration handles edge cases like token refresh and tenant switching correctly.
- App Store listing goes live after approval. The Xero Marketplace guidelines cover content requirements, category selection, and update obligations.
Timeline varies, but allow four to eight weeks from submission to listing. Xero's review is more thorough than most OAuth app review processes, particularly around the OAuth flow itself and multi-tenant handling.
Certification is not required to operate a working Xero integration. Most integrations start uncertified, run in production with real customers, and pursue certification when distribution through the App Store becomes strategically valuable.
Start for free and extract your first 10 invoices without a credit card.
Pitfalls that trip up real integrations
Five issues that surface repeatedly in production Xero integrations, each worth knowing before you start rather than discovering after.
Tenant switching bugs
The single most common production incident in multi-tenant Xero integrations: the user connected two organisations during OAuth, your code stores only the first tenantId, and all writes go to the wrong company. The fix is to fetch all connections from GET https://api.xero.com/connections immediately after every token exchange and refresh, not just the first time, and present the user with a tenant selector if more than one connection exists. Store all tenantIds associated with a token, not just index zero.
A related issue: when a user disconnects your app from one organisation and reconnects, the tenantId for that organisation may change. Do not cache tenantIds permanently. Re-fetch connections on every token refresh cycle and reconcile against your stored list.
Attachment size limits
Xero limits individual file attachments to 25 MB. This is generous for most invoice PDFs, but scanned documents, multi-page statements, and files exported from certain accounting platforms can exceed it. If your integration uploads attachments automatically, add a size check before attempting the upload and surface the error in a way that does not block the rest of the invoice from being created. A failed attachment should not prevent the invoice record from being written.
Currency and exchange rate handling
Xero stores invoice amounts in the transaction currency and applies exchange rates to calculate base currency equivalents. When you read a multi-currency invoice, the CurrencyRate field on the invoice reflects the rate Xero used. If your integration needs to report or compare amounts in a single currency, do not use Xero's stored exchange rates for accounting purposes without verifying them. The rates Xero applies are mid-market rates sourced from a data provider and may differ from your bank's actual transaction rate. For reconciliation between bank transactions and vendor invoices across currencies, the amount comparison needs to account for exchange rate differences, not just check for exact matches.
For integrations that already handle multi-currency invoice extraction, the accounting automation software guide covers how to structure currency normalisation across multiple accounting backends.
AUTHORISED versus DRAFT state on creation
Creating an invoice in DRAFT status means it will not appear in Xero's payables aging or AP reports until someone in Xero authorises it manually. If your customers expect invoices to flow through immediately, create them in AUTHORISED status. If your customers want an approval step inside Xero before invoices affect their AP balance, use DRAFT. This choice should be a configuration option in your integration, not a hardcoded decision, because different customers have different workflows.
The ifModifiedSince timezone trap
The ifModifiedSince query parameter on Xero's GET endpoints accepts a UTC datetime. If your application stores the last sync timestamp in local time and passes it to Xero without converting to UTC, you will either re-fetch records you already have (wasted calls) or miss records that were modified in the gap between your local timestamp and UTC. Always store and compare sync cursors in UTC, and pass UTC values to Xero's API.
Putting it together: the practical integration checklist
If you are building a Xero integration for the first time, here is the short version of what to get right before you go live:
Request minimum viable scopes. Do not ask for read/write when read-only suffices.
Fetch all tenant connections after every token exchange and store all tenantIds. Build a tenant selector for multi-org users.
Use ifModifiedSince on every polling request. Cache accounts, contacts, and tracking categories locally.
Batch writes wherever the endpoint supports it. Check attachment sizes before uploading.
Handle 429 responses with Retry-After respect. Do not implement a fixed sleep.
Create invoices as AUTHORISED or DRAFT based on customer configuration, not a hardcoded default.
Store sync cursors in UTC.
Plan your Marketplace listing path early if distribution through Xero's app browser matters to your growth.
For integrations that start with invoice capture from email, the path to Xero is: extract structured data from the PDF, match or create a Contact, post an ACCPAY invoice in AUTHORISED state, attach the source PDF, and optionally tag with tracking categories based on your customer's mapping. That flow handles the vast majority of accounts payable use cases.
If you are evaluating how Inbox Ledger connects captured invoices to Xero alongside other accounting destinations, see our integrations feature page. For a broader view of how this fits into a full AP automation stack, the accounting automation software guide covers how the pieces connect. For teams deciding between Xero and its competitors, our QuickBooks Online integration guide and best Xero add-ons overview cover what matters in that decision.
The Xero API is well-documented, actively maintained, and designed for the kind of integrations that automate accounts payable work. The tenant model and the ACCREC/ACCPAY distinction are the two places where most implementations stumble. Get those right in the design phase and the rest of the integration is straightforward.