Import a single-line diagram with an agent
Use the Single-Line Import skill in the EKX plugin when your agent needs to turn an existing drawing into an editable EKX project. Invoke it explicitly as $ekx-single-line-import, or ask the agent to create the attached SLD in EKX. The agent supplies electrical meaning; EKX deterministically owns normalization, catalog matching, rendered connections, spacing, and collision checks.
EKX does not receive, read, retain, or verify the source PDF in this workflow. It stores only the semantic proposal and source references submitted by your agent.
Prerequisites
- When using the EKX plugin in Codex, connect your EKX account when Codex prompts you. The plugin
requests permission to read projects and create the imported project in your selected EKX workspace.
- For another agent harness, create an API key with
projects:write. Addprojects:readwhen the
agent must retrieve the current build. Keep the key in the harness's secret store and never put it in prompts or logs.
- For an existing project, the connected user or API-key owner must be an owner or editor.
The connected user must still have project-creation or target-project permission when a proposal becomes ready. EKX applies that ready proposal in the same request.
Preflight the source before proposal submission
The client agent should resolve questions that the source drawing and this guide already expose before it calls the Single-Line Import API.
- Inventory the components, supported electrical relationships, ratings, names, and source
references shown on the drawing.
- Inventory every question the source reveals, then ask the engineer once with the complete list.
Include model scope and exclusions, code book, demolition conventions, system boundaries, every structural voltage, unsupported switching arrangements, cable sizes and cable type disposition, transformer construction, impedance, X/R, vector group, and load values. Do not ask one field at a time. Use the newest edition EKX supports for the code book, currently CEC 2024 and NEC 2026; do not offer an older edition.
- Derive a value only when the drawing or connected electrical context establishes it
unambiguously. Retain the source reference that supports the derivation.
- Normalize the engineer's answer even when it is unstructured. Mark source values with
origin: "source_drawing", engineer-supplied values with origin: "user_clarification", and explicitly approved assumptions with state: "assumed" plus their basis. Set remaining known study-data gaps to { "state": "unresolved" }. Do not omit them merely to have EKX ask whether they are unresolved.
- Do not choose coordinates, spacing, rendered handles, routes, or collision settings. EKX owns
those decisions.
- Use
PANEL <tag>for panel buses. For example, submitPANEL LPOfor "Existing Panel LPO"
and retain the full drawing text in the description or evidence. Name an untagged cable from its concise endpoint tags, such as CBL_LPO_P, instead of Cable 16.
One preflight question can cover every known gap:
> The drawing omits seven cable lengths, transformer impedance and X/R for T1 and T2, and three > load values. Should I leave these unresolved, use EKX typical transformer values, or do you have > values to provide? You can reply in any format. For cable lengths, you can provide individual > values or one assumption for all unknown cables.
The agent converts a free-form answer into field states. It does not require the engineer to format JSON or answer a sequence of one-field prompts.
Source preflight cannot know everything EKX knows. The Managed SLD compiler still checks current catalog matches, the existing project, protected construction rules, permissions, limits, and final layout. It returns any remaining decision as a build issue after proposal submission.
Follow the managed-build workflow
- Ask your client agent to interpret the source drawing and complete source preflight.
- Answer any structural, ambiguity, or system-boundary questions it finds.
- Create a draft with
POST /sld-builds. - Submit the complete proposal with
PUT /sld-builds/{buildId}/proposal. - Read the complete returned blocking issue list. If judgment is still required, present every
question together in one follow-up.
- Submit the resulting corrections together with
PATCH /sld-builds/{buildId}/proposal. - When no blocking issues remain, EKX applies the proposal atomically, verifies saved cable
selections, bus base voltages and transformer winding voltages against the compiled intent, and returns projectUrl. A mismatch rolls back the transaction instead of producing an applied build.
- Read
appliedSummary.appliedVerification, then open the project. Summarize the verified cable
dispositions and any nonblocking engineering limitations to the engineer.
The EKX Codex plugin performs steps 3 and 4 with create_single_line, submits step 6 with revise_single_line, and retrieves current state with get_single_line_import. These MCP tools use the same REST workflow documented below. Direct integrations can continue to call the endpoints.
Applied builds remain immutable audit records. To correct an applied project, create a new build with an existing_project target after the engineer authorizes the merge. The new build records the correction and preserves the original build history.
Every mutation requires an Idempotency-Key. Generate a unique key for the intended mutation and reuse it only when retrying the exact same method, path, and body. EKX replays the original response for 30 days. Reusing a key for different input returns 409 IDEMPOTENCY_CONFLICT.
Create a draft
Create a new-project draft:
curl "https://joinekx.com/api/v1/sld-builds" \
-X POST \
-H "Authorization: Bearer $EKX_API_KEY" \
-H "Idempotency-Key: 71e09248-e500-438e-9512-9671c582c68d" \
-H "Content-Type: application/json" \
--data '{"target":{"kind":"new_project"}}'Create a merge draft by fixing the target project when the draft is created:
{
"target": {
"kind": "existing_project",
"projectId": "7e9d24a7-488d-4201-a729-7464aa76f9e8"
}
}A successful create returns 201 without creating or changing a project:
{
"data": {
"version": 1,
"buildId": "eb0d4107-e333-4d42-b493-8b9473a6e9fb",
"targetKind": "new_project",
"targetProjectId": null,
"status": "created",
"currentVersion": 0,
"expiresAt": "2026-10-01T12:00:00.000Z",
"projectPath": null,
"projectUrl": null,
"allowedActions": ["get", "submit_proposal", "cancel"]
},
"meta": { "timestamp": "2026-09-01T12:00:00.000Z", "version": "1" }
}The draft remains in the personal or organization workspace recorded at creation. EKX does not move it to another workspace during application. Losing access to that organization blocks application without falling back to a personal workspace.
Submit a complete proposal
PUT /sld-builds/{buildId}/proposal accepts the complete semantic graph. expectedVersion is 0 for the first submission and the latest currentVersion for later replacements.
{
"expectedVersion": 0,
"proposal": {
"version": 1,
"source": {
"identity": "Drawing E-01",
"evidence": [
{
"ref": "service-riser",
"page": 2,
"locator": "Power distribution single-line",
"explanation": "Agent-supplied interpretation of the service and main bus"
}
]
},
"project": {
"kind": "new_project",
"name": "Plant distribution",
"codeBook": "NEC",
"codeEditionYear": 2026
},
"components": [
{
"ref": "utility",
"componentType": "utility_feed",
"name": "UTILITY",
"properties": {
"voltageKv": { "state": "supplied", "value": 13.8 },
"inputMethod": { "state": "supplied", "value": "mva" }
},
"branchOrder": 0,
"evidenceRefs": ["service-riser"]
},
{
"ref": "service-bus",
"componentType": "bus",
"name": "SERVICE BUS",
"properties": {
"baseKv": { "state": "supplied", "value": 13.8 },
"ratedAmperage": { "state": "unresolved" },
"bracingRatingKa": { "state": "unresolved" }
},
"layoutParentRef": "utility",
"branchOrder": 0,
"evidenceRefs": ["service-riser"]
}
],
"relationships": [
{
"ref": "utility-to-service",
"kind": "electrical",
"from": { "componentRef": "utility", "terminal": "source" },
"to": { "componentRef": "service-bus", "terminal": "bus" },
"evidenceRefs": ["service-riser"]
}
],
"removals": [],
"resolutions": []
}
}Do not submit pixel coordinates, spacing, routes, rendered handle IDs, or collision settings. EKX rejects protected construction fields. Use layoutParentRef and branchOrder to describe source hierarchy and ordering.
For a merge, set project.kind to existing_project. Use existingComponentId only when the agent knows the exact existing component. Omitting an existing record does not delete it. A removal must use the explicit reviewed-removal contract in the proposal reference.
Resolve issues through the agent
A blocked result includes stable issue keys, affected references, allowed responses, and whether each issue prevents readiness:
{
"key": "sld-issue:v1:2a7f...",
"code": "MISSING_ENGINEERING_INPUT",
"class": "required_decision",
"severity": "error",
"affectedRefs": ["utility"],
"field": "shortCircuitMva",
"message": "shortCircuitMva was omitted.",
"allowedResponses": ["mark_unresolved"],
"blocksReady": true
}This agent conversation is the review step. The agent retrieves the latest build, asks the engineer only when needed, presents all current questions in one message, then submits a bounded revision against the current version:
{
"version": 1,
"expectedVersion": 1,
"operations": [
{
"operation": "upsert_resolution",
"resolution": {
"type": "mark_unresolved",
"issueKey": "sld-issue:v1:2a7f...",
"componentRef": "utility",
"field": "shortCircuitMva"
}
}
]
}Agent-loop pseudocode:
read the source drawing and run the Single-Line Import source preflight
inventory every source-visible question before contacting the user
ask one consolidated question covering all structural gaps, ambiguity, system-boundary choices, and study inputs
normalize the user's free-form answer with source, clarification, assumed, or unresolved origin
create draft with a new idempotency key
submit the complete proposal with expectedVersion 0
while status is blocked:
inspect only issues where blocksReady is true
collect every current issue before asking the user
ask one consolidated follow-up or recheck the local source when judgment is required
submit all supported revision operations together against currentVersion
if EKX reports SLD_BUILD_STALE_VERSION, GET the draft and reconcile
when status is applied, give projectUrl to the engineer
summarize compiledResult.limitations without treating them as construction blockersEKX does not call back into the harness. The harness reads the mutation response or uses GET /sld-builds/{buildId} to continue.
Read, revise, or cancel a build
| Method and path | Scope | Purpose |
|---|---|---|
GET /sld-builds/{buildId} | projects:read | Read the current permitted draft status and result. |
PUT /sld-builds/{buildId}/proposal | projects:write | Replace the complete proposal at an expected version. |
PATCH /sld-builds/{buildId}/proposal | projects:write | Apply 1–100 bounded revision operations. |
DELETE /sld-builds/{buildId} | projects:write | Cancel an unapplied build without changing its target project. |
The complete JSON body for each mutation is limited to 2 MiB. A proposal supports at most 500 components, 1,000 relationships, and 100 source-evidence references.
If another agent has already changed the draft, EKX returns 409 SLD_BUILD_STALE_VERSION with the current and expected versions. Retrieve the draft, reconcile the intended correction, and retry with a new idempotency key and the current version.
Understand statuses and actions
| Status | Meaning | Available next step |
|---|---|---|
created | The draft exists but has no accepted proposal version. | Submit the complete proposal or cancel. |
blocked | The current compiled version has a blocking issue. | Continue in the originating agent, revise, or cancel. |
ready | The exact current version has no blocking issue and is being applied in the submission transaction. | Wait for that mutation response. |
applied | The resolved version changed one project atomically. | Open projectUrl or continue through the project API. |
canceled | An authorized caller canceled the unapplied draft. | Read the terminal explanation. |
expired | The draft had no activity for 30 days. | Read the terminal explanation and start a new build if needed. |
Canceled and expired proposal content is removed 30 days after the terminal transition. EKX keeps a minimal tombstone for authorized API reads. Applied history is bounded and follows normal project access, archive, export, support, restore, and deletion rules.
Apply safely
EKX applies only a proposal with no blocking issues. It repeats permission, workspace, policy, catalog, baseline, layout, and plan-limit checks after acquiring its locks. Compilation and project mutation share one transaction, so a failure leaves no partial project. Merge snapshots remain available to EKX's guarded restore service, but the managed-build API does not expose a restore operation.
Construction decisions, ambiguous matches, merge conflicts, and removals block application. The agent must present those issues to the engineer. Study inputs that are safe to leave unknown, such as cable lengths or fault values, remain null and appear in compiledResult.limitations after project creation. A new project created with autoSizingMode apply, the default, is then sized before the response returns, so conductor sizes and ratings that sizing fills are not limitations; see the proposal reference.
Log only safe operational data
An agent may log HTTP status, stable error code, build ID, proposal version, policy version, counts, issue count, blocking-issue count, layout outcome, duration, and result. Do not log the API key, Idempotency-Key, complete proposal, evidence text, source PDF, raw catalog rows, project snapshot, or exception body.