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. An assistant connected by signing in (below) works on any plan. 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. The label and the limit can be changed later, and each key shows its calls today and over the last 30 days, split by scope. A team admin issues keys for the team from the same tab and sees the same figures for every team key.
| 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 | AI design review and 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 or any MCP client that signs you in, 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 on Pro and Team; 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. Any plan can connect; on Free the assistant gets 10 solves a month and the Free limits, and the Pro-only tools say so.
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 and its own call counts, 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, worst), or three input points at once with options.sweep: true: every battery empty, half and full, every supply across its voltage tolerance. Synchronous. |
POST /v1/designs/monte-carlo | solve | Monte Carlo (options.trials, options.seed). Metered. Trials are clamped to the plan cap and to what one request can finish; meta.clamped and meta.clampedBy say so. A big design clamps hard — use /v1/jobs/monte-carlo for the full run. |
POST /v1/designs/derating | solve | Derating analysis (standard: NASA_GSFC or IPC_9592, required). One nominal solve, then every rated part is graded against the standard's table — actual, rated, derated limit, stress and a GREEN / ORANGE / RED status per parameter, with mitigation text. The same evaluator as the editor's Derating panel. Unmetered. |
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, solve_json (the solved result at options.corner), report_json (summary and grouped warnings with their fix patches at all three corners, plus the BOM). Returned inline. Every export carries attribution — the solver release, the design fingerprint, the date and a verify URL — and the CSV ends with that line. |
POST /v1/designs/verify | read | Re-solve a design and check the fingerprint printed on an export: body { design, hash }, answer matches plus the summary numbers. Unmetered. The keyless twin is the page at /verify/{hash}. |
POST /v1/designs/compare | solve | What changed between two designs and what it did. Each side is a design document, { designId } or { templateId }; the answer lists the document diff, the movement in loss, efficiency and warnings, every node whose operating point moved, and the warnings resolved and introduced. options.monteCarlo adds a yield comparison as two metered runs. |
POST /v1/ai/review | ai | AI design review of a design document (or a solve result): what the numbers mean in plain English, then the recommended changes. A document is solved by the review itself, at options.ambientTemperature, and every part a suggestion names is checked against its worst-case input (batteries at full charge, every tolerance corner); one rated below it moves to rejectedSuggestions. One AI call. At most 3 reviews run at once per account on the server that takes the request, the editor's AI reviews included; a 4th is 429 SOLVE_IN_PROGRESS with Retry-After: 30 and does not count against the hourly limit. |
POST /v1/ai/synthesize | ai | Generate a design from a plain-English prompt; returned already solved. At most 3 syntheses run at once per account on the server that takes the request, the editor's included; a 4th is 429 SOLVE_IN_PROGRESS with Retry-After: 30 and does not count against the hourly limit. |
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; 500 nodes per design in total, across all its sheets). |
| 429 | RATE_LIMITED | The key's per-minute allowance is used; Retry-After is set. |
| 429 | SOLVE_IN_PROGRESS | This account already has 3 calls of one kind running on the server that took the request: synchronous solves, AI reviews or AI syntheses, each counted separately. The Retry-After header gives the wait: 2 s for solves, 30 s for AI reviews and syntheses; for long solves, use a job. |
| 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
Monte Carlo jobs are live at /v1/jobs/* — see
the contract for POST /v1/jobs/monte-carlo,
GET and DELETE /v1/jobs/{id} and GET /v1/jobs. A job runs
in slices of a few seconds (3 s of trials; allow up to 5 s, since the first also checks the design), one inside the request that creates it and one inside each poll, so a
run far longer than a request timeout completes as you poll it; the answer at a given seed matches
POST /v1/designs/monte-carlo at that seed. An assistant on the MCP server has the same
thing as two tools: run_monte_carlo_job starts one and get_job polls it, each
poll running the next slice and reporting how many polls are likely left, measured from the slice that
just ran. Cancelling and listing stay REST-only (DELETE /v1/jobs/{id},
GET /v1/jobs). Derating is synchronous and live at POST /v1/designs/derating.
A distributor part search (/v1/parts/*) is not planned:
distributor data is licensed to us for our own site only, so it will not be served through this API or
the MCP server. The part library is the alternative — curated and
community-verified models that grow as datasheets are contributed. PDF exports (pdf,
mc_report, derating_report, dossier) are rendered in the editor.
Questions and bug reports: the contact page. The terms of service apply to API use as they do to the editor.