API reference
Programmatic access to projects, reports, and webhooks. Pro keys carry read scope; Team keys carry read + write. Generate keys at /dashboard/settings/api-keys.
Auth
Every request sends an Authorization: Bearer cp_live_<secret> header. Keys are SHA-256 hashed at rest; the plaintext shown at generation time is the only copy. Revoked keys are rejected immediately.
curl https://circuitproof.vercel.app/api/v1/projects \ -H "Authorization: Bearer cp_live_<your-secret>"
Endpoints
/api/v1publicDiscovery payload — endpoint list + auth format + tier matrix. No auth required.
Response
{
"object": "circuitproof_api",
"version": "1",
"endpoints": { ... },
"auth": "Bearer cp_live_<secret>",
"tiers": { "read": ["pro","team"], "write": ["team"] }
}/api/v1/projectsPro / Team (read)List the caller's projects. Paginated; archived projects excluded by default.
Query
?limit=50&offset=0&archived=0
Response
{
"object": "list",
"data": [
{
"id": "uuid",
"name": "Sprinter van 12V",
"jurisdiction": "NEC_690_706_710",
"archived": false,
"created_at": "2026-05-26T...",
"updated_at": "2026-05-28T..."
}
],
"has_more": false,
"total": 1
}/api/v1/projects/{id}Pro / Team (read)Single project + computed report (summary / BOM / runs / issues).
Response
{
"object": "project",
"id": "uuid",
"name": "...",
"jurisdiction": "NEC_690_706_710",
"codebase": "nec",
"canvas_state": { "nodes": [...], "edges": [...] },
"summary": { "systemVoltage": 12, "batteryWh": 2400, ... },
"bom": [{ "label": "Battery", "manufacturer": "Battle Born", "sku": "BB10012", "spec": "...", "qty": 2 }],
"runs": [{ "from": "Battery", "to": "Inverter", "length_ft": 5, "current_a": 180, "awg": "2", "vd": 1.62, ... }],
"issues": [{ "severity": "warning", "code": "ABYC E-11.4", "message": "..." }]
}/api/v1/projectsTeam (write)Create a project. Returns 201 + the new project record.
Request body
{
"name": "Off-grid cabin 48V",
"jurisdiction": "NEC_690_706_710",
"canvas_state": { "nodes": [], "edges": [] },
"notes": "Optional"
}/api/v1/projects/{id}Team (write)Partial update. Send only the fields you want to change.
Request body
{
"name": "Renamed",
"archived": true
}/api/v1/projects/{id}Team (write)Hard delete. Returns { object: 'deleted', id }.
Error codes
| HTTP | Body code | Meaning |
|---|---|---|
| 400 | invalid_json | Body wasn't valid JSON. |
| 400 | missing_name | POST /projects without a name. |
| 400 | no_fields_to_update | PATCH with an empty body. |
| 401 | unauthorized | Missing / malformed / revoked key. |
| 403 | tier_locked | Caller's tier doesn't include API access. |
| 403 | scope_required | Read-only key on a write endpoint. |
| 404 | not_found | Project ID unknown or owned by another user. |
| 500 | insert_failed / update_failed / delete_failed | DB error; the message field carries the underlying detail. |
Webhooks
Team-tier endpoints can receive HTTP POSTs on project + report + share + AI events. Manage at /dashboard/settings/webhooks.
Every delivery carries a X-CircuitProof-Signature: t=<unix>,v1=<hex> header. The v1 hex is HMAC-SHA256(secret, `$${t}.$${rawBody}`). Verify on every receipt:
// Node receiver pseudo-code
const sig = req.headers["x-circuitproof-signature"]; // "t=...,v1=..."
const [t, v1] = sig.split(",").map(p => p.split("=")[1]);
const expected = crypto.createHmac("sha256", SECRET)
.update(`${t}.${rawBody}`)
.digest("hex");
if (expected !== v1) return reject(); // signature mismatch
if (Date.now() / 1000 - Number(t) > 300) return reject(); // > 5 min old — replayEvent types currently shipped:
- project.created
- project.archived
- project.deleted
- report.pdf_generated
- report.dxf_generated
- share.link_created
- ai.summary_requested
- dfy.intake_submitted
Rate limits
Fair-use; no hard quota today. We log per-key request volume and will publish per-tier limits before surprising any caller. If you need higher sustained throughput than ~10 req/sec, reach the CircuitProof Team via any onboarding email and we'll raise it.
Stability
The /api/v1 surface is committed-stable: additive changes only, no breaking changes without a version bump. New endpoints will land at the same /api/v1 prefix; deprecations announced ≥90 days before removal.
Questions or missing capabilities? Reply to any onboarding email and the CircuitProof Team will get back to you. PRs against an OpenAPI spec (planned for v1.1) will be welcome on the public repo.