US economic-nexus thresholds
US economic-nexus thresholds
The default NexusThresholds binding is RegisterNexus, which reads threshold
rules from the installed register. It selects
USD thresholds whose binds field is remote_seller. The old static threshold
table has been removed.
What the engine does with them
The US regime takes nexus from an explicit SellerRegistration. When the seller
is not registered in a state, the NotRegistered assessment reason includes the
available threshold as an indication that registration may be needed.
use Cbox\Geo\ValueObjects\SubdivisionCode;
use Cbox\Tax\Contracts\NexusThresholds;
$threshold = app(NexusThresholds::class)->for(
new SubdivisionCode('US-TX'),
new DateTimeImmutable('2026-09-18'),
);
$description = $threshold?->describe();
A missing threshold returns null; it does not establish that a seller has no
obligation. The lookup supplies figures and a combinator, not a decision about a
seller's accumulated sales.
What counts, and from when — measuredBy and obligations
Twelve states publish more than a number. NexusThreshold carries both extra parts
in the state's own words, because applying them needs facts only the host has:
foreach ($threshold->measuredBy as $rule) {
// marketplace_sales / excluded, affiliated_persons / aggregate, transaction_unit / invoice …
[$rule->dimension, $rule->treatment, $rule->says];
}
foreach ($threshold->obligations as $obligation) {
// remit / first_crossing_in_current_year / first_month_start_on_or_after_days / 30
[$obligation->action, $obligation->trigger, $obligation->dateKind, $obligation->dateFigure];
}
measuredBy changes whether the figure was crossed at all: Arizona excludes
marketplace-facilitated sales from the count and aggregates affiliated persons, so a
seller measuring gross turnover against the bare figure can be wrong in either
direction. obligations changes the date collection starts — Arizona's is the
first day of the month beginning at least 30 days after the crossing, not the
crossing itself, so a system that starts charging immediately bills tax for up to two
months the state did not ask for.
Both are reported, never applied. This package is not told the seller's marketplace sales, its affiliates, or the day it crossed. Refusing them instead left Arizona, California, Colorado, Iowa, Michigan, Minnesota, North Carolina, North Dakota, Oklahoma, Tennessee, Vermont and Wisconsin with no threshold at all, which was worse.
The threshold in the law's words
Alongside the figures, NexusThreshold carries the register's description of what
they mean, for display and review: measuredOver (the period the figure is measured
over, such as current_or_previous_calendar_year), counts (which sales count
towards it), basis, says (the source's own words, verbatim) and source (the
register's key for the document it was read from). Each is null where the register
publishes none.
note is the register maintainer's note: it quotes the source and then says how the
register read it — what it could not store, which comparator it followed. It is for a
reviewer, not a customer; show says as the law's words, never note.
Scope and dates
NexusThresholds::for() accepts an optional date and selects the rule whose
inclusive effective window contains it. Omitting the date means today. The US
regime passes the supply date when producing its advisory annotation. Keep the
release pinned as well when reproducing an earlier result.
The adapter reads the combinators sales_and_transactions, sales_or_transactions
and sales_only; legacy and/or values remain accepted. A missing or unknown
combinator where a transaction count is published raises UnresolvedTaxRule —
the adapter never invents an OR condition — as do overlapping applicable thresholds
and a threshold whose applicability is itself conditional (conditions).
The operators are a different field: amountOperator and transactionsOperator
say whether the figure is crossed by exceeding it or by reaching it ("more than
$500,000" against "$500,000 or more"). Most states publish none, so an absent
operator is permitted and reported as null; only a stated value this reader does
not know refuses. A threshold qualified by unresolvedQualifications — the register
saying it has not modelled the statutory trigger — also refuses.
Custom implementations must add the optional ?DateTimeImmutable $at = null
parameter to their for() method.
Determining whether a seller crossed a threshold also requires the state's
measuring period and sales basis, plus the measuredBy rules above. That cumulative
calculation belongs to cboxdk/laravel-nexus or the host application, which then
states the outcome to this package as a
seller registration in the state. This package neither registers the
seller nor infers an obligation from a single invoice.
The amounts and transaction counts can change independently of this package.
Use tax:data:sync to install a reviewed release, retain its provenance, and bind
Contracts\NexusThresholds if your application supplies its own source.