WooCommerce Xero Integration: How to Sync Orders Cleanly

Your WooCommerce store did 380 orders last month. Now the numbers have to land in Xero, and the first of the month is when that job comes due. Someone exports a CSV of orders, opens Xero, and starts creating invoices and payments by hand, one screen at a time. Two hours in, a payout from your gateway hits the bank feed for $6,214.08 and ties back to nothing, because that figure is 40-odd orders bundled together, minus processing fees, minus a refund from a customer who changed their mind.
Connecting WooCommerce and Xero is the easy part. Both have APIs, and a dozen apps will move data between them. The hard part is making the two sets of numbers agree, so your revenue, your fees, and your bank balance all reconcile instead of just filling up. Most walkthroughs stop at "install the plugin and click connect", which is exactly why so many stores end up with a sync that runs nightly and a bookkeeper who still spends month-end untangling it. The difference between those two outcomes comes down to a handful of build decisions, and one in particular that every clean integration hinges on.
What "synced" actually means here
Before touching a connector, get specific about what has to move. A WooCommerce order is not one thing to Xero. It is several, and each one lands in a different place in the books:
- The sale. Revenue, split by the accounts you track (product income, shipping income, maybe by category).
- The tax. Sales tax or VAT collected, which Xero needs posted to a liability account, not lumped into revenue.
- The payment. What the customer actually paid, through Stripe, PayPal, or whatever gateway WooCommerce is running.
- The fees. The cut the gateway kept before the money reached your bank. This never appears on the WooCommerce order at all.
- Refunds and partial refunds. These reverse revenue and tax, and sometimes the fee, depending on the gateway.
A sync that copies orders but ignores fees and refunds gives you books that look done and are quietly wrong. Getting all five to reconcile against a single bank deposit is the whole job. Everything below is in service of that.
Decide between a summary sync and a per-order sync
This is the decision that determines whether your integration scales or drowns. There are two fundamentally different ways to get WooCommerce data into Xero, and picking wrong is the most common mistake we see.
Per-order sync creates one Xero invoice (or sales receipt) for every WooCommerce order. A store doing 380 orders a month generates 380 Xero transactions a month. This is fine at low volume and gives you line-item detail in Xero. It also means your Xero organisation fills with thousands of tiny invoices a year, your reconciliation screen becomes unusable, and you burn through API calls fast.
Summary sync batches orders into periodic journal entries, typically one settlement or one day at a time, grouped by account. Instead of 380 invoices, you get a handful of summary entries whose totals match your gateway payouts. This is the model tools like A2X are built around, and it is what most accountants actually want, because a summary entry ties one-to-one to a bank deposit.
The rule of thumb: if you do fewer than roughly 100 orders a month and genuinely need each order visible in Xero, per-order can work. Above that, or any time reconciliation is the real goal, sync summaries. A store owner does not need 4,000 individual invoices in Xero. They need the deposit to match the books.
Map what moves before you connect anything
The stores that get burned are the ones that install a connector, click through the defaults, and discover three months later that shipping income went to the wrong account and tax was never split out. Spend an hour on a mapping table first. For each thing that moves, write down where it lands:
| WooCommerce item | Xero destination | |---|---| | Product revenue | Sales / product income account | | Shipping charged | Shipping income account | | Tax collected | Tax liability account (via Xero's tax rates) | | Gateway payment | A clearing account, not the bank directly | | Gateway fees | Merchant fees expense account | | Refunds | Reverse revenue and tax |
The clearing account is the piece most tutorials skip and the piece that makes reconciliation work. Money flows WooCommerce sale into the clearing account, then the gateway payout moves from the clearing account into the bank. When those two sides net to zero, you know every sale is accounted for and every fee is booked. Skip it and you are back to matching deposits by hand.
Set up the WooCommerce side: webhooks over polling
Once you know what moves and how it maps, wire up the trigger. WooCommerce can push events to your integration in real time through webhooks, which beats polling the store on a schedule and missing orders in the gap.
WooCommerce webhooks are managed under WooCommerce > Settings > Advanced > Webhooks, and you can also create them through the REST API at /wp-json/wc/v3/webhooks, per WooCommerce's webhook documentation. The order topics you care about are order.created, order.updated, order.deleted, and order.restored. In practice you subscribe to order.updated and act when an order reaches a paid or completed status, because that is the moment the money is real.
Two details a durable build gets right:
- Verify the signature. WooCommerce signs each delivery with an HMAC-SHA256 hash in the
X-WC-Webhook-Signatureheader. Check it on your endpoint so a random POST cannot inject a fake order into your books. - Handle retries idempotently. Webhooks can fire more than once for the same event. Key every order by its WooCommerce order ID so a duplicate delivery updates the existing record instead of creating a second Xero invoice.
Get idempotency wrong and you get double-counted revenue, which is worse than no sync at all, because now the books look full and are overstated.
Match orders to Xero without creating duplicates or hitting limits
On the Xero side, the same idempotency discipline applies, plus a hard constraint most people learn the expensive way. Xero's API is rate limited. Per Xero's developer documentation, each app gets 60 calls per minute and 5,000 calls per day against a single organisation, with a wider ceiling of 10,000 calls per minute across all connected organisations.
Those limits are why a naive per-order sync struggles at volume. If a nightly job tries to push 800 backed-up orders as 800 separate invoices, each needing a contact lookup and a create call, you will trip the daily cap and the run will fail halfway, leaving half your month in Xero and half not. A summary sync sidesteps this by design, because a day of orders becomes one or two API calls instead of hundreds.
When you do write to Xero, a few practices keep it clean:
- Use a reference the sync can look up. Stamp each Xero invoice or journal with the WooCommerce order or batch ID so the next run can find it and skip it rather than duplicate it.
- Match contacts deliberately. Decide up front whether every customer becomes a Xero contact or whether ecommerce sales roll up to a single "WooCommerce Customer". For most stores the second is cleaner. You do not want 4,000 one-time contacts cluttering Xero.
- Post tax through Xero's tax rates, not as a manual line, so Xero's own tax reports stay correct.
Reconcile payouts, fees, and refunds: the part tools get wrong
Off-the-shelf connectors are good at the sale and weak at everything after it. Fees and refunds are where a sync earns its keep or quietly corrupts the books.
Fees never come through on the WooCommerce order. Stripe or PayPal deducts them before the payout lands, so the deposit is always smaller than the orders it represents. Your integration has to book the fee as an expense and net it against the clearing account, or the clearing account never zeroes out and reconciliation stalls. This is the single most common reason a "working" WooCommerce Xero sync still will not reconcile.
Refunds have to reverse the right pieces. A full refund reverses revenue and tax. A partial refund reverses part of each. Depending on the gateway, the fee may or may not come back. A sync that treats a refund as just a negative sale gets the tax liability wrong, which is the kind of error that surfaces at filing time when it is most expensive to fix. If you want the reconciliation mechanics in more depth, our guide on how to reconcile payments covers the clearing-account pattern across gateways.
The test for any WooCommerce Xero setup is simple. Take one real gateway payout, find it in the bank feed, and see whether Xero already has a matching entry whose total equals it to the cent. If yes, the sync works. If you are still doing mental math, it does not, no matter what the connector's dashboard says.
When a WooCommerce Xero connector is enough, and when to build
For a store doing modest volume with a standard setup, one gateway, simple tax, no unusual accounts, an off-the-shelf connector is the right first move, and recommending otherwise would be dishonest. A2X starts at $29 a month for its Mini plan covering up to 200 orders a month per A2X's pricing page, the official WooCommerce Xero extension is a paid marketplace add-on, and Zapier can move individual orders across if your needs are light. Any of these will get basic sales into Xero without a developer.
They hit a wall in predictable places. Multiple gateways settling on different schedules, which turns this into reconciling across more than two systems at once. Multi-currency, where the payout currency differs from the order. Marketplace or subscription revenue that needs to split across accounts. Custom tax logic that the connector's dropdowns cannot express. Once your setup has two or three of those, you are spending more time working around the tool than the tool saves, and the monthly fee buys you a sync you still have to babysit.
That is where working with bottta changes the math. We are an automation studio, and this is squarely what we build: integrations that glue WooCommerce, your gateway, and Xero together with the reconciliation logic baked in, not bolted on. We map every account with you, wire the webhooks with signature verification and idempotency, batch to summaries so you never fight Xero's rate limits, and handle fees and refunds so the clearing account actually zeroes out. A defined WooCommerce-to-Xero build like this fits our $4K fixed-scope project, integrations included, with 30 days of post-launch support. If the store keeps changing, new gateways, new markets, new revenue lines, the $3K/month retainer keeps the sync monitored and adjusted so it does not silently break at 5,000 orders the way it ran fine at 500.
This is the same integration discipline we bring to any two tools that refuse to talk. It looks a lot like our writeups on the QuickBooks Stripe integration, the Stripe Xero integration, and the QuickBooks Shopify integration: different tools, same job of making the money tie out. It also connects to the broader accounting automation and invoice automation work that clears the manual re-keying out of an ops team's month-end entirely.
Common mistakes to avoid
- No clearing account. Posting gateway payments straight to the bank account leaves you matching every deposit by hand. The clearing account is the whole reconciliation mechanism.
- Per-order sync at high volume. Thousands of tiny invoices clog Xero and burn API calls. Summarise unless you have a real reason not to.
- Ignoring fees. If the sync does not book gateway fees, the clearing account never zeroes and the books never reconcile.
- No idempotency. Without a stable key per order, webhook retries create duplicate invoices and overstate revenue.
- Treating refunds as negative sales. Refunds have to reverse tax correctly or your tax reporting drifts wrong.
Frequently asked questions
Does WooCommerce have a native Xero integration?
No. WooCommerce and Xero are separate products from separate companies, so a connector always sits between them. WooCommerce publishes an official Xero extension, and third parties like A2X offer their own, but none is a built-in bridge. You are always choosing which connector, or whether to build one.
Should I sync every order or a daily summary to Xero?
Summarise unless you specifically need each order visible in Xero. A summary entry ties one-to-one to a gateway payout, which is what makes reconciliation fast, and it keeps you well under Xero's API limits. Per-order sync only makes sense at low volume when line-item detail in Xero genuinely matters.
Why don't my WooCommerce sales match my Xero bank balance?
Almost always because gateway fees are not being booked. The deposit is the order total minus the processing fee, so unless the fee posts as an expense against a clearing account, the two sides never net to zero. Refunds handled as plain negative sales cause the same drift on the tax side.
How much does a WooCommerce Xero integration cost?
Off-the-shelf connectors start around $29 a month, for example A2X's Mini plan for up to 200 orders. A custom build that handles multiple gateways, multi-currency, or non-standard tax is a bottta $4K fixed-scope project, with an optional $3K/month retainer for ongoing monitoring once the store keeps evolving.
Can I sync historical WooCommerce orders into Xero?
Yes, but mind the rate limits. Xero allows 5,000 API calls per day per organisation, so a large backfill has to be paced across multiple days or batched into summaries rather than pushed as thousands of individual invoices in one run.
A sync that runs is not the same as books that reconcile. The connector moves data. The reconciliation logic, the clearing account, the fee handling, the idempotent matching, is what makes the numbers tie out, and that is a build rather than a plugin. When you are ready to stop reconciling WooCommerce by hand every month, that clean version is what bottta builds against your actual store and stack.