API reference
Validate, solve, store, share and export power-tree designs from a script or an AI agent. Same solver, same designs, same quotas as the editor.
The API is the agent door to Electronics Architect. Everything it does, the editor can do too, and
everything it makes lands where a person can review it: a design saved through the API appears in
My Designs (or Team Designs) and opens on the canvas. The machine-readable contract is at
/v1/openapi.json (OpenAPI 3.1).
1. Get a key
Open the editor, click your account, and use Developer — API keys. Keys are available on Pro and Team plans. Each key has a label, a set of scopes and a per-minute rate limit; the secret is shown once, when it is created, and only its hash is stored. Revoking a key stops it immediately.
| Scope | Allows | Default |
|---|---|---|
read | Read designs, validate, see usage | on |
write | Create, update, delete and share designs | on |
solve | Solve and Monte Carlo | on |
export | Bill of materials and design JSON | on |
parts | Part search (reserved; the endpoint is not live yet) | off |
ai | Explain, suggest, synthesize — uses the account's AI quota | off |
Send the key on every call: Authorization: Bearer ea_live_…. A Team admin can issue keys
that act for the team; their designs land in Team Designs and their revisions are attributed to the key.
1b. Or connect an AI assistant by address (MCP)
If you work in Claude Desktop, Claude Code, Cursor, ChatGPT or any client that speaks the Model Context Protocol, you do not need a key or a script. Add the server by address:
https://electronics-architect.com/mcp
The client sends you to a sign-in page once (OAuth 2.1); you see what the app is asking for and tick the permissions you want it to have. That creates an ordinary API key labelled with the app's name under Account → Developer, and revoking it there disconnects the app at once. From then on the assistant has the same tools as the REST API below — list the shipped templates, read the schema, validate and solve a design, run a Monte Carlo, save, share and export — on the same solver and the same monthly quotas as the editor. Solving costs nothing; a Monte Carlo run counts as one, as it does from the editor. Everything it saves appears in My Designs for a person to open and approve. Pro and Team plans.
2. The contract in five points
- Designs are schema v1 documents. The schema is generated from the same attribute
registry the editor uses and published at
/schema/design-v1.json. Numbers are SI base units with no prefix, as JSON numbers:0.0006for 600 µA,47e-6for 47 µF. A document that does not validate is refused before the solver sees it. - Solving is unmetered. Only the key's rate limit applies (60 requests per minute by default).
- Monte Carlo and AI draw on the monthly quotas shown in Account. When one is used up the
call returns
402 QUOTA_EXHAUSTEDwithresetsAt. - Errors are always the same shape:
{ "error": { "code", "message", "field"? } }. Branch oncode; it is stable. The message is for people. - Warnings carry codes and remedies. Every solver warning has
code,level,label,messageandremedy; the sheet'ssummary.warningGroupscollapses repeats.
3. Endpoints
Base URL https://electronics-architect.com. Request and response bodies are JSON.
| Call | Scope | What it does |
|---|---|---|
GET /v1/me | any | The key, its scopes, the account tier, and this month's usage with the reset date. |
POST /v1/designs/validate | read | Schema and structure check. Always 200; read valid and errors. |
POST /v1/designs/solve | solve | Solve one tolerance corner (options.corner: nom, min, max). Synchronous. |
POST /v1/designs/monte-carlo | solve | Monte Carlo (options.trials, options.seed). Metered. Trials are clamped to the plan cap and a CPU budget; meta.clamped says so. |
GET /v1/designs | read | List the designs visible to the key. |
POST /v1/designs | write | Create a cloud design (name, design). Send Idempotency-Key to make retries safe. |
GET /v1/designs/{id} | read | Read a design. |
PUT /v1/designs/{id} | write | Replace a design. The previous state is kept in the revision history, authored "via key <label>". |
DELETE /v1/designs/{id} | write | Delete a design. |
POST /v1/designs/{id}/share | write | Create or update the public share link (permissions, expiresInDays 1 / 7 / 30 / 90). |
POST /v1/designs/{id}/exports | export | type: bom_json, bom_csv, design_json. Returned inline. |
POST /v1/ai/explain | ai | Plain-English explanation of a design or a solve result. |
POST /v1/ai/suggest | ai | Architectural suggestions. |
POST /v1/ai/synthesize | ai | Generate a design from a plain-English prompt; returned already solved. |
GET /v1/templates, /v1/templates/{id} | read | The design templates that ship with the editor, as a list and as solvable documents. |
GET /v1/library/parts, /v1/library/parts/{mpn} | read | The part library: parametric simulation models for real parts (?type=LDO, SMPS…), each with a properties block ready to paste into a node, provenance per value and filledFromDefaults. It grows as datasheet contributions are verified and merged. Curated, datasheet-extracted and community-verified values only; never DigiKey data, never pricing or stock. |
GET /v1/openapi.json | none | This contract, machine-readable. |
4. A full flow: create, solve, Monte Carlo, export
The same four calls in three languages. Replace ea_live_… with your key. The design is the
smallest thing the schema accepts: a 12 V source, an LDO, a 100 mA load.
KEY="ea_live_…"
API="https://electronics-architect.com"
cat > design.json <<'EOF'
{
"schemaVersion": 1, "version": "3.0",
"sheets": [{
"id": "sheet-1", "name": "12 V to 5 V",
"nodes": [
{ "id": "src", "type": "Source", "name": "V1", "x": 0, "y": 0,
"properties": { "voltage": 12, "currentLimit": 2, "internalR": 0.05 }, "portDirections": { "right": "out" } },
{ "id": "ldo", "type": "LDO", "name": "U1", "x": 240, "y": 0,
"properties": { "vinMin": 6, "outputVoltage": 5, "dropout": 0.5, "iq": 0.0001, "vinMax": 20,
"iMax": 1, "pdMax": 1.5, "thetaJA": 60, "tMax": 125 },
"portDirections": { "left": "in", "right": "out" } },
{ "id": "load", "type": "Load", "name": "L1", "x": 480, "y": 0,
"properties": { "current": 0.1 }, "portDirections": { "left": "in" } }
],
"connections": [
{ "from": { "nodeId": "src", "side": "right" }, "to": { "nodeId": "ldo", "side": "left" } },
{ "from": { "nodeId": "ldo", "side": "right" }, "to": { "nodeId": "load", "side": "left" } }
]
}]
}
EOF
# 1. Create the cloud design (retry-safe with an Idempotency-Key)
ID=$(jq -n --slurpfile d design.json '{name: "Agent LDO", design: $d[0]}' \
| curl -s -X POST "$API/v1/designs" -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -H "Idempotency-Key: run-001" -d @- | jq -r .id)
# 2. Solve the nominal corner
jq -n --slurpfile d design.json '{design: $d[0], options: {corner: "nom"}}' \
| curl -s -X POST "$API/v1/designs/solve" -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d @- \
| jq '.result.sheets[0] | {converged, warnings: [.warnings[] | {code, level, remedy}]}'
# 3. Monte Carlo, seeded so it can be reproduced (metered)
jq -n --slurpfile d design.json '{design: $d[0], options: {trials: 500, seed: 42}}' \
| curl -s -X POST "$API/v1/designs/monte-carlo" -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d @- | jq '.meta'
# 4. Export the bill of materials as CSV
curl -s -X POST "$API/v1/designs/$ID/exports" -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d '{"type":"bom_csv"}' | jq -r .content
import json, requests
API = "https://electronics-architect.com"
H = {"Authorization": "Bearer ea_live_…"}
with open("design.json") as f: # the document from the curl tab
design = json.load(f)
def call(method, path, **kw):
r = requests.request(method, API + path, headers=H, **kw)
body = r.json()
if not r.ok:
err = body["error"]
raise RuntimeError(f'{r.status_code} {err["code"]}: {err["message"]}')
return body
# 1. Create
created = call("POST", "/v1/designs",
json={"name": "Agent LDO", "design": design},
headers={**H, "Idempotency-Key": "run-001"})
design_id = created["id"]
# 2. Solve
solved = call("POST", "/v1/designs/solve", json={"design": design, "options": {"corner": "nom"}})
sheet = solved["result"]["sheets"][0]
print("converged:", sheet["converged"])
for w in sheet["warnings"]:
print(f' [{w["level"]}] {w["code"]}: {w["remedy"]}')
# 3. Monte Carlo (metered; 402 QUOTA_EXHAUSTED when the month's runs are used up)
mc = call("POST", "/v1/designs/monte-carlo", json={"design": design, "options": {"trials": 500, "seed": 42}})
print("trials:", mc["meta"]["trials"], "seed:", mc["meta"]["seed"])
# 4. Export the BOM
bom = call("POST", f"/v1/designs/{design_id}/exports", json={"type": "bom_csv"})
open(bom["filename"], "w", newline="").write(bom["content"])
import { readFile, writeFile } from "node:fs/promises";
const API = "https://electronics-architect.com";
const KEY = "ea_live_…";
async function call<T>(method: string, path: string, body?: unknown, extra: Record<string, string> = {}): Promise<T> {
const res = await fetch(API + path, {
method,
headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json", ...extra },
body: body === undefined ? undefined : JSON.stringify(body),
});
const json = await res.json();
if (!res.ok) throw new Error(`${res.status} ${json.error.code}: ${json.error.message}`);
return json as T;
}
const design = JSON.parse(await readFile("design.json", "utf8")); // the document from the curl tab
// 1. Create
const { id } = await call<{ id: string }>("POST", "/v1/designs",
{ name: "Agent LDO", design }, { "Idempotency-Key": "run-001" });
// 2. Solve
const solved = await call<{ result: { sheets: Array<{ converged: boolean; warnings: Array<{ code: string; level: string; remedy?: string }> }> } }>(
"POST", "/v1/designs/solve", { design, options: { corner: "nom" } });
console.log("converged:", solved.result.sheets[0].converged);
for (const w of solved.result.sheets[0].warnings) console.log(` [${w.level}] ${w.code}: ${w.remedy ?? ""}`);
// 3. Monte Carlo
const mc = await call<{ meta: { trials: number; seed: number } }>(
"POST", "/v1/designs/monte-carlo", { design, options: { trials: 500, seed: 42 } });
console.log("trials:", mc.meta.trials, "seed:", mc.meta.seed);
// 4. Export
const bom = await call<{ filename: string; content: string }>("POST", `/v1/designs/${id}/exports`, { type: "bom_csv" });
await writeFile(bom.filename, bom.content);
5. Error codes
| Status | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_FAILED | The design does not match schema v1. errors[] lists each path; field is the first. |
| 400 | INVALID_REQUEST | A malformed option or missing field; field names it. |
| 401 | UNAUTHENTICATED, INVALID_API_KEY, KEY_REVOKED | No key, an unknown key, or a revoked key. |
| 402 | QUOTA_EXHAUSTED | The monthly counter for feature is full; resetsAt says when it rolls over. |
| 403 | INSUFFICIENT_SCOPE, PLAN_REQUIRED | The key lacks a scope (requiredScopes), or the plan behind it no longer allows keys. |
| 404 | NOT_FOUND | No such design visible to this key, or no such route. |
| 413 | PAYLOAD_TOO_LARGE | Over a payload cap (500 KB per design; 2000 nodes per solve). |
| 429 | RATE_LIMITED | The key's per-minute allowance is used; Retry-After is set. |
| 500 | SOLVE_FAILED | The solver refused the circuit; the message says why. |
| 501 | EXPORT_TYPE_UNAVAILABLE | A PDF export type; those are rendered in the editor. |
6. Not in this version
Asynchronous jobs (/v1/jobs/*, including derating against NASA GSFC and IPC 9592) and
part search (/v1/parts/*) are planned and not yet live. PDF exports (pdf,
mc_report, derating_report, dossier) are rendered in the editor. A hosted
MCP server for Claude, Cursor and similar tools follows this API.
Questions and bug reports: the contact page. The terms of service apply to API use as they do to the editor.