# Performance West — 52-Jurisdiction Business Formation System

**Last updated:** 2026-04-05

50 US states + District of Columbia + British Columbia (Canada) = 52 jurisdictions.

## Architecture

```
Customer                    Website                      API                    Database
  |                           |                           |                       |
  +- Select state ----------->|                           |                       |
  +- Search name ------------>+- GET /states/:code/ ----->+- (proxy to Python) -->|
  |  <-- available/taken ---- |    name-search             |                       |
  +- Fill details ----------->|                           |                       |
  +- Submit order ----------->+- POST /formations ------->+- INSERT ------------->|
  |  <-- order number ------  |                           |   formation_orders     |
  |                           |                           |                       |
  |                    ERPNext (BPM Orchestrator)          |                       |
  |                           |                           |                       |
  |                    +------+------+                    |                       |
  |                    | Webhook     |<-- order.created --|                       |
  |                    | triggers    |   (not polling)      |                       |
  |                    | worker      |                    |                       |
  |                    +------+------+                    |                       |
  |                           |                           |                       |
  |                    +------+------+                    |                       |
  |                    | Load state  |                    |                       |
  |                    | adapter     |                    |                       |
  |                    +------+------+                    |                       |
  |                           |                           |                       |
  |                    +------+------+                    |                       |
  |                    | Playwright  |--> State SOS       |                       |
  |                    | automation  |    Portal          |                       |
  |                    +------+------+                    |                       |
  |                           |                           |                       |
  |                    +------+------+                    |                       |
  |                    | Update DB   |---- UPDATE ------->|                       |
  |                    | with result |   status=filed      |                       |
  |                    +------+------+                    |                       |
  |                           |                           |                       |
  |  <-- email docs --------- |                           |                       |
```

**ERPNext as BPM orchestrator:** ERPNext webhooks trigger formation workers on order events (e.g., `order.created`, `payment.confirmed`). Workers are event-driven, not polling-based.

## Directory Structure

```
scripts/formation/
+-- __init__.py
+-- base.py                     # StatePortal base class + data models
+-- name_search.py              # Multi-state name search coordinator
+-- formation_worker.py         # Event-driven queue processor (ERPNext webhook triggers)
+-- operating_agreement.py      # Template-based .docx/.pdf generator
+-- ein_worker.py               # IRS EIN online application (Playwright)
+-- document_delivery.py        # SMTP email with attached formation docs
+-- states/
    +-- __init__.py             # Registry: get_adapter(), get_config(), STATES dict
    +-- al/                     # Alabama
    |   +-- __init__.py
    |   +-- config.py           # Portal URLs, NW RA address, fees, CSS selectors
    |   +-- adapter.py          # ALPortal(StatePortal) -- search + filing automation
    +-- ak/                     # Alaska
    +-- az/                     # Arizona
    ... (51 US: 50 states + DC)
    +-- dc/                     # District of Columbia
    +-- bc/                     # British Columbia (Canada) -- CRTC Carrier Package
```

## BC (Canada) — CRTC Carrier Package

British Columbia uses a completely different workflow from US states:

- **Not a state SOS filing** — BC adapter handles the CRTC Carrier Package registration
- **Different portal:** BC Corporate Online (bcregistryservices.gov.bc.ca) — anonymous, no login required
- **CRTC requirements:** Carrier registration with the Canadian Radio-television and Telecommunications Commission
- **Separate API endpoint:** `/api/v1/canada-crtc` for BC-specific orders
- **Separate DB table:** `canada_crtc_orders` tracks BC orders independently
- **14-step pipeline** managed by `services/canada_crtc.py`

The BC adapter implements `BCPortal(StatePortal)` but overrides both name search and filing methods for Canadian requirements. BC government fees are passed through at cost.

### BC Config (`scripts/formation/states/bc/config.py`)

The BC config contains configuration blocks for all Canadian-specific services:

| Config Block | Purpose |
|-------------|---------|
| `bits` | BITS (international telecom) registration — filing URL, required documents |
| `ccts` | CCTS (complaint commission) membership — application URL, required info |
| `gckey` | GCKey signup wizard — 5-step URLs, CSS selectors, hCaptcha sitekeys, password rules |
| `ats` | Annual Telecommunications Survey — 6 survey forms (REP-T/T1, REP-U, 802a, 802j, Facilities, Pricing) with deadlines and revenue thresholds |
| `corporate_obligations` | BC corporate tax/filing obligations — T2, GST/HST, T4/T4A, BC PST, WorkSafeBC, CRTC update |

### GCKey Provisioning

Each carrier needs its own GCKey account (Government of Canada credential) for filing
annual telecommunications surveys via My CRTC Account.

- **Username format:** `pw-{bc_number}`
- **Recovery email:** `regulatory@domain.ca` (we control the mailbox)
- **Automation:** `gckey_provisioner.py` — Playwright-based 5-step signup wizard
- **hCaptcha:** Invisible challenge on Step 2 (sitekey `99871bd1-7b22-417a-b6cc-7ef645e5147a`)
- **Credentials stored** in ERPNext Sensitive ID (encrypted)
- **Steps 3-5** selectors inferred from Step 1-2 recon — need verification on first live run

### Compliance Calendar (17 Entries Per Carrier)

Created at pipeline Step 13. Entries span regulatory, corporate tax, and survey obligations:

**Regulatory (7):** BC Annual Report, CRTC Maintenance, Mailbox Renewal, Domain Renewal,
DID Renewal, CCTS Renewal, CRTC Registration Update

**Corporate Tax (6):** T2 Return (Jun 30), Tax Payment (Mar 31), GST/HST (Mar 31),
T4/T4A Slips (Feb 28), BC PST (volume-based), WorkSafeBC (Mar 1 — if employees)

**Annual Telecom Surveys (4+):** REP-T/T1 (mandatory for ALL carriers, Mar 1),
REP-U/802a/802j/Facilities/Pricing (only if >$10M CAD revenue)

## State Adapter Status

### Name Search Methods

| Method | States | How It Works |
|--------|--------|-------------|
| **Socrata REST API** | CO, IA, IL, MI, NY, OR, PA, VT, WA | Free JSON API via state open data portals. No browser needed. |
| **SFTP Bulk Download** | FL | Free SFTP (public credentials) with daily entity dumps. Pre-load into local DB. |
| **Playwright** | All remaining ~40 US states | Browser automation against state SOS web portals. |
| **BC Corporate Online** | BC (Canada) | Browser automation against BC Registry Services. |

### Filing Method

**All 51 US jurisdictions use Playwright** — no state offers a filing API.
**BC (Canada)** uses Playwright against BC Corporate Online + CRTC portal.

### Implementation Status

| State | Name Search | LLC Filing | Corp Filing | Selectors Verified |
|-------|-------------|-----------|-------------|-------------------|
| **WY** | Working | Stub (needs filing walkthrough) | Stub | Name search: YES |
| **CO** | Socrata API (working) | Stub | Stub | N/A (API) |
| **BC** | Stub (BC Corporate Online) | N/A (CRTC Carrier Package) | Stub | Need portal inspection |
| All others | Stub (structure ready) | Stub | Stub | Need portal inspection |

~12 states have real implementations, ~40 still stubbed.

### Per-State Work Required

For each state, the following work is needed to go live:

1. **Visit the state's name search URL** in a browser
2. **Inspect form elements** — record CSS selectors for input fields, buttons, result areas
3. **Update `config.py`** with verified selectors
4. **Test name search** with known entities
5. **Visit the filing URL** and walk through the LLC formation form
6. **Record all filing form selectors** — name, agent, address, member, payment fields
7. **Implement the `file_llc()` method** with the state-specific workflow
8. **Test with a real formation** (or test/sandbox environment if available)
9. **Repeat for `file_corporation()`**

Estimated time: **1-3 hours per state** depending on portal complexity.

## API Endpoints

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/v1/states` | All 52 jurisdictions with fees |
| `GET` | `/api/v1/states/:code/name-search?name=...` | Name availability check |
| `POST` | `/api/v1/formations` | Create formation order |
| `GET` | `/api/v1/formations/:orderNumber` | Check order status |
| `GET` | `/api/v1/bundles` | List available service bundles |
| `GET` | `/api/v1/payment-methods` | Available payment methods + surcharges |
| `POST` | `/api/v1/agents` | Sales agent referral tracking |
| `POST` | `/api/v1/canada-crtc` | BC (Canada) CRTC Carrier Package order |

## Database Tables

### `state_filing_fees` — 52 rows (populated via migration 002)
All state/jurisdiction filing fees, portal URLs, special requirements (publication, franchise tax, etc.)

### `formation_orders` — Customer orders
Full order lifecycle: received -> processing -> submitted -> filed -> delivered.
Includes `erpnext_invoice_name` and `erpnext_payment_request` columns for ERPNext integration.

### `nwra_wholesale_pricing` — NW RA costs per state
Our wholesale cost for registered agent service (TBD from portal exploration).

### `canada_crtc_orders` — BC Canada orders
Separate table for CRTC Carrier Package orders with Canadian-specific fields.
Includes `erpnext_invoice_name` and `erpnext_payment_request` columns.

### `service_bundles` — Pre-configured service bundles
Bundle definitions (e.g., "Complete Formation + RA + EIN") with 20% discount applied.

### `bundle_orders` — Bundle order tracking
Links bundle purchases to individual service orders.

### `sales_agents` — Referral agent accounts
Agent ID (REF-XXXXX), contact info, commission tier, payout details.

### `commission_ledger` — Agent commission tracking
Per-order commission records: agent_id, order_id, service_type, flat_commission_amount, status (pending/paid).

### `payment_surcharges` — Payment method surcharge rates
ACH 0%, Card 3%, Klarna 5%, CashApp 3%, AmazonPay 3%, Crypto 0%.

### `accounting_advisors` — Advisor profiles
Accounting advisor details for post-formation consulting referrals.

### `accounting_support_accounts` — Client accounting support
Links clients to assigned accounting advisors.

### `conversation_flags` — Support conversation metadata
Flags for support conversations (escalation, priority, category).

## Formation Worker

`scripts/formation/formation_worker.py` is triggered by ERPNext webhooks (event-driven):

1. ERPNext webhook fires on `order.created` or `payment.confirmed`
2. Worker loads the appropriate state adapter
3. Verifies name availability
4. Submits the filing via Playwright
5. Updates DB with filing number and status
6. Generates operating agreement (if ordered)
7. Obtains EIN (if ordered)
8. Emails all documents to customer

**Human-paced delays:** 30-120 minutes between orders (configurable via env vars) to avoid appearing automated.

**Single-instance locking:** `fcntl.flock` on `/tmp/formation-worker.lock` prevents concurrent runs.

## Document Generation — PDF Conversion

- **Primary:** Windows DocServer (108.181.102.34, port 22422) — Office 365 Word COM automation via MinIO transport
  - `docserver_worker.py` polls `to-convert/` bucket every 12 seconds
  - Converts via Word COM, drops PDF in `converted/` bucket
  - Heartbeat file at `minio://performancewest/docserver-heartbeat.json` (60s interval)
  - Atomic uploads via `.tmp_` prefix + `copy_object` rename
  - Task Scheduler: `PW-DocserverWorker` — self-healing: restarts on failure (99×/1 min) + AtStartup and a 5-min repeating trigger (relaunches within ~5 min if the process dies). The worker also retries MinIO on outage instead of exiting.
- **Fallback:** LibreOffice headless (`soffice --headless --convert-to pdf`) auto-activates when DocServer heartbeat stale (>5 min)
- **E2E tested:** 36KB DOCX → 82KB PDF in 12 seconds total round-trip

Operating agreements, CRTC letters, and other generated documents go through DocServer first. If the Windows VM is down, the system falls back to LibreOffice automatically.

## Northwest Registered Agent Integration

NW RA is used **only for registered agent service**, not for filings.
- Wholesale portal at accounts.northwestregisteredagent.com
- We order RA service for the customer's state after filing completes
- NW RA address for each state is stored in the state's `config.py`

## Operating Agreement Generator

Template-based using `python-docx`:
- Standard 10-article LLC operating agreement
- Variables filled from order data (entity name, state, members, etc.)
- Outputs `.docx` + `.pdf` (via Windows DocServer, LibreOffice fallback)
- Disclaimer: "This is not legal advice"

## EIN Obtainment

Playwright automation against IRS EIN Assistant:
- Available Mon-Fri 7am-10pm ET only
- Fills SS-4 equivalent online form
- Extracts EIN from confirmation page
- Saves PDF confirmation

## Pricing

| Component | Customer Pays | Our Cost |
|-----------|--------------|----------|
| State filing fee | Pass-through (varies $35-$725) | Same |
| Service fee (basic) | $179 | $0 (our margin) |
| Service fee (complete) | $399 | $0 (our margin) |
| RA service | $99/year (WY: $49/year) | ~$80/year (NW RA wholesale, TBD) |
| EIN | $49 | $0 (IRS is free) |
| Operating agreement | $99 | $0 (template-generated) |
| Expedited | Pass-through (varies by state) | Same |

## Service Bundles (20% Off)

Customers can purchase pre-configured bundles at a 20% discount off individual pricing:

| Bundle | Includes | Individual Total | Bundle Price |
|--------|----------|-----------------|-------------|
| **Starter** | Basic Formation + EIN | $198 | $158 |
| **Professional** | Complete Formation + EIN + RA (1yr) | $573 | $458 |
| **Full Package** | Complete Formation + EIN + RA (1yr) + Operating Agreement | $672 | $538 |

Bundle orders are tracked in `service_bundles` and `bundle_orders` tables.

## Payment Methods & Surcharges

| Method | Surcharge | Notes |
|--------|-----------|-------|
| **ACH Direct Debit** | 0% | Recommended — lowest cost |
| **Credit/Debit Card** | 3% | Via Adyen (Visa/MC/Amex + Apple Pay + Google Pay) |
| **Klarna (Pay in 4)** | 5% | Via Adyen — installment payments |
| **Cash App Pay** | 3% | Via Adyen |
| **Amazon Pay** | 3% | Via Adyen |
| **Crypto (BTC/ETH/USDC/USDT/MATIC/TRX/BNB/LTC/DOGE)** | 0% | Via SHKeeper (pay.performancewest.net) |

Surcharges are displayed at checkout before payment confirmation. Stored in `payment_surcharges` table. Surcharge injection is handled by the `performancewest_erpnext` Frappe app hook.

**SHKeeper** runs as a self-hosted instance in k3s (Kubernetes) at `pay.performancewest.net` for crypto payments. Supports any ERC-20/TRC-20/BEP-20 token. Webhook callbacks confirm payment and trigger formation workflow via ERPNext.

## Sales Agent Commission System

Sales agents refer clients using a unique referral code (format: `REF-XXXXX`).

### How It Works

1. Agent shares their referral link: `performancewest.net/form?ref=REF-XXXXX`
2. Client receives a **5% discount** on service fees
3. Agent earns a **flat commission per service** (not percentage-based)

### Commission Schedule

| Service | Agent Commission |
|---------|-----------------|
| Canada CRTC Package | $300 |
| Basic Formation | $50 |
| Bundle Purchase | $100 |

Commissions are tracked in ERPNext Commission Ledger DocType (with PostgreSQL backup in `commission_ledger`). Paid 14 days after order delivery via Relay ACH.

## Discount Code System

Discount codes can be created in ERPNext and applied at checkout:

- **Percentage-based** (e.g., 10% off)
- **Fixed amount** (e.g., $25 off)
- **Usage limits** (single-use, multi-use, or unlimited)
- **Expiration dates**
- Discount codes stack with agent referral discounts (capped at 15% total)
- Discounts apply to **service fees only** — never to state filing fees, government fees, or expedited fees

## Employment Pages

Employment/careers pages exist in the codebase but are **hidden in production** (not linked from navigation, not indexed by search engines). Gated behind `import.meta.env.DEV` flag. Reserved for future use.
