Skip to main content

Xero

With Xero, Vodia Billing computes the tax and Xero records the figures supplied to it.

This is not a preference. Xero's automated sales tax cannot run on invoices created through its API, so an API-created invoice carries whatever tax figures the caller provides. Vodia Billing therefore has to be the tax authority for a Xero tenant, and the tenant needs a tax profile applied here.

Vodia Billing 18

Setup

1. Register the app

In the Xero developer portal, create an app of type Web app.

Redirect URI:

https://your-domain/api/xero/callback

Copy the client ID and client secret.

2. Enter the credentials

Accounting → App, choose Xero, paste the client ID and secret. Both are stored encrypted under ENCRYPTION_KEY.

3. Connect the organisation

Accounting → Connect. Authorise the specific Xero organisation you bill from. The organisation name appears on the connection once it is established.

4. Discover

Discover pulls the contact list, chart of accounts and tax rates.

5. Map accounts

Map usage, recurring, goods and discount lines to revenue account codes from your Xero chart of accounts.

6. Map customers

Map each billing tenant to a Xero contact. Auto-bind proposes matches from account references and domain names; confirm each one.

7. Apply a tax profile

Every Xero tenant needs a tax profile applied in Tax, because Vodia Billing is computing the tax that Xero will record.

Tax types

Xero invoice lines carry a TaxType. Vodia Billing supplies the tax type alongside the amount it computed.

Tax types that silently produce zero

Two failure modes here produce a 0.00 tax figure with no error at all:

  • TaxType: "AVALARA" returns a successful response with zero tax.
  • GUID-shaped tax type values are Avalara-managed and cannot be used on a manual tax document.

Both look like success. Always check the tax figure on the first invoice pushed to a new Xero organisation, in Xero itself, before you send anything to a customer.

Use the tax rates that Discover pulled from your own organisation. Those are the ones that work.

Totals

For a Xero tenant, Vodia Billing's total is the invoice total. There is no separate face value to reconcile, because Xero is recording what it was given rather than computing its own figure.

The divergence detector treats Xero this way, so a mismatch on a Xero tenant is a real problem and worth investigating, unlike on QuickBooks.

One push, and no re-push button

Vodia Billing creates Xero invoices with PUT, not POST, and this is deliberate.

POST /Invoices silently overwrites an authorised invoice

Xero's POST /Invoices behaves as an upsert. Sent against an invoice number that already exists — including one already authorised — it overwrites it without error.

PUT is create-only. A second attempt against the same number returns a 400 rather than destroying the original.

On top of that, Vodia Billing refuses a second push locally, before any API call, once an invoice is recorded as pushed. Between the local blocker and PUT, a duplicate push cannot happen.

The consequence is that there is no re-push button. If a push genuinely needs redoing, the invoice must be voided in both systems and reissued.

Daily API ceiling

Xero's daily limit is 1,000 calls per organisation, not 5,000.

A pushed tenant costs roughly three calls, so a full fleet push is limited to around 300 tenants per organisation per day. Bill runs pace themselves against this and stop cleanly when the budget is spent, reporting what remains for the next day.

If you bill more than a few hundred tenants from one Xero organisation, split the fleet across multiple runs on consecutive days, or across multiple organisations.

Sending the invoice

For a Xero tenant, either side can send. Configure it per tenant:

  • Vodia Billing sends — the customer receives the Vodia Billing invoice email, with call detail attached and a share link. Xero holds the accounting record.
  • Xero sends — Xero emails its own invoice. Vodia Billing then treats the invoice as externally delivered and does not send its own copy.

Reminders follow the same setting. This is why tax authority and delivery are tracked separately: a Xero tenant has Vodia Billing computing tax while either side may be sending.

Credits

Because Vodia Billing owns the tax on a Xero tenant, credits can be issued from within Vodia Billing. They apply after tax, and voiding the invoice restores them.

The corresponding credit note still has to be raised in Xero. Vodia Billing records the credit but does not create Xero credit notes.

Australian GST

Check the GST on your first discounted AU invoice

The discount travels to Xero as its own negative line, so GST should be computed on the discounted base.

Verify that figure by hand in Xero on the first Australian invoice that carries a discount, before it reaches the customer. Tax is the one area where a wrong result produces an invoice that looks entirely normal.

Worked example

What arrives in Xero

The invoice from Worked Example, pushed to Xero:

Invoice INV-00151        Contact: Acme Corp
Date 2026-09-01 Due 2026-09-15 Status: AUTHORISED

Line Qty Unit Account TaxType Amount
US National 1240.00 0.012 200 OUTPUT2 14.88
United Kingdom 86.00 0.035 200 OUTPUT2 3.01
UK Mobile 12.00 0.14 200 OUTPUT2 1.68
US Toll Free 318.00 0.00 200 OUTPUT2 0.00
Extensions 42 12.00 200 OUTPUT2 504.00
Hunt groups 3 5.00 200 OUTPUT2 15.00
DID numbers 18 1.50 200 OUTPUT2 27.00
Managed support plan 1 150.00 200 OUTPUT2 150.00
Desk handset 1 89.00 260 OUTPUT2 89.00

Subtotal 804.57
Total Tax 82.72
TOTAL 887.29

Xero's total is 887.29, the same as Vodia Billing's, because Xero is recording the tax figures it was given rather than computing its own.

That is why a divergence alert on a Xero tenant is a real problem worth investigating, unlike on QuickBooks.

The failure that looks like success

The same push with the wrong tax type:

  Line                        Qty      Unit    Account   TaxType     Amount
US National 1240.00 0.012 200 AVALARA 14.88
...
Subtotal 804.57
Total Tax 0.00 <-
TOTAL 804.57

HTTP 200. No error, no warning, no alert. An invoice that is $82.72 short, sitting in Xero as authorised.

AVALARA and GUID-shaped tax type values are Avalara-managed and cannot carry manual tax. Use the tax rates Discover pulled from your own organisation.

Check the tax figure on the first invoice pushed to any new Xero organisation, in Xero, before it goes to a customer.

A second push

POST /api/invoices/<id>/push
409 Conflict — invoice already pushed to Xero on 2026-09-01 03:14

Refused locally, before any API call. And if it somehow reached Xero, the adapter uses PUT:

PUT /Invoices  (InvoiceNumber INV-00151)
400 Bad Request — invoice number already exists

POST /Invoices would have behaved as an upsert and overwritten the authorised invoice without error. PUT is create-only. Between the local blocker and PUT, a duplicate push cannot happen — and that is why there is no re-push button.

Pacing against the daily ceiling

Push run, Xero org "Northwind Telecom"
Ceiling: 1,000 calls/day
Consumed: 996
Pushed: 331 tenants (~3 calls each)
Remaining: 68 tenants — resume tomorrow

At about three calls per tenant, a single Xero organisation supports roughly 330 pushed tenants per day.