Quickstart
From zero to a sealed PDF in under 10 minutes.
1. Sign up
Create an account at /login. The first project is free under the residential ≤ 300 m² rule. Beyond that, the Pro plan starts a 14-day trial — no card required.
2. Pick a template (optional)
From the workflow page, choose residential-200a, commercial-office, industrial-derate, or evse-retrofit. The template seeds service, panels, and a starter load list.
3. Edit anywhere
Service voltage, panel layout, load list, conduit material — change anything. The verdict bar at the top updates instantly: total demand, calculated service amps, voltage drop, conduit fill, LPD verdict, free-tier status.
4. Resolve warnings
Each warning carries a code (e.g. VD_FEEDER_OVER), a list of affectedElementIds, and a suggested fix. Click through to jump to the offending element.
5. Stamp & export
Move the project from UNDER_REVIEW to STAMPED (requires a P.E. / P.Eng on the team). Export the report PDF + single-line PDF.
Core concepts
Five ideas you need to know before reading any other doc.
ProjectState
A single immutable JSON document describing the entire project: meta, service, panels, loads, feeders, equipment, options. Everything else is derived from it.
derive(state)
A pure function. Same state in → same outputs out. No hidden DB calls, no time-dependent behavior. This is what makes EEP auditable.
Rule pack
A code object (NEC_2023_PACK, CEC_2024_PACK) listing every rule the engine evaluates. Each rule has an ID, a citation, and a function.
Element graph
Every element (panel, load, feeder) has a stable ID. The graph tracks parents and dependents so deletion impact is always knowable.
Snapshot
A (projectId, version) tuple in the database holding the full state at that moment. Versions are immutable; restore creates a new version.
NEC / CEC rule packs
Both packs ship in every plan. Switch per project.
NEC 2023 (US)
Articles 210, 215, 220 (incl. T220.42 occupancy variants for hotel / school / hospital), 230, 240, 250, 310 (T310.16 ampacity, derating for ambient + bundling), 408, 430 (motors), 450 (transformers), 625 (EVSE), Chapter 9 T1 (conduit fill).
CEC 2024 (Canada)
Sections 4 (conductors + T2/T4), 6 (services), 8 (loads + 8-200 calculation, 8-202 multi-dwelling, 8-210 commercial), 10 (grounding), 14 (overcurrent), 26 (transformers), 28 (motors), 86 (EVSE).
Provincial flags
CEC mode applies provincial deviations where they exist (Ontario 26-700 service-disconnect amendments, BC 4-006 conductor fill, Quebec rule numbering). Each fired rule includes a citation.
derive(state) — engine surface
The single function the entire app reads from.
import { derive } from '@eep/engine'
const result = derive(projectState)
result.totalConnectedKva // raw connected load
result.demandKva // post-demand-factor
result.calculatedAmps // service ampacity needed
result.feeders // [{ id, conduitSize, conduitFillPct, vdPct, ... }]
result.panelSchedule // [{ panelId, totalConnectedKva, legBalance, ... }]
result.warnings // [{ code, severity, message, affectedElementIds }]
result.firedRuleIds // ['NEC-220.84', 'NEC-T220.42-hotel', ...]
result.freeTier // { eligible: boolean, reason?: string }
result.lpd // { pass, allowance, actual, ratio } | null
The function is exposed as a TypeScript module (@eep/engine) and over HTTP at POST /api/v1/engine/derive.
Warning codes
26 codes. Every code carries severity, message, and the element IDs it touches.
| Code | Severity | Trigger |
|---|---|---|
VD_BRANCH_OVER | error | Branch voltage drop > configured limit |
VD_FEEDER_OVER | error | Feeder voltage drop > configured limit |
AMPACITY_INSUFFICIENT | error | Conductor ampacity < calculated load after derate |
CONDUIT_OVERFILL | error | Conduit fill > 40 % (NEC) / 40 % (CEC) for 4+ wires |
PANEL_BALANCE_OFF | warn | 3φ panel leg imbalance > 10 % |
FREE_TIER_GATE | info | Project exceeds free-tier eligibility |
LPD_OVER | warn | Lighting power density > ASHRAE / NECB allowance |
NEUTRAL_NONLINEAR | warn | > 50 % non-linear load on 3φ feeder — neutral upsize |
ORPHAN_ELEMENT | warn | Element references a deleted parent |
| …and 17 more. See /element-graph/warnings.ts for the full list. | ||
Calc extensions
Targeted modules called from derive() for non-trivial sub-calculations.
conduit-fill.ts
NEC Chapter 9 Table 1. pickConduit(awgList, type) returns trade size + fill %, or flags parallel-runs needed.
lpd.ts
ASHRAE 90.1 / NECB 2017 lighting power density allowances by occupancy. checkLpd(occupancy, m², actualW/m²).
free-tier.ts
Business rule: residential dwellings ≤ 300 m² are free forever; everything else requires a paid plan.
equipment-library.ts
40+ real manufacturer items (Square D, Eaton, Siemens, ABB, Schneider). Filter by manufacturer, kind, amps.
csv-import.ts
CSV / TSV importer with header aliasing and per-row error reporting.
Projects & versions
Optimistic concurrency on (projectId, version). No lost writes, no overwritten teammate edits.
Save
Every save creates a snapshot row. The version column is unique per project; concurrent saves with the same version receive 409 Conflict and must rebase.
Restore
Restoring an older version creates a new version with that content — the older row stays untouched. History is append-only.
Branch
POST /projects/:id/branch?fromVersion=N creates a new project rooted at version N. Use it to explore an alternative without polluting the live design.
Templates
Four built-ins. Point of departure, not point of arrival.
residential-200a— single-family, 200 A service, NEC 2023commercial-office— 480/277 V, panel + lighting + receptacle distributionindustrial-derate— motor-heavy, ambient + bundling derate scenariosevse-retrofit— adding 1–6 EVSE to existing residential service
Status & sealing
DRAFT → UNDER_REVIEW → STAMPED → ARCHIVED.
The status transition UNDER_REVIEW → STAMPED requires a team member with the SEAL role. The actor's name + license number are written into the audit log permanently. STAMPED versions are content-locked; further edits create a new branch.
Auth
Bearer JWT. Tokens issued at POST /auth/login, refreshed at POST /auth/refresh.
curl -X POST https://api.ee-platform.com/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"[email protected]","password":"…"}'
# response: { "accessToken": "eyJ…", "refreshToken": "eyJ…" }
Enterprise plans support SAML / OIDC SSO with custom IdP. Contact [email protected].
REST endpoints (selected)
~25 endpoints total. The most-used are listed here; the rest live behind your dashboard's API explorer.
| Verb | Path | Purpose |
|---|---|---|
| POST | /projects | Create project (optionally from template) |
| GET | /projects | List your projects |
| GET | /projects/:id | Latest version of a project |
| POST | /projects/:id/snapshots | Save a new version (optimistic on version) |
| POST | /projects/:id/restore | Restore an older version as a new version |
| POST | /projects/:id/branch | Fork into a new project |
| PATCH | /projects/:id/status | Move DRAFT → UNDER_REVIEW → STAMPED |
| POST | /projects/:id/elements/:eid/force-delete | Delete element with reason + impact log |
| GET | /projects/:id/changes?since=N | Poll for teammate edits |
| GET | /projects/:id/audit | Immutable audit log |
| POST | /engine/derive | Run derive() on a state without persisting |
| POST | /loads/import | CSV / TSV bulk import |
| GET | /equipment | Search the equipment library |
| POST | /public/contact | Public contact form (no auth) |
Webhooks
Pro plan and above. POST your endpoint when events fire.
Configure at /settings/webhooks. Events: project.saved, project.stamped, project.warning_added, project.element_deleted. Payload includes project ID, version, actor email, and a HMAC-SHA256 signature in the X-EEP-Signature header.
Deployment
Hosted SaaS or self-host on Railway + Supabase + Cloudflare Pages. (Enterprise.)
Hosted
Default. Backend on Railway, database on Supabase (Postgres 15), static frontend on Cloudflare Pages. SOC 2 Type I in progress; backups every 6 h, point-in-time recovery to 7 days.
Self-host (Enterprise)
Provided as a Docker Compose bundle: backend (NestJS), Postgres 15, Redis (optional, for Bull queues). Migration: pnpm prisma migrate deploy. See RUNBOOK.md in your customer share.
Runbook (operations)
For DevOps. The bare minimum to keep EEP healthy.
Health
GET /healthz returns 200 ok if the API is up; GET /readyz additionally pings the DB. Wire both into your uptime monitor.
Migrations
Always backwards-compatible inside a single major. Run pnpm prisma migrate deploy on release; never migrate reset against production.
Backups
Hosted: daily logical (pg_dump) + WAL streaming. Self-host: configure your own; do not skip this.
Incident
Status page: status.ee-platform.com. Sev-1 hotline (Enterprise): in your contract.
Security
Light on theatre, heavy on the basics.
- TLS 1.2+ everywhere; HSTS on the marketing + app domains
- Argon2id password hashing; 30-min access JWTs, 14-day refresh
- Rate limit on
/auth/*and/public/*viahelmet+ custom guard - Tenant isolation on every Prisma query (global guard)
- SOC 2 Type I in progress; full report at /security
- Disclosure: [email protected]