Xero ↔ ERPNext — Finance Integration
Connecting Xero (cloud accounting) with ERPNext for the finance function — whether you keep Xero as the books and sync, or move accounting into ERPNext entirely.
01 · First decision — integrate or migrate?
Unlike SysPro/Sage X3 (where the goal is to replace the ERP), Xero often stays. Choose the model deliberately:
| Model | When it fits | Finance system of record |
|---|---|---|
| Integrate (sync) | Client & their accountant are happy on Xero; they want ERPNext for ops (sales, stock, manufacturing) and Xero to stay the books. | Xero owns the GL, bank rec, VAT, statutory reporting; ERPNext owns operations. |
| Migrate (replace) | They want one system; ERPNext Accounts becomes the GL and Xero is retired. | ERPNext; Xero archived read-only. See the migration alternative. |
02 · Integration architecture
The Xero API (what you're building against)
- RESTful JSON Accounting API — full CRUD on Invoices, Bills, Credit Notes, Payments, Bank Transactions, Manual Journals, Contacts, Items, Accounts, Tax Rates, Tracking Categories. Batch up to 50 records/request.
- OAuth 2.0 (authorization-code flow). Access tokens last 30 min; request
offline_accessfor a refresh token (60-day); refresh againsthttps://identity.xero.com/connect/token. Store + rotate tokens carefully (the integration dies if the 60-day refresh lapses). - Webhooks — event notifications (invoice/contact created/updated) for near-real-time pull, instead of polling.
- Rate limits — ~60 calls/min, a daily cap per org, and concurrency limits. Design for backoff + batching from day one.
Build options
| Option | Notes |
|---|---|
| Custom Frappe app (recommended for ownership) | A Frappe app in ERPNext that calls the Xero API: OAuth token store, scheduled sync jobs + webhook receiver, mapping + idempotency. Keeps everything in the ERPNext stack — best for handover as venture IP. |
| iPaaS middleware (n8n, Pipedream, elastic.io) | Fastest to stand up; good for simple flows. Adds a third system to run/monitor and a recurring cost; less control over mapping edge cases. There is no native ERPNext↔Xero connector — it's always API + glue. |
Sync direction & cadence
- Define each flow's direction (push ERPNext→Xero, pull Xero→ERPNext) and trigger (on submit, scheduled batch, or webhook).
- Idempotency — store the Xero ID against each ERPNext doc (and vice-versa) so re-runs upsert, never duplicate. A sync log/audit per record is non-negotiable.
- Error handling — ret/backoff on 429s, dead-letter failed records for review, alert on token-refresh failure.
03 · Object mapping (Xero ↔ ERPNext)
| Xero | ERPNext | Typical direction / notes |
|---|---|---|
| Chart of Accounts (Accounts) | Account (tree) | Xero→ERPNext on setup; Xero usually owns the GL structure if integrating |
| Contacts | Customer / Supplier | A Xero Contact can be both → may map to a Customer and a Supplier; sync both ways with one owner |
| Invoices (ACCREC) | Sales Invoice | ERPNext→Xero (ERPNext raises ops invoices; Xero books AR) |
| Bills (ACCPAY) | Purchase Invoice | Either direction — decide who captures supplier bills |
| Credit Notes | Sales/Purchase Credit Note | Mirror the invoice direction |
| Payments | Payment Entry | Usually Xero owns (bank feed + rec); pull settlement status back to ERPNext to close invoices — avoid double entry |
| Bank Transactions | Bank Transaction / Journal Entry | Xero owns the bank feed & reconciliation |
| Manual Journals | Journal Entry | Whoever owns the GL |
| Items | Item | ERPNext owns (it's the ops master); push to Xero if Xero needs them on invoices |
| Tax Rates | Tax templates (+ csf_za VAT) | Map Xero tax rates ↔ ERPNext tax templates; VAT201 sign-off |
| Tracking Categories | Cost Center / Accounting Dimensions | Map Xero tracking ↔ ERPNext dimensions |
04 · Build & operate
- Register a Xero app (OAuth2), capture client_id/secret as ERPNext secrets, implement the auth-code flow + token store with 60-day refresh.
- Sync foundation data first: Accounts (CoA), Tax Rates, Tracking Categories, Contacts — reconcile before transactions.
- Wire the transaction flows per the mapping, each with direction, trigger, idempotency key (Xero ID ↔ ERPNext name) and a sync log.
- Add the webhook receiver for near-real-time pulls; keep a scheduled reconciliation sweep as the safety net.
- Build a reconciliation report: ERPNext vs Xero AR/AP totals, invoice counts, and unmatched records — run daily.
- Pilot on one entity / a date-boxed window; widen once the daily reconciliation is clean.
05 · Risks & gotchas
- Dual system-of-record — the #1 failure: both systems editing the same object → conflicting truth. Enforce one owner per object.
- Payment double-counting — if both push payments, invoices settle twice. Pick one payment owner (usually Xero via bank feed) and pull status back.
- Token-refresh lapse — the 60-day refresh token expiring silently kills the sync. Monitor + alert.
- Rate limits — 429s under load; batch (50/req), backoff, and spread scheduled jobs.
- Tax mapping — Xero tax rates vs ERPNext tax templates must align for VAT201 to be correct (accountant sign-off).
- Contact duality — a Contact that's both customer and supplier needs careful mapping or AR/AP get crossed.
- No native connector — it's always custom API work or middleware; budget accordingly.
Migration alternative — retiring Xero
If the client wants one system, treat it as a finance migration like the others: set a cutover date, load Chart of Accounts → Contacts (Customers/Suppliers) → open AR/AP → opening balances (opening Journal Entry) → current-year detail, reconcile the trial balance, AR/AP aging and bank balances to the cent, run in parallel, then set Xero read-only. The method is identical to the SysPro / Sage X3 playbooks — Xero's clean API just makes extraction easier.
Sources
Xero Developer: Accounting API overview · Invoices · OAuth2 auth-code flow · API limits
Integration guides: Knit — Xero API guide · Satva — Xero API guide · Apideck — Xero integrations
ERPNext ↔ Xero: Pipedream · n8n · Frappe forum — Xero integration
Shared finance-migration method: see the SysPro → ERPNext playbook.