API Reference
Complete reference for the CircuitHub Platform REST API.
Base URL: https://api.circuithub.com/v1
All endpoints require authentication via Bearer token unless otherwise noted.
curl -H "Authorization: Bearer $CIRCUITHUB_API_KEY" https://api.circuithub.com/v1/...Health
GET /health
Check API health. No authentication required.
// Response: 200 OK
{ "status": "ok" }Authentication
GET /auth/cli
Initiate CLI login flow. Redirects to the callback URL with a JWT token.
| Parameter | In | Type | Description |
|---|---|---|---|
redirect_uri | query | string | Required. Must be http://localhost:* or http://127.0.0.1:* |
Response: 302 Found — redirects to {redirect_uri}?token={JWT}
The JWT is valid for 24 hours.
This endpoint is used internally by
circuithub auth login. You don't need to call it directly.
Workspaces
GET /workspaces
List workspaces the authenticated user belongs to.
Response: 200 OK
{
"workspaces": [
{
"id": 1,
"slug": "acme-electronics",
"name": "Acme Electronics"
}
]
}| Field | Type | Description |
|---|---|---|
id | integer | Workspace ID |
slug | string | URL-safe identifier |
name | string | Display name |
Projects
GET /projects
List projects accessible to the authenticated user.
| Parameter | In | Type | Description |
|---|---|---|---|
workspace | query | string | Required. Workspace slug |
Response: 200 OK
{
"projects": [
{
"id": 12345,
"name": "power-supply-board",
"description": "24V to 5V buck converter",
"createdAt": "2025-01-10T08:00:00Z",
"updatedAt": "2025-03-01T14:30:00Z",
"exportClassification": "EAR99",
"latestRevisionId": 99001,
"latestRevisionNumber": 3
}
]
}GET /projects/:projectId
Get a single project by ID.
Response: 200 OK
{
"id": 12345,
"name": "power-supply-board",
"description": "24V to 5V buck converter",
"createdAt": "2025-01-10T08:00:00Z",
"updatedAt": "2025-03-01T14:30:00Z",
"exportClassification": "EAR99",
"latestRevisionId": 99001,
"latestRevisionNumber": 3
}POST /projects/upload-urls
Request presigned S3 upload URLs. Call this before creating a project or revision. You can request URLs for multiple files at once — for example, a design file and a BOM CSV.
Request body:
{
"workspace": "acme-electronics",
"files": [
{ "filename": "my-board.cvg", "size": 48210 },
{ "filename": "bom.csv", "size": 1024 }
]
}| Field | In | Type | Description |
|---|---|---|---|
workspace | body | string | Required. Workspace slug |
files | body | array | Required. Files to upload |
files[].filename | body | string | Required. Filename |
files[].size | body | integer | Required. File size in bytes |
Response: 200 OK
{
"uploads": [
{
"filename": "my-board.cvg",
"uploadUrl": "https://circuithub-uploads.s3.amazonaws.com/...",
"objectKey": "uploads/abc123/my-board.cvg",
"validTill": "2025-03-15T11:00:00Z",
"headers": [
["Content-Type", "application/octet-stream"],
["x-amz-acl", "bucket-owner-full-control"]
]
},
{
"filename": "bom.csv",
"uploadUrl": "https://circuithub-uploads.s3.amazonaws.com/...",
"objectKey": "uploads/abc123/bom.csv",
"validTill": "2025-03-15T11:00:00Z",
"headers": [
["Content-Type", "application/octet-stream"],
["x-amz-acl", "bucket-owner-full-control"]
]
}
]
}Upload each file by sending a PUT request to its uploadUrl with the headers from the response and the file as the request body.
POST /projects
Create a new project from previously uploaded files. Include all files (design files and optional BOM CSV) in the files array.
Request body:
{
"name": "power-supply-board",
"workspace": "acme-electronics",
"files": [
{ "objectKey": "uploads/abc123/my-board.cvg", "filename": "my-board.cvg" },
{ "objectKey": "uploads/abc123/bom.csv", "filename": "bom.csv" }
]
}| Field | In | Type | Description |
|---|---|---|---|
name | body | string | Required. Project name |
workspace | body | string | Required. Workspace slug |
files | body | array | Required. Uploaded file references |
files[].objectKey | body | string | Required. S3 object key from upload-urls response |
files[].filename | body | string | Required. Original filename |
Response: 201 Created
{
"projectId": 12345,
"revisionId": 98999,
"statusUrl": "/v1/projects/12345/status"
}GET /projects/:projectId/status
Check the import status of a project.
Response: 200 OK
{
"projectId": 12345,
"status": "importing",
"message": null
}| Field | Type | Description |
|---|---|---|
projectId | integer | Project ID |
status | string | "importing", "complete", or "failed" |
message | string | null | Error message when status is "failed" |
GET /projects/:projectId/revisions
List all revisions for a project.
Response: 200 OK
{
"revisions": [
{
"id": 99001,
"number": 3,
"createdAt": "2025-03-01T14:30:00Z",
"importedAt": "2025-03-01T14:35:00Z",
"edaTool": "kicad",
"edaError": null
}
]
}POST /projects/:projectId/revisions
Create a new revision on an existing project from previously uploaded files.
Request body:
{
"files": [
{ "objectKey": "uploads/def456/my-board-v2.kicad_pcb", "filename": "my-board-v2.kicad_pcb" },
{ "objectKey": "uploads/def456/bom.csv", "filename": "bom.csv" }
]
}| Field | In | Type | Description |
|---|---|---|---|
files | body | array | Required. Uploaded file references |
files[].objectKey | body | string | Required. S3 object key from upload-urls response |
files[].filename | body | string | Required. Original filename |
Response: 201 Created
{
"projectId": 12345,
"revisionId": 99002,
"statusUrl": "/v1/projects/12345/status"
}GET /projects/:projectId/bom
Get the Bill of Materials for a project. Returns the latest revision's BOM by default.
| Parameter | In | Type | Description |
|---|---|---|---|
revision | query | integer | Revision ID. Defaults to the latest revision if omitted |
Response: 200 OK
{
"revisionId": 99001,
"bomLines": [
{
"id": 1001,
"value": "100nF",
"footprint": "C_0402",
"description": "Ceramic capacitor",
"referenceDesignators": ["C1", "C2", "C3", "C4"],
"status": "resolved",
"resolvedPartMpn": "GCM155R71C104KA55D",
"resolvedPartDescription": "100nF 16V X7R 0402",
"resolvedManufacturer": "Murata"
}
],
"summary": {
"total": 4,
"resolved": 2,
"unresolved": 1,
"errored": 1
}
}| Field | Type | Description |
|---|---|---|
revisionId | integer | Revision the BOM belongs to |
BOM line fields:
| Field | Type | Description |
|---|---|---|
id | integer | BOM line ID |
value | string | null | Component value |
footprint | string | null | Component footprint |
description | string | null | Component description |
referenceDesignators | string[] | Reference designators |
status | string | "resolved" | "unresolved" | "error" |
resolvedPartMpn | string | null | Matched MPN |
resolvedPartDescription | string | null | Matched part description |
resolvedManufacturer | string | null | Matched manufacturer |
Summary fields:
| Field | Type | Description |
|---|---|---|
total | integer | Total BOM lines |
resolved | integer | Matched to a purchasable part |
unresolved | integer | No match found |
errored | integer | Resolution failed |
PUT /bom/:bomLineId/part
Set the part for a BOM line. Use the BOM line id from GET /projects/:projectId/bom and a part id from GET /parts. The project is derived from the BOM line — no project ID is needed.
Request body:
{ "partId": 5001 }| Field | In | Type | Description |
|---|---|---|---|
partId | body | integer | Required. Part ID from GET /parts |
Response: 200 OK
{
"partId": 5001,
"mpn": "LM358DR",
"manufacturer": "Texas Instruments"
}| Field | Type | Description |
|---|---|---|
partId | integer | Part ID that was set on the BOM line |
mpn | string | Manufacturer part number |
manufacturer | string | Manufacturer name |
PUT /bom/:bomLineId/placements/:ref/dnp
Mark or unmark a placement as Do Not Place (DNP). A DNP placement will not be assembled or purchased. The project is derived from the BOM line — no project ID is needed.
Request body:
{ "dnp": true }| Field | In | Type | Description |
|---|---|---|---|
dnp | body | boolean | Required. true to mark as DNP, false to unmark |
| Parameter | In | Type | Description |
|---|---|---|---|
ref | path | string | Required. Reference designator (e.g. "C3") |
Response: 200 OK
{
"ref": "C3",
"dnp": true
}| Field | Type | Description |
|---|---|---|
ref | string | Reference designator |
dnp | boolean | Current DNP state |
Quotes
Quoting is coming soon. The endpoints below describe the planned API surface — they are not yet available.
POST /quotes
Request a quote. Provide exactly one of projectId (quotes the latest revision) or revisionId.
Returns a cached quote if one is still valid for the same revision, otherwise generates a new one.
Request body:
{ "projectId": 12345 }
// or
{ "revisionId": 99001 }| Field | In | Type | Description |
|---|---|---|---|
projectId | body | integer | Project to quote — uses the latest revision |
revisionId | body | integer | Specific revision to quote |
Response: 200 OK
{
"id": 50001,
"projectId": 12345,
"revisionId": 99001,
"revisionNumber": 3,
"createdAt": "2025-03-15T10:00:00Z",
"expiresAt": "2025-03-15T11:00:00Z",
"offers": [
{
"quantity": 10,
"leadTimeDays": 10,
"estimatedShipDate": "2025-03-28T00:00:00Z",
"boardPrice": 230.00,
"partsPrice": 310.50,
"assemblyPrice": 250.00,
"totalPrice": 790.50,
"unitPrice": 79.05,
"currency": "USD"
}
]
}Quote fields:
| Field | Type | Description |
|---|---|---|
id | integer | Quote ID |
projectId | integer | Project this quote is for |
revisionId | integer | Revision used for pricing |
revisionNumber | integer | Human-readable revision number |
createdAt | string | ISO 8601 creation timestamp |
expiresAt | string | ISO 8601 expiry — quotes are valid for one hour |
offers | array | Pricing options at different quantities and lead times |
Offer fields:
| Field | Type | Description |
|---|---|---|
quantity | integer | Number of assembled boards |
leadTimeDays | integer | Business days from order to shipment |
estimatedShipDate | string | ISO 8601 estimated ship date |
boardPrice | number | Bare PCB fabrication cost (USD) |
partsPrice | number | Electronic components cost (USD) |
assemblyPrice | number | Assembly labor cost (USD) |
totalPrice | number | Total cost (USD) |
unitPrice | number | Per-board cost (USD) |
currency | string | Always "USD" |
GET /quotes/:quoteId/breakdown
Get a detailed cost breakdown for a specific quantity and lead time.
| Parameter | In | Type | Description |
|---|---|---|---|
quantity | query | integer | Required. Board quantity matching an offer |
leadTimeDays | query | integer | Required. Lead time matching an offer |
Response: 200 OK
{
"quoteId": 50001,
"quantity": 10,
"leadTimeDays": 10,
"total": 790.50,
"currency": "USD",
"lines": [
{
"type": "part",
"description": "100nF 0402",
"bomLineId": 1001,
"resolvedPartMpn": "GCM155R71C104KA55D",
"manufacturer": "Murata",
"supplier": "Digi-Key",
"orderQuantity": 44,
"unitPrice": 0.02,
"lineTotal": 0.88
}
]
}Line item fields:
| Field | Type | Description |
|---|---|---|
type | string | Line type — "part" for BOM components, other types may be added |
description | string | Human-readable description |
bomLineId | integer | null | BOM line ID (present when type is "part") |
resolvedPartMpn | string | null | Selected manufacturer part number (present when type is "part") |
manufacturer | string | null | Part manufacturer (present when type is "part") |
supplier | string | null | Distributor (e.g. "Digi-Key", "Mouser") |
orderQuantity | integer | Units ordered (includes attrition and price-break rounding) |
unitPrice | number | Cost per unit (USD) |
lineTotal | number | Total cost for this line (USD) |
Orders
GET /orders
List orders for a workspace.
| Parameter | In | Type | Description |
|---|---|---|---|
workspace | query | string | Required. Workspace slug |
Response: 200 OK
{
"orders": [
{
"id": 8001,
"urn": "urn:circuithub:order:8001",
"name": "ORD-8001",
"status": "in_progress",
"quantity": 10,
"createdAt": "2025-03-01T14:00:00Z",
"updatedAt": "2025-03-02T11:00:00Z",
"quotedShippingDate": "2025-03-15T00:00:00Z",
"estimatedShippingDate": "2025-03-14T00:00:00Z",
"shippedDate": null,
"trackingNumber": null,
"shippingService": null,
"value": 790.50,
"paymentStatus": "paid",
"poNumber": "PO-2025-042",
"projectId": 12345,
"projectName": "Power Supply Board",
"revisionId": 99001,
"revisionNumber": 3
}
]
}Order fields:
| Field | Type | Description |
|---|---|---|
id | integer | Order ID |
urn | string | Order URN |
name | string | Order name/number |
status | string | Order status (e.g. in_progress, completed) |
quantity | integer | Number of boards ordered |
createdAt | string | ISO 8601 creation timestamp |
updatedAt | string | ISO 8601 last update timestamp |
quotedShippingDate | string | Quoted shipping date, ISO 8601 |
estimatedShippingDate | string | null | Estimated shipping date, ISO 8601 |
shippedDate | string | null | Actual ship date, ISO 8601 |
trackingNumber | string | null | Shipment tracking number |
shippingService | string | null | Shipping carrier/service |
value | number | Order value in USD |
paymentStatus | string | Payment status |
poNumber | string | Purchase order number |
projectId | integer | Project this order belongs to |
projectName | string | Project name |
revisionId | integer | Project revision used for this order |
revisionNumber | integer | Human-readable revision number |
GET /orders/:orderId
Get a single order by ID.
Response: 200 OK
{
"id": 8001,
"urn": "urn:circuithub:order:8001",
"name": "ORD-8001",
"status": "in_progress",
"quantity": 10,
"createdAt": "2025-03-01T14:00:00Z",
"updatedAt": "2025-03-02T11:00:00Z",
"quotedShippingDate": "2025-03-15T00:00:00Z",
"estimatedShippingDate": "2025-03-14T00:00:00Z",
"shippedDate": null,
"trackingNumber": null,
"shippingService": null,
"value": 790.50,
"paymentStatus": "paid",
"poNumber": "PO-2025-042",
"projectId": 12345,
"projectName": "Power Supply Board",
"revisionId": 99001,
"revisionNumber": 3
}Parts
GET /parts
Search for parts by MPN or description.
| Parameter | In | Type | Description |
|---|---|---|---|
q | query | string | Required. Search query |
Response: 200 OK
{
"query": "LM358",
"exactMatches": [
{
"id": 5001,
"mpn": "LM358DR",
"manufacturer": "Texas Instruments",
"description": "Dual Operational Amplifier",
"available": 12500,
"moq": 1
}
],
"inexactMatches": [
{
"id": 5010,
"mpn": "LM358ADR",
"manufacturer": "Texas Instruments",
"description": "Dual Op-Amp, Improved",
"available": 4200,
"moq": 1
}
]
}| Field | Type | Description |
|---|---|---|
query | string | Original search query |
exactMatches | array | Exact MPN matches (case-insensitive) |
inexactMatches | array | Fuzzy/related matches |
Part fields:
| Field | Type | Description |
|---|---|---|
id | integer | Part ID |
mpn | string | Manufacturer part number |
manufacturer | string | Manufacturer name |
description | string | null | Part description |
available | integer | Current stock availability |
moq | integer | null | Minimum order quantity |
Issues
Issues are coming soon. The endpoints below describe the planned API surface — they are not yet available.
GET /issues
List issues for a workspace.
| Parameter | In | Type | Description |
|---|---|---|---|
workspace | query | string | Required. Workspace slug |
order | query | integer | Filter by order ID (optional) |
status | query | string | Filter by status: open, closed (optional) |
POST /orders/:orderId/issues
Create a new issue on an order.
| Parameter | In | Type | Description |
|---|---|---|---|
orderId | path | integer | Required. Order ID to file the issue against |
| Field | In | Type | Description |
|---|---|---|---|
title | body | string | Required. Short summary of the issue |
comment | body | string | Optional initial comment attached to the issue |
See the Issues guide for full examples and response shapes.
Billing
Billing is coming soon. The endpoints below describe the planned API surface — they are not yet available.
All billing endpoints are scoped to a workspace.
GET /workspaces/:workspaceSlug/billing/invoices
List invoices for a workspace.
| Parameter | In | Type | Description |
|---|---|---|---|
status | query | string | Filter: open, paid_in_full, approved_for_posting, fully_applied (optional) |
from | query | string | Start date, ISO 8601 (optional) |
to | query | string | End date, ISO 8601 (optional) |
Response: 200 OK
{
"invoices": [
{
"id": 50124,
"name": "INV-4835",
"type": "charge",
"recordType": "invoice",
"orderId": 8001,
"transactionDate": "2025-03-01T00:00:00Z",
"dueDate": "2025-03-20T00:00:00Z",
"totalNoTax": 80.00,
"tax": 5.00,
"total": 85.00,
"amountPaid": 0.00,
"amountUnpaid": 85.00,
"status": "open",
"description": "Additional testing services"
}
]
}GET /workspaces/:workspaceSlug/billing/invoices/:invoiceId
Get a single invoice by ID.
Response: 200 OK
{
"id": 50124,
"name": "INV-4835",
"type": "charge",
"recordType": "invoice",
"orderId": 8001,
"transactionDate": "2025-03-01T00:00:00Z",
"dueDate": "2025-03-20T00:00:00Z",
"totalNoTax": 80.00,
"tax": 5.00,
"total": 85.00,
"amountPaid": 0.00,
"amountUnpaid": 85.00,
"status": "open",
"description": "Additional testing services"
}GET /workspaces/:workspaceSlug/billing/invoices/:invoiceId/pdf
Download an invoice as PDF.
Response: 200 OK with Content-Type: application/pdf
GET /workspaces/:workspaceSlug/billing/statement
Generate a PDF statement for a date range.
| Parameter | In | Type | Description |
|---|---|---|---|
startDate | query | string | Required. Start date, ISO 8601 |
endDate | query | string | Required. End date, ISO 8601 |
Response: 200 OK with Content-Type: application/pdf
GET /workspaces/:workspaceSlug/billing/payments
List payments for a workspace. Includes both applied NetSuite payments and pending Stripe payments.
Response: 200 OK
{
"payments": [
{
"id": 60001,
"name": "PMT-1192",
"date": "2025-02-16T00:00:00Z",
"amount": 1240.00,
"method": "card",
"invoices": ["INV-4821"],
"orders": [{ "id": 8001, "projectName": "Power Supply Board" }],
"status": "applied"
}
]
}GET /workspaces/:workspaceSlug/billing/payment-methods
List payment methods configured for a workspace.
Response: 200 OK
{
"paymentMethods": [
{
"id": "pm_abc123",
"type": "card",
"brand": "visa",
"last4": "4242",
"isDefault": true
},
{
"id": "net_301",
"type": "net_terms",
"terms": "net_30",
"isDefault": false
}
]
}GET /workspaces/:workspaceSlug/billing/balance
Get credit balance and limit for a workspace. Returns null if no credit is enabled.
Response: 200 OK
{
"balance": 2450.00,
"creditLimit": 10000.00
}Error responses
All endpoints return errors in this format:
{
"error": "Project not found",
"code": "NOT_FOUND"
}| Code | Meaning |
|---|---|
NOT_FOUND | Requested resource does not exist |
VALIDATION_ERROR | Request payload or query parameters were invalid |
INVALID_REDIRECT_URI | redirect_uri is not an absolute localhost URL |
FILE_TOO_LARGE | Upload exceeded the maximum allowed size |
INVALID_UPLOAD_SIZE | Upload size was invalid |
INVALID_OBJECT_KEY | Uploaded object key did not match the workspace upload prefix |
FORBIDDEN | Authenticated user does not have permission to access the resource |
UNAUTHORIZED | Missing or invalid authentication |
RATE_LIMITED | Too many requests |
CREATION_FAILED | Project or revision creation failed after validation |
INTERNAL_ERROR | Unexpected server error |