# Tax

> The messiest area of the three APIs. QuickBooks in the United States computes tax itself and will not let you create rates; everywhere else you carry your own. And rounding rules differ enough that re-deriving tax from rate times base is always wrong.

Source: https://lopifi.com/apis/tax
Part of: Lopifi — US GAAP books on autopilot, reviewed by a CPA (https://lopifi.com)

---

**Who computes the tax, and can you override it?** Store the tax amount the source computed. Never recompute it, and never reduce it to a scalar — a tax return needs the components.

## Divergences between Xero, QuickBooks Online and NetSuite

### QuickBooks US is a different product from QuickBooks elsewhere

Under automated sales tax you cannot create tax rates through the API, and the tax on a transaction is computed by Intuit from the addresses on it. Amounts you send may be silently replaced. Outside the US, GlobalTaxCalculation and explicit tax codes behave the way Xero's do.

**Handling:** Expose canCreateTaxRates as a capability. The tax UI is read-mostly where it is false, and the write path does not promise a tax amount it cannot guarantee.

### Compound tax survives only as a list

Xero's TaxComponents and QBO's TaxLine both express 'this 13% is 5% federal plus 8% provincial'. A canonical taxAmount scalar throws that away — and the components are exactly what a tax return needs.

**Handling:** Model tax as { code, name, components: [{ name, rate, amount }], total }. The scalar is a computed property of the breakdown, not the storage.

### Never re-derive tax from rate times base

Rounding differs per system and per jurisdiction: per line or per document, half-up or half-even. Recomputing produces a number that is right most of the time and off by a cent on the invoices someone is looking at.

**Handling:** Store the source's amount. If you display a rate, display the source's rate. Where you must compute — a preview before a write — mark it as an estimate until the source answers.

## Canonical shape

```ts
type TaxComponent = { name: string; rate: string; amount: Money };

type TaxBreakdown = {
  code?: string;                 // the source's tax code, verbatim
  name?: string;
  /** Compound and multi-jurisdiction tax, kept whole. A return needs these. */
  components: TaxComponent[];
  total: Money;                  // as computed by the source, never by us
  mode: "exclusive" | "inclusive" | "none";
  computedBy: "source" | "estimate";
};
```
