Run studies and apply sizing through the API
Use the engineering endpoints to run power flow, short-circuit, or arc flash studies, take the worst case across scenarios, read immutable run evidence, preview sizing, and apply current proposals.
Access requirements
| Operation | API scopes | Project role |
|---|---|---|
| Read study history and results | projects:read | Viewer or higher |
| Check current sizing selections | projects:read | Viewer or higher |
| Preview sizing | projects:read | Viewer or higher |
| Run a study | projects:read, studies:run | Editor, admin, or owner |
| Apply sizing | projects:read, sizing:apply | Editor, admin, or owner |
Study runs and sizing applies require a unique Idempotency-Key. Keep the same key and body when retrying after a timeout or an IDEMPOTENCY_IN_PROGRESS response.
EKX stores an answer for a key only when the request decides it: a completed run or apply, or a refusal caused by the request or the project data. A repeat with the same key and body replays that answer. A rate limit (429), a busy project or calculation service (503), changed inputs during a study run (409 with STUDY_INPUTS_CHANGED) and an unexpected server error (500) are not stored. They include Retry-After; repeat the same request with the same key after that delay. An archived project answers 404 like a missing one.
Run a power flow study
Send POST /projects/{projectId}/studies/power-flow with a body up to 16 KiB:
curl "https://joinekx.com/api/v1/projects/$PROJECT_ID/studies/power-flow" \
-X POST \
-H "Authorization: Bearer $EKX_API_KEY" \
-H "Idempotency-Key: $REQUEST_ID" \
-H "Content-Type: application/json" \
-d '{"load_profile":"normal","algorithm":"nr"}'| Field | Values | Default |
|---|---|---|
scenario_id | Scenario UUID or null | null |
load_profile | normal, peak, minimum, emergency | normal |
algorithm | nr, bfsw, gs | nr |
allow_incomplete_loads | true or false | false |
Do not send tolerance, max_iterations, or calculate_voltage_angles. They are not part of the API contract.
A completed study returns 201 with the study run and counts for its calculation and protection-operation rows. A nonconverged power flow returns 422 with POWER_FLOW_NOT_CONVERGED and stores no result rows for that run. A bus that the scenario's open devices, open wires or out-of-service components cut off from every source is a result, not a failure: its result rows read { "energization": "de_energized" } instead of values, and an arc flash row has status and reason de_energized. A bus that nothing in the diagram joins to a utility or generator, even with everything closed and in service, returns 422 with INVALID_STUDY_INPUT and a message naming it; connect or delete it, then run the study with a new key.
Set allow_incomplete_loads to true only when you intend to run a partial power flow. EKX models regular loads with missing or zero active power at 0 MW and omits motors that have no active power, horsepower, or full-load current. The completed study run stores a structured warning for each affected load in its parameters. This option does not change the saved project and does not bypass other missing engineering inputs.
Run a short-circuit study
Send POST /projects/{projectId}/studies/short-circuit:
{
"scenario_id": null,
"fault_type": "3ph",
"case": "max",
"include_detailed_results": true
}Add fault_bus_id to target one bus. Omit it to evaluate the supported network scope.
The pre-fault voltage is a project setting, not a body field. A completed run records the basis it used in parameters.preFaultVoltagePercent: a percent of nominal, or null for the IEC 60909 voltage factor. Arc flash runs record their own setting the same way. A fixed arc flash percent also sets the IEEE 1584 voltage at that percent of nominal; each arc flash result records the voltage it used in provenance.arcVoltageKv, while voltageKv stays the nominal voltage.
> Important: EKX's validated production workflow currently supports three-phase maximum-current analysis. Use fault_type: "3ph" and case: "max" for engineering decisions.
Utility fault cases
A short-circuit run evaluates every utility fault case included in short circuit (see utility fault cases). With several utilities, it evaluates every combination of their included cases, up to 16 calculations; a run that would need more is refused with 409 INCOMPLETE_ENGINEERING_INPUT naming each utility's count. A utility with no case included in short circuit is refused the same way: "No fault case of HYDRO is included in short circuit. Choose one on its Study tab." The case field is unrelated: it is the IEC calculation case and stays max.
Each result holds the highest value across the evaluated cases, each quantity on its own: a bus's ikss, ip and ith, and each current on a cable or transformer. When a run evaluated more than one case, the result names the case it came from:
results.utility_fault_caseslists{ utilityId, utilityName, caseId, caseName }for each utility with more than one included case that reaches the location.results.utility_fault_cases_by_quantityreplaces it when the quantities came from different cases, keyedikss,ip,ithfor buses and loads,i_from,i_tofor cables andi_hv,i_lvfor transformers. Two cases with different X/R can give one the highest bolted current and the other the highest peak.- Protection operation results carry
utilityFaultCases: they come from the case with the highest bolted current at their fault location. parameters.utilityFaultCaseson the run lists each calculation's selection, the source each utility was solved with, and its input hash. With several calculations,evaluatedInputHashis a hash of theirs.
A run of one case stores and returns exactly what it did before cases existed, with none of these fields.
Run an arc flash study
Send POST /projects/{projectId}/studies/arc-flash:
{
"scenario_id": null
}The scenario is the only field the body accepts. Every other input is a project or bus setting: the equipment class, electrode configuration, gap, working distance, and enclosure size live on the bus, and the two-second cap, the policy for buses with no upstream device, and the label settings live on the project.
The run performs its own short-circuit calculation and persists it, so one completed run holds the fault currents, the protection evidence, and the arc flash results from the same solve. A completed study returns 201 with counts for all three.
Each bus gets one row per run. A bus whose geometry is incomplete, whose inputs fall outside the IEEE 1584-2018 model, or whose clearing device has no usable clearing time keeps a row with a status and a reason instead of numbers. Results carry both the full and the variation-corrected case, the governing case, the clearing device and its governing function, both clearing times, and the source of each duration. A duration that did not come from the device, meaning an accepted duration entered on the bus or the two-second cap, is flagged on the result, the report, and the label. An accepted exposure assumption is a maximum for each case: a case the device clears sooner keeps the device time and the source device. reason is a stable code, such as no_upstream_device, no_device_model, no_resolved_characteristics, motor_contribution_unsupported or downstream_of_drive; the app shows each code as a plain sentence under one of six headings. no_device_model means the clearing device has no catalogue model, so it has no curve. A row with no_upstream_device or main_inside_enclosure may carry provenance.addDeviceAt, naming the connection (lineSideId/lineSideName to loadSideId/loadSideName) where a fuse or breaker would be credited.
Utility fault cases in arc flash
An arc flash run evaluates every utility fault case included in arc flash, combined across utilities as for short circuit (up to 16 calculations), and refuses the same way. Each bus keeps one row:
- When every case has a result, the row is the case with the highest
energyCalCm2, and its currents, clearing device, clearing times, durations and energy all come from that case.boundaryMmis the largest across all cases and both arcing currents;provenance.boundarynames its cases, which arcing current (fullormin) gave it, and that case's duration source. - When any case has no result, the row has no energy. It takes the first such case's
statusandreason, and names that case. When every case fails with the same status and reason, no case is named. utilityFaultCaseson the row lists{ utilityId, utilityName, caseId, caseName }for each utility with more than one included case that reaches the bus.provenance.utilityFaultCaseskeeps every case's status, reason, bolted and arcing currents, attributed device current, clearing times, duration sources, energy and boundary. An excluded-motor warning any case raised stays inprovenance.motorSizePolicy, with that case.
The run's short-circuit rows are chosen as a short-circuit run's are. A run of one case stores exactly what it did before, with utilityFaultCases null. Results from runs before pipeline arc-flash-pipeline-v7 keep their values.
The study is available on the Professional plan.
A study request is refused with 409 and the code INCOMPLETE_ENGINEERING_INPUT when an input it needs is blank. The error lists each component and field, such as a cable's length or conductor size, a transformer's impedance, or the utility fault level. No run is recorded. EKX does not substitute a typical cable impedance for a missing conductor size; run sizing or select a size.
A short-circuit or arc flash request for a project whose buses are all left out of that study is refused with 409 and the code NO_FAULTED_BUSES. No run is recorded. A bus is left out through its includeInShortCircuit and includeInArcFlash properties; arc flash counts only buses with both on.
Run a harmonic design study
Send POST /projects/{projectId}/studies/harmonics with an Idempotency-Key:
{
"scenario_id": null
}The body accepts only the scenario. Save source spectra and passive models on the equipment, and PCC boundaries, demand references, utility fault references, background spectrum and scan settings in the project's harmonicSettings before running. A new drive uses the documented six-pulse DC-choke typical; a new UPS uses the documented conventional six-pulse rectifier profile. Each installed profile retains its values and source revision.
The balanced three-phase study returns 201 and one calculation row. A limit exceedance is a completed result. An unavailable demand reference prevents the current comparison while leaving available voltage results intact. Unsupported equipment or missing physical data identifies the affected result; it never becomes zero distortion or a passing comparison.
Read the saved packet through the normal study-results endpoint. Its project-level calculation row has calculationType: "harmonics" and contains the frozen inputs, phasors, conservative PCC envelopes, driving-point scans, IEEE 519-2022 evaluation, engine/method identity and assumptions. Magnitude-only source contributions use the stated worst-case envelope. The additional harmonics object identifies the original scenario and whether the evidence is current, stale or historical. A deleted scenario's evidence remains historical and never becomes Base evidence.
The comparison is a design prediction, not measured IEEE 519 compliance. It uses the fixed declared maximum-demand current; changing operating load does not silently change that reference. A source-only supply equivalent or documented utility value supplies the short-circuit reference.
A completed key replay returns the stored response and creates no solve or quota charge. All internal solves and the scan share one admission. Engineering edits during execution return 409 with STUDY_INPUTS_CHANGED; an internal service outage returns 503. Loss of project access prevents publication, and a superseded worker cannot publish or fail another worker's run.
Run every scenario for the worst case
Send POST /projects/{projectId}/studies/worst-case with an empty body, {}. It runs a short-circuit study and an arc flash study in every scenario, Base first, each exactly as the single-scenario endpoints run them, and keeps the worst case at each bus:
- Duty: each bus's highest
ikss,ipandithacross the scenarios' short-circuit runs, each
naming the scenario it came from in scenarios and, when a utility has several fault cases, the case in utilityFaultCases.
- Arc flash: the scenario with the highest
energyCalCm2. Every value in the row, from the arcing
currents and clearing device to the duration and its flags, comes from that scenario and its governing utility fault case. boundaryMm is the largest across scenarios, and provenance.boundary names its scenario.
- A bus with no supply in a scenario is skipped for that scenario. A bus with no supply in every
scenario keeps status de_energized.
- A bus without a result in any other scenario has no energy. It takes the first such scenario's
status and reason, and provenance.scenario names that scenario.
Each run counts toward your study quota. When every run completes, the answer is 201 with worstCaseId and each scenario's short-circuit and arc flash outcome. When a scenario is refused or fails, for example on a voltage conflict that only that scenario has, the other scenarios still run and the answer is 422 with WORST_CASE_INCOMPLETE, the same outcomes in details, and no worst case. A rate limit, changed inputs during a run (409 STUDY_INPUTS_CHANGED) or a busy calculation service (503) stops the remaining scenarios and is not stored against the key; repeat the request with the same key after Retry-After.
Read the newest worst case with GET /projects/{projectId}/studies/worst-case (projects:read). It answers 404 with WORST_CASE_NOT_FOUND until the action has run.
| Field | Meaning |
|---|---|
id, runAt | The action that produced it and its newest run's time. |
complete | Every scenario has a completed short-circuit and arc flash run. When false, arcFlashResults and duty are empty and scenarios shows which run is missing or failed. |
stale, staleReasons | inputs_changed when any of its runs has invalidatedAt; scenarios_changed when scenarios were added or deleted since. |
scenarios | Each scenario's name at run time, with its short-circuit and arc flash run ids, status and invalidatedAt. |
arcFlashResults | One row per bus, shaped like a run's arc flash results. provenance.scenario names the governing scenario; provenance.scenarios keeps each scenario's status, reason, currents, clearing times, energy and boundary. |
duty | One row per faulted bus: ikssKa, ipKa, ithKa, the scenario for each in scenarios, and deEnergized. |
The runs it combined are ordinary study runs. Each carries parameters.scenarioEnvelope with the action's id and scenario list, and stays readable through the history endpoints.
Read study history and evidence
| Method and path | Result |
|---|---|
GET /projects/{projectId}/studies | Study runs, newest first. |
GET /projects/{projectId}/studies/{studyRunId} | One run and its status. |
GET /projects/{projectId}/studies/{studyRunId}/results | Run-owned calculation rows, protection-operation evidence, and arc flash results. |
Cable result rows include optional from_endpoint and to_endpoint objects, each with the cable's component_id, physical terminal_side and zero-based terminal_index. These identify which physical end owns each from/to power, voltage or current value; they do not imply a supply direction. New solves order a cable's top terminal before its bottom terminal, including an energized end with no downstream bus. Equivalent connection insertion orders retain that mapping. Project exports preserve the recorded identifiers. Historical rows without them have unrecorded ends; do not infer their identity from current direction, magnitude, drawing position or today's wires.
The history endpoint accepts type=power_flow, type=short_circuit, type=arc_flash, or type=harmonics. Use limit from 1 to 100 and a nonnegative offset for pagination.
Each run includes status, failureCode, errorMessage, evaluatedInputHash, runAt, completedAt, and invalidatedAt. A change to the engineering inputs that study consumes sets invalidatedAt; it does not erase the completed evidence. Check freshness before relying on an older result. Harmonic history also retains its immutable origin and deletedScenario flag.
Check current sizing selections
POST /projects/{projectId}/sizing/check performs a read-only check of the values currently installed in the project. It accepts the optional Base or scenario identifier:
{
"scenario_id": null
}The response is either an evaluated envelope with basisHash, codeProfile, tableBasisHash, and per-component evaluations, or an unavailable envelope with a stable code and a retry message. Each evaluation includes the installed selected snapshot, its sizingMode, a current requirement status, findings, and a separate recommendation. A recommendation may require dependent component changes; it does not make the installed selection pass.
The check includes Auto, Manual, and Locked selections and does not require the installed value to equal the recommendation. It performs no component, settings, sizing-result, or scenario writes. A scenario check evaluates effective scenario overrides while retaining the base selection and mode. An unavailable check never falls back to an older sizing result or reports a current pass.
Preview sizing
POST /projects/{projectId}/sizing/preview needs only projects:read. Send an empty object for the Base scenario or select another scenario:
{
"scenario_id": "..."
}The response contains proposals, protectionResolutions, voltageDropEvaluations, skipped items, errors, the scenario ID, and the code profile used to build the proposal set. Each proposal includes its current and proposed values plus the evidence needed to detect stale or incomplete applies. Evaluations remain present when missing inputs produce no conductor proposals.
For a selected-value review, add selected_value_targets:
{
"scenario_id": null,
"selected_value_targets": [
{
"componentId": "...",
"componentType": "cable",
"fields": ["conductorSize", "parallelRuns"]
}
]
}Each target names one component and one or more fields to review. For a valid target set in a project mode that permits preview, the response adds selectionEvaluations and selectionBasisHash, plus selectedValueAcceptance metadata. Selected-value acceptance is for the Base scenario and reviewed manual values; scenario overrides cannot be accepted as physical selections.
For CEC 2024, an evaluation reports whole circuit and total path limits, current provenance, cable contributions, bonding consequences and named blockers. Optional design targets cannot relax the mandatory limits. NEC evaluations remain design_only and do not establish special-case or grounding-conductor compliance.
Apply sizing proposals
POST /projects/{projectId}/sizing/apply accepts up to 2,000 proposal fields in a body up to 2 MiB. A request with no fields must explicitly save a passing check as described below. Apply requires projects:read, sizing:apply, and an editor or higher role.
Build each apply entry from the latest preview proposal:
- Copy
componentId,componentType,field, and all dependency, group, anchor, explanation, and basis metadata that are present. - Rename
proposedValuetovalue. - Rename
currentValuetoexpectedCurrentValue. - Copy
codeProfile.keytocodeProfileKey. - Copy
codeProfile.implementationRevisiontocodeProfileRevision. - Copy
tableBasisHashwithout changing it.
Send the entries in a proposals array:
{
"protectionIntent": { "kind": "accept_all" },
"proposals": [
{
"componentId": "...",
"componentType": "cable",
"field": "conductorSize",
"value": "1/0",
"expectedCurrentValue": "2",
"codeProfileKey": "NEC:2026",
"codeProfileRevision": "...",
"tableBasisHash": "..."
}
]
}protectionIntent is optional. When omitted, the API uses the conservative accept_all intent; it never accepts an actor identity or automatic-update preference from the request body. To apply one reviewed equipment choice, send the T1 explicit_device intent together with its proposal entry.
Do not construct proposal evidence yourself or reuse an old preview. EKX rebuilds the current sizing plan and may add required dependency or coordinated-group proposals from the saved topology. It applies the resolved set atomically and rejects stale or internally inconsistent requests. No proposal fields change when strict validation fails.
Copy every voltageDropGroup unchanged and submit its complete member set, including exact cableVariantId, bonding and dependent protection changes. CEC acceptance validates the complete group before writing; selecting only one cable or omitting a bonding change is rejected.
Save a passing check without equipment changes
For a passing CEC evaluation whose proposedValues is empty, send proposals: [] and a voltageDropChecks entry containing its groupId, basisHash, version: 1 and memberKeys: []. The server rebuilds the check and saves accepted evidence without rewriting component values. The same permissions, sizing-mode, Base-scenario, stale-input and idempotency rules apply.
GET /projects/{projectId}/sizing preserves the saved rows and adds voltageDropReview with accepted evidence, current, stale or historical freshness, and the latest failed attempt separately. Read and export do not recalculate or accept results. Old evidence without the new payload remains historical. The project export preserves accepted hashes and timestamps for support and review.
A successful response has an outcome of applied and may include changeSetId plus the resolved protectionOutcome. If the equipment fields commit but the follow-up evidence refresh needs a retry, the outcome is applied_with_warnings and the response includes Retry-After. Repeat the exact request with the same Idempotency-Key; EKX resumes the refresh without applying the equipment fields again.