AWRA OpsHub Search

Destination-Based Sales Tax Needs a Destination

The rate depends on where your customer is. Our customer record holds a latitude to seven decimal places and has no field for the state — and when we went looking for where the question gets asked, we found it asked four times and answered from the wrong record every time. Two of the four are fixed as of publication.

Accounting Insights Washingtone Aura 12 min read

US sales tax is destination-based. The rate is set by the state, and usually by a county or a city underneath it, and which one applies turns on where the customer takes delivery. So the rate is a property of the destination, and the destination is a property of the customer record. That single sentence is the whole design constraint, and it is the one that a system built for value-added tax will not satisfy — not because it is missing a feature, but because it is keyed on the wrong thing.

Nothing here is tax advice, and nothing here will tell you where you have an obligation to collect. That is a legal determination about your own activity and it belongs to your adviser. What follows is about data modelling, and we are going to use our own schema as the specimen because it is the one we can open up.

The customer record, as it actually is

Here is the tax-relevant half of our customer table. Read the last two rows before the first four.

Field Type What it can answer
country varchar(100) A country. Enough for a VAT jurisdiction and nothing below it.
address varchar(255) One free-text line. A person can read a state out of it; nothing can key a rate to it.
latitude decimal(10,7) A position to roughly a centimetre.
longitude decimal(10,7) The same again.
state or province no column Nothing. The field does not exist.
postal code no column Nothing. Also absent.

We can place a customer precisely enough to identify a loading dock, and we cannot say which state they are in. Those two facts sit in the same row of the same table.

It is worth saying why the coordinates are not a solution dressed as a joke. A point can be resolved to a taxing jurisdiction — that is what boundary datasets are for, and it is a real product that real companies maintain. It is not a thing you get for free by having stored two decimals, and it is not something we have built or would claim. What we will not do is the cheap version: a regular expression over a free-text address line, deciding real tax on real money, failing silently on every address that does not fit the pattern.

Four places the question gets asked

This is the part that surprised us. The expectation going in was that the customer's jurisdiction would simply be absent from the model — a thing nobody had thought about. What we found instead was the question asked in four places and discarded in all four. Every row below is checkable by reading the file named in it.

  1. A resolver that takes three things it does not read

    Our tax context resolver accepted a customer id, an item id and a category name, and read none of them — the rate came from the organization, the tax type and a default flag. Fixed: it reads the customer now, and the other two parameters are gone rather than newly honoured. There is no behaviour behind them, and a signature that accepts an argument it discards is a promise to every caller that it matters.

  2. A resolver with a customer parameter and no callers

    A second method took a customer id and returned a "customer exempt" value. That value was set to false as a literal, with a comment beside it describing it as a placeholder, and the method was called from nowhere in the product. Fixed by removal: the parameter and the key are gone, and the method keeps the one job it was really doing.

  3. A jurisdiction column that selects nothing

    Our rate table has a jurisdiction region column alongside a jurisdiction country column. It is written by the settings screens. Rate selection filters on organization, tax type and default flag — the jurisdiction is never part of the query.

  4. A whole table nothing read, half of which now does

    There is a per-customer tax settings table: registration status, registration number, registration date, an exemption flag, an override rate, an international-trade flag. It was written by the customer form, shown on the customer page, and read by no tax calculation at all. Half fixed: the exemption flag is read now. The override rate still is not.

The fourth one was a defect, and we fixed it the same day

A customer you marked tax-exempt was charged anyway. The flag was captured, stored and displayed, and every sales document resolved tax from the organization alone. We found it writing this post — and because it was a defect rather than a missing feature, the right response was to fix it rather than to add it to a quote. An exempt customer is now untaxed on invoices, quotations and point-of-sale sales alike. The override rate on the same record is still inert, deliberately: that is a per-customer rate override, a feature rather than a correction, and switching it on would change what customers are charged in a second way nobody asked for.

Why four asks and no answers is more interesting than an absence

A missing capability tells you what to build. A capability whose shape is already present in the code — parameters, columns, a table, a return value — and which resolves to the organization every time tells you something more useful: that somebody understood the requirement, started the plumbing, and stopped before connecting it. That is not a criticism of whoever wrote it. It is the ordinary result of a product growing up in a market where the country-level answer is correct.

It also changes the estimate. "We do not support destination-based tax" sounds like a rewrite. "There is a jurisdiction column that nothing selects on, a customer table nothing reads, and two resolvers that accept a customer id and ignore it" is a list of connections, plus one genuinely new field on the customer record. Those are very different quotes, and the second is the honest one — and it proved to be the accurate one too, because three of those four turned out to be wiring and were done the day this was written.

The general lesson, which is not about tax

Parameters that are accepted and not read are a specific kind of debt, and they are worse than a missing parameter, because they are indistinguishable from a working one at every call site. Somebody passing a customer id to that resolver has every reason to believe it matters. The signature is a promise, and it was not being kept.

  • Grep your own codebase for function parameters that never appear in the body. Static analysis finds these and most configurations do not fail the build on them.
  • Look for columns written by a form and read by nothing. A migration plus a form plus a detail panel is the signature of a feature that was designed and then not wired.
  • Look for return values that are always the same literal. Ours had a suppression comment on it, which is at least honest, and is also exactly what an audit should be searching for.
  • For anything you find, ask the question we should have asked sooner: can a user set this and reasonably believe it does something? If yes, it is a defect and not a gap.

What we would build, and the one thing we would decline

The buildable sequence is short and specific: a jurisdiction field on the customer and on the sale; the override rate on that same customer record, which is the last piece of it still inert; and the interface a specialist tax service plugs into, so the rate comes from something that does nothing but rates. What we would decline is maintaining a US rate table ourselves. Thousands of jurisdictions with rules that move is not a side line, and the responsible answer is an integration rather than a table we keep.

US sales tax in our schema, in three parts

What AWRA OpsHub does today

  • A jurisdiction country and jurisdiction region column on the rate table — half the plumbing, and it is genuinely there.
  • Per-customer tax exemption, read and applied — a customer marked exempt on their own record is not charged, on any sales document. Wired 2026-08-05 after this post found it inert.
  • A document vault with a scheduled retention and purge job, which is where an exemption certificate would live as evidence.
  • Machine-readable exports in CSV, XLSX and JSON, which is what any downstream tax engine needs from us.
  • Procurement, inventory, projects and governance, which is the operational half and is not affected by any of this.

What it does not do

  • A jurisdiction on the customer record. No state, province, region or postal code. This is the field everything else waits on.
  • Rate selection by jurisdiction. The column exists and no query uses it.
  • The per-customer override rate. The exemption flag beside it is read; this one is still stored and not applied, because it is a feature rather than a correction.
  • Exemption certificate handling with an issuing state, an expiry and a link to how a sale is taxed. A file in the vault is not that.
  • Any US sales tax calculation, filing or return, at state or local level.

Not ours, by choice

  • We will not tell you where you have nexus. It turns on facts about your own activity and it is a legal determination.
  • We will not read a state out of an address string. It is the obvious shortcut, it is demonstrable, and it produces confidently incorrect returns.
  • We will not maintain a US rate table. Thousands of jurisdictions with moving rules belongs to a service that does nothing else.
  • We would decline a US sales tax build as a first engagement, because it is the largest gap we have and the worst possible introduction.

A jurisdiction field on the customer and the sale, the override rate on the customer record, and an interface to a specialist rate service — in that order, and priced as three pieces rather than one module. The precedent that market-specific tax work gets finished here is Kenya: a live tax-authority integration and a maintained statutory payroll engine, both built to specification and both still ours to run. A written specification, a timeline and a price agreed before anything starts, and no dates on a public page.

The exemption flag is the item on the middle list we would do first, and it is the only one of the four that is a defect rather than an absence. A user can set it today and reasonably believe it does something.

This is scope, not a ceiling

What is not built for the United States today can still be built for you

Anything described above as not built is a statement about what ships in the standard product today — not a limit on what AWRA OpsHub can do in the United States. Kenya's eTIMS integration and its maintained payroll engine exist because Kenyan clients needed them and commissioned them; neither appeared by itself. The same door is open here. If a jurisdiction on the record, and settings that are actually read, a bank or mobile money feed, a statutory return format, a rule your own operation needs that the standard one does not have, or a link to a system you already run is what stands between you and a decision, tell us and we will scope it as a build — written spec, timeline and price — before you commit to anything.

Three builds, and we would decline the fourth

A jurisdiction field on the customer and on the sale — a real field, not a state parsed out of a free-text address line, which is the cheap version and produces confidently wrong returns. Then per-customer tax settings that are read rather than only stored: the exemption flag, the registration number and the override rate all exist on the customer record today and no tax calculation looks at any of them, which is a defect fix rather than a feature. Then the interface a specialist tax service plugs into, so the rate comes from something that does nothing but rates. What we would decline is the fourth build — a US rate table we maintain ourselves. Thousands of jurisdictions with rules that move is not a thing to own as a side line, and we would rather say so in a specification than discover it in year two.

Banks and payments

ACH origination and bank statement feeds wired into the Payments Register, so money in and out reconciles against the documents that authorised it rather than against a spreadsheet.

The operational work, which is what most commissions actually are

An extra approval stage in a chain that does not match the standard one, a custom field set on employees or assets that only your sector needs, an expiry that has to block an order rather than send an email, a report your board asks for in a shape nothing produces, or a scanner or weighbridge feeding the goods-in door. These are the commissions we are asked for most often and the smallest ones we quote — and unlike a revenue-authority pipeline, none of them waits on a regulator.

Payroll and statutory returns

A US payroll engine with federal and state withholding, FICA, quarterly and annual returns, state unemployment insurance and multi-state allocation for employees who cross state lines. None of it exists today.

Systems you already run

The accounting package, CRM, online store or custom database you intend to keep — connected through our API so a fact is entered once and appears everywhere it is needed.

How it works: you describe the requirement, we return a written scope, timeline and cost, and once agreed it is built into your environment and maintained as part of the product. No roadmap slide, and no pretending in a demo that something exists when it does not.

Tell us what you need integrated

The one-line version

Destination-based tax needs a destination on the record, and a coordinate is not one. Before you evaluate any vendor's US tax capability, ask which field on the customer the rate is keyed to — and if the answer is the address line, ask what happens to the addresses that do not parse.

Ask us what our schema holds

The four asks above are all checkable, and we would rather walk you through them than have you discover the fourth one in an audit.

Read the United States page

Help Center

Need a quick answer while you read?

Run inventory, procurement, assets, sales, and field work with approved AWRA guidance for setup, migration, integrations, security, pricing, and support.

Search all approved AWRA public help articles.

Open Help Center