Classifying Foreign Qualifications and Nexus-Triggered Filings
This guide is part of the Entity Taxonomy & Classification area within the Core Architecture & Regulatory Mapping framework: it takes the raw activity signals a portfolio company throws off — a payrolled employee in a new state, a leased office, revenue above a registration floor — and decides where that activity has crossed the line into “doing business,” obliging the entity to foreign-qualify and, from that moment, to file an annual report in each of those states forever after.
Scope of This Page
This page covers the classification step that sits one layer below the single-intent classifier: given an already-normalized entity and a stream of per-state activity signals, produce the set of jurisdictions where registration is triggered, each annotated with the qualification statute and the recurring obligation it drags in. It deliberately excludes the mechanics of filing the Certificate of Authority (a portal-ingestion concern) and the scheduling of the resulting annual reports, which the State Filing Deadline Calendars layer resolves into dated obligations. It also assumes the entity has already been resolved to a canonical type and home jurisdiction upstream; here we classify foreign exposure only, and we treat the home state as a given rather than re-deriving it.
The Constraint That Forces a Nexus Classifier
“Doing business” is a statutory term of art, not a business judgement, and every state defines the threshold differently. California requires any entity “transacting intrastate business” to register under Cal. Corp. Code § 2105, and failure to do so bars the entity from maintaining a court action under § 2203 while a penalty accrues; New York requires a foreign corporation “doing business in this state” to obtain authority under N.Y. BCL § 1301, with the same litigation bar under § 1312; Texas requires registration for a foreign entity “transacting business” under Tex. BOC § 9.001, with a late-registration penalty equal to the fees that would have accrued under § 9.054; Delaware requires qualification under 8 Del. C. § 371 before doing business there. The trap for automation is that qualification is not a one-time cost — the moment an entity registers, it inherits that state’s recurring annual-report and franchise-tax cadence (a Delaware foreign corporation now owes the § 502 annual report, a California registrant now owes the § 1502 Statement of Information). A classifier that detects nexus but forgets to attach the downstream obligations leaves the entity registered and silently delinquent a year later.
Prerequisites
- Python 3.10+ — for
X | Yunions,match,frozensetset algebra, anddataclass(slots=True). - A normalized entity carrying its canonical home
JurisdictionCodeandEntityType, produced upstream by the classification stage. - A per-state activity feed — payroll, property/lease, and revenue signals keyed to an ISO 3166-2 subdivision — sourced from ERP, HRIS, and the general ledger.
- A versioned nexus rule set encoding each state’s registration threshold as data, with a statutory citation per rule so a triggered obligation traces back to the code section that produced it.
- Standard library only beyond that:
logging,json,datetime,enum,dataclasses.
Implementation: A Deterministic Nexus Classifier
The classifier is a pure transformation: an entity plus its activity signals go in, a set of TriggeredObligation records comes out — one per state where a threshold was crossed, each carrying the qualification statute and the recurring filing it induces. Nexus rules are data, never branches, so a new state is a table row rather than a code change, and every trigger logs the exact rule and signal that fired it. The diagram traces one entity’s signals through the per-state rules to the emitted obligation set.
from __future__ import annotations
import json
import logging
from dataclasses import dataclass, field
from enum import Enum
# Structured JSON logging — every trigger is an auditable classification event.
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger("taxonomy.nexus")
class Signal(str, Enum):
PAYROLL = "payroll" # a W-2 employee physically working in the state
PROPERTY = "property" # a lease, office, warehouse, or owned real property
REVENUE = "revenue" # gross intrastate revenue attributable to the state
class Filing(str, Enum):
QUALIFY = "foreign_qualification" # Certificate of Authority / registration
ANNUAL_REPORT = "annual_report" # recurring report induced by qualification
STATEMENT_OF_INFO = "statement_of_information"
BIENNIAL_STATEMENT = "biennial_statement"
FRANCHISE_PIR = "franchise_tax_public_information_report"
@dataclass(frozen=True, slots=True)
class Activity:
"""One entity's measured presence in a single state during the period."""
state: str # ISO 3166-2 subdivision, e.g. "US-CA"
employees: int = 0
has_property: bool = False
gross_revenue_cents: int = 0
def has(self, signal: Signal, revenue_floor_cents: int) -> bool:
match signal:
case Signal.PAYROLL:
return self.employees > 0
case Signal.PROPERTY:
return self.has_property
case Signal.REVENUE:
return self.gross_revenue_cents >= revenue_floor_cents
@dataclass(frozen=True, slots=True)
class NexusRule:
"""A data-defined registration threshold for one jurisdiction.
A rule fires when ANY of `triggering_signals` is present. `induced` is the
recurring obligation that qualification drags in — nexus is never a one-time cost,
so we attach the downstream filing at trigger time, not after registration.
"""
state: str
citation: str # the statute that defines "doing business" here
triggering_signals: frozenset[Signal]
revenue_floor_cents: int # only consulted for the REVENUE signal
induced: Filing # the recurring filing attached on trigger
@dataclass(frozen=True, slots=True)
class TriggeredObligation:
state: str
qualification_citation: str
recurring_filing: Filing
fired_on: Signal # the specific signal that established nexus
# The rule set is data. Adding a state is a new row, never a new branch. Thresholds
# and citations are the compliance-critical lines — each traces to a code section.
NEXUS_RULES: tuple[NexusRule, ...] = (
NexusRule("US-CA", "Cal. Corp. Code §2105 (register) / §1502 (recurring)",
frozenset({Signal.PAYROLL, Signal.PROPERTY}), 0, Filing.STATEMENT_OF_INFO),
NexusRule("US-NY", "N.Y. BCL §1301 (authority) / §408 (recurring)",
frozenset({Signal.PROPERTY, Signal.REVENUE}), 1_000_000_00, Filing.BIENNIAL_STATEMENT),
NexusRule("US-TX", "Tex. BOC §9.001 (register) / Tax Code §171 (recurring)",
frozenset({Signal.PROPERTY, Signal.PAYROLL}), 0, Filing.FRANCHISE_PIR),
NexusRule("US-DE", "8 Del. C. §371 (qualify) / §502 (recurring)",
frozenset({Signal.PROPERTY, Signal.PAYROLL}), 0, Filing.ANNUAL_REPORT),
)
class NexusClassifier:
"""Emit the set of jurisdictions where an entity's activity triggers registration."""
def __init__(self, rules: tuple[NexusRule, ...] = NEXUS_RULES, home_state: str = "") -> None:
self._rules = {r.state: r for r in rules}
self._home = home_state # the home state is a domestic obligation, never "foreign"
def classify(self, activities: list[Activity]) -> frozenset[TriggeredObligation]:
triggered: set[TriggeredObligation] = set()
for activity in activities:
rule = self._rules.get(activity.state)
if rule is None or activity.state == self._home:
continue # unknown jurisdiction or the home state: not a foreign trigger
fired = self._first_fired_signal(activity, rule)
if fired is None:
logger.info(json.dumps({"event": "no_nexus", "state": activity.state}))
continue
obligation = TriggeredObligation(
state=rule.state,
qualification_citation=rule.citation,
recurring_filing=rule.induced,
fired_on=fired,
)
triggered.add(obligation)
logger.info(json.dumps({
"event": "nexus_triggered", "state": rule.state,
"fired_on": fired.value, "citation": rule.citation,
"induced_filing": rule.induced.value,
}))
return frozenset(triggered)
@staticmethod
def _first_fired_signal(activity: Activity, rule: NexusRule) -> Signal | None:
# Deterministic order so the "reason" logged for a trigger is reproducible.
for signal in (Signal.PROPERTY, Signal.PAYROLL, Signal.REVENUE):
if signal in rule.triggering_signals and activity.has(signal, rule.revenue_floor_cents):
return signal
return None
if __name__ == "__main__":
entity_activity = [
Activity("US-CA", employees=3), # payroll -> CA nexus
Activity("US-NY", gross_revenue_cents=2_500_000_00), # revenue over floor -> NY nexus
Activity("US-TX", employees=0, has_property=False), # no presence -> no trigger
Activity("US-DE", has_property=True), # office -> DE nexus
]
classifier = NexusClassifier(home_state="US-DE") # DE is home: excluded from foreign set
result = classifier.classify(entity_activity)
logger.info(json.dumps({
"event": "classification_complete",
"triggered_states": sorted(o.state for o in result),
}))
The classifier is a leaf: it decides only where nexus exists and what recurring filing each qualification induces. It does not file the Certificate of Authority, and it does not compute the report’s due date — it emits a set that the downstream calendar and portal layers consume. Because the output is a frozenset, re-running the same activity feed produces an identical, order-independent result, which is exactly what an audit of “why did we register this entity in New York” requires.
Configuration Reference
Every threshold below is data on a NexusRule, not a literal in the classifier, because “doing business” is defined per state by statute and shifts as case law and revenue floors move.
| Jurisdiction | Registration statute | Nexus signal that triggers | Recurring obligation induced | Citation for the recurring filing |
|---|---|---|---|---|
| Delaware (US-DE) | 8 Del. C. § 371 | Office, agent, or in-state payroll | Annual report + franchise tax | 8 Del. C. § 502 |
| California (US-CA) | Cal. Corp. Code § 2105 | Any intrastate payroll or office | Statement of Information | Cal. Corp. Code § 1502 |
| New York (US-NY) | N.Y. BCL § 1301 | Office, or systematic revenue above the floor | Biennial statement | N.Y. BCL § 408 |
| Texas (US-TX) | Tex. BOC § 9.001 | Property or payroll presence | Franchise-tax Public Information Report | Tex. Tax Code § 171 |
revenue_floor_cents |
per rule | Guards the REVENUE signal so a trivial mail-order sale is not treated as “systematic and continuous” business |
— | — |
home_state |
per entity | Excludes the domestic jurisdiction so a home filing is never miscounted as a foreign trigger | — | — |
Failure Modes and Fallback Routing
Each fault maps onto the tiered scheme the parent area’s classifier already uses — a high-confidence direct result, an async-validation tier, or a legal-ops halt — and the nexus classifier feeds those tiers rather than deciding registration unilaterally.
- Ambiguous “doing business” near a threshold (validation tier). A single remote employee or a handful of mail-order sales sits in the grey zone between economic activity and statutory nexus. The classifier only fires on a rule whose signal is unambiguously present; a borderline
REVENUEreading belowrevenue_floor_centsemitsno_nexusand the record is queued for a human “doing business” determination rather than auto-registered, because the resolution is a legal judgement, not a heuristic. - Retroactive nexus discovered after the fact (statutory). Payroll data arrives late and reveals the entity has been doing business in a state for eight months already. The trigger is real but so is the accrued late-registration penalty (Texas assesses fees back to the nexus date under Tex. BOC § 9.054). The obligation is emitted with its
fired_onsignal so the downstream layer can back-date the qualification window and escalate to counsel instead of quietly filing as though presence began today. - Unknown jurisdiction in the activity feed (system). An activity record arrives for a state with no
NexusRule.classifyskips it and logs the gap rather than crashing; the missing state is a feedback signal that the rule set needs a new row, treated the same way the parent classifier treats an unmapped alias — a registry gap, not a defect. - A signal for the home state (data-validation). The general ledger reports revenue in the entity’s own formation state. That is a domestic obligation, not a foreign trigger; the
home_stateguard drops it so it is routed to the entity’s normal annual-filing path through the Compliance Metadata Schemas rather than double-counted as a foreign qualification.
Frequently Asked Questions
Why does a triggered qualification also carry a recurring filing instead of just the registration?
Because nexus is never a one-time cost. The instant an entity registers under, say, Cal. Corp. Code § 2105, it inherits California’s § 1502 Statement of Information cadence for as long as it stays qualified. If the classifier emitted only the QUALIFY action, the entity would register, file once, and then go silently delinquent a year later when the recurring obligation nobody attached comes due. Attaching induced at trigger time is what keeps the downstream calendar honest.
How do I keep a single remote employee from triggering registration everywhere they happen to live?
That is exactly what the signal thresholds are for. Payroll presence firing a rule is a state-by-state policy decision encoded on the NexusRule, and the revenue_floor_cents guard keeps a trivial, non-systematic activity below the line. Where the reading is genuinely borderline the classifier declines to fire and routes the record to a human “doing business” determination rather than auto-registering, because a marginal nexus call is a legal judgement the code should surface, not make.
Why return a frozenset instead of a list of obligations?
A set is idempotent and order-independent, which matches the domain: an entity either has nexus in a state or it does not, and two activity records for the same state must not produce two registrations. Running the same activity feed twice yields an identical set, so the classification is reproducible under audit — an examiner replaying the feed obtains the same triggered states, and any divergence points to a changed rule version rather than a non-deterministic classifier.
Where does this sit relative to the single-intent entity classifier?
It runs after it. The Entity Taxonomy & Classification stage resolves an entity to its canonical type and home jurisdiction; this classifier then takes that normalized entity plus its activity signals and expands its foreign exposure into the set of states where registration is triggered. Each triggered obligation is a new single-intent record that flows into the deadline and schema layers exactly like a domestic one.