Core Architecture Regulatory Mapping

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 | Y unions, match, frozenset set algebra, and dataclass(slots=True).
  • A normalized entity carrying its canonical home JurisdictionCode and EntityType, 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 per-state activity signals through nexus rules to the set of triggered foreign-qualification and annual-report obligations Per-state activity signals (payroll, property, revenue) for one entity are tested against per-jurisdiction nexus rules keyed to registration statutes; rules that fire converge into a triggered-obligation set that pairs each foreign qualification with the recurring annual report it induces, while rules that do not fire emit nothing. ACTIVITY SIGNALS NEXUS RULES · per state TRIGGERED OBLIGATIONS Payroll headcount employees per state Property / office lease · warehouse Gross revenue intrastate, per state CA · intrastate business? Corp. Code §2105 · payroll or office NY · doing business? BCL §1301 · office or systematic revenue TX · transacting business? BOC §9.001 · property or payroll DE · doing business? 8 Del. C. §371 · office or agent evaluate fires frozenset of TriggeredObligation CA qualify → §1502 SOI biennial for LLC, annual corp recurring obligation attached NY qualify → §408 biennial statement every two years TX register → PIR franchise tax · Tax Code §171 DE qualify → §502 report annual report + franchise tax rule does not fire → no-nexus · emits nothing Each fired rule attaches the recurring filing its qualification induces — nexus is never a one-time cost Output is a set: idempotent, order-independent, one entry per triggered jurisdiction
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.

  1. 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 REVENUE reading below revenue_floor_cents emits no_nexus and the record is queued for a human “doing business” determination rather than auto-registered, because the resolution is a legal judgement, not a heuristic.
  2. 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_on signal so the downstream layer can back-date the qualification window and escalate to counsel instead of quietly filing as though presence began today.
  3. Unknown jurisdiction in the activity feed (system). An activity record arrives for a state with no NexusRule. classify skips 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.
  4. 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_state guard 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.