Documentation
Project lifecycle
Create a project, save its design data, reopen it, and choose the right kind of history.
What a project contains
A project groups drawings, reusable components, and saved toolpaths. A drawing stores a JSON document; a component stores reusable design data; a toolpath can hold plain NC and metadata or a validated portable CAM document. Materials, cutters, and machine configurations are separate resource records that machining workflows can reference. A project identifier does not replace any of those resource identifiers.
This guide follows the same families used by the desktop. It separates saving application state, downloading a file, recording a toolpath version, and committing project history. Those operations preserve different information. Start with a disposable development project before applying the sequence to production work.
The examples are request templates whose JSON bodies match the current schemas. They are not captured server responses. For every field and declared status, use the Projects, Drawings, Components, and Toolpaths API references.
Select the real API
Run the desktop with bun run desktop:dev, or start an isolated manual sidecar using the local development recipe. The manual process announces an ephemeral port. Browser preview without an endpoint uses in-memory fixtures; it cannot establish that a project survives restarting the server.
CADCAM_URL=http://127.0.0.1:43111
CADCAM_TOKEN=local-development-token
CADCAM_DEMO_DIR=$(mktemp -d /tmp/cadcamfun-project-guide.XXXXXX)
curl --fail-with-body "$CADCAM_URL/api/health"
curl --fail-with-body "$CADCAM_URL/api/projects" -H "Authorization: Bearer $CADCAM_TOKEN" Health is public, so it can succeed while a protected project request fails authorization. Use the token supplied to this process. Keep the same shell for the following examples so the URL, token, temporary directory, and extracted identifiers remain available. Keep the sidecar's stdin open in its launching terminal.
Create and inspect a project
curl --fail-with-body -X POST "$CADCAM_URL/api/projects" -H "Authorization: Bearer $CADCAM_TOKEN" -H 'Content-Type: application/json' -d '{"name":"Bracket study","description":"Disposable guide project","organizationId":null,"isPublic":false}' -o "$CADCAM_DEMO_DIR/project.json"
CADCAM_PROJECT_ID=$(bun --no-env-file -e 'console.write((await Bun.file(process.argv[1]).json()).id)' "$CADCAM_DEMO_DIR/project.json")
curl --fail-with-body "$CADCAM_URL/api/projects/$CADCAM_PROJECT_ID" -H "Authorization: Bearer $CADCAM_TOKEN" Creation returns HTTP 201 with a project row. The handler requires a truthy name even though the transport schema declares that field optional. A supplied organization must be one the caller belongs to. Reading the project returns owner/organization information and drawing/component counts; it is a different response shape from creation.
In the desktop, use /projects to browse and /projects/:id to open the dashboard. It has Drawings, Components, and Toolpaths tabs, plus Edit, Delete, and Version control actions. Project update requires the name again; owner or organization ADMIN/MANAGER can update, while deletion is restricted to the owner or ADMIN.
Create and open a drawing
curl --fail-with-body -X POST "$CADCAM_URL/api/projects/$CADCAM_PROJECT_ID/drawings" -H "Authorization: Bearer $CADCAM_TOKEN" -H 'Content-Type: application/json' -d '{"name":"Main drawing","description":null,"data":{"elements":[],"version":"1.0"}}' -o "$CADCAM_DEMO_DIR/drawing.json"
CADCAM_DRAWING_ID=$(bun --no-env-file -e 'console.write((await Bun.file(process.argv[1]).json()).id)' "$CADCAM_DEMO_DIR/drawing.json")
curl --fail-with-body "$CADCAM_URL/api/drawings/$CADCAM_DRAWING_ID" -H "Authorization: Bearer $CADCAM_TOKEN" The same empty document shape is used by the project dashboard's create-drawing flow. It creates a row, not geometry. Open /cad?drawingId=<id> or /drawings/<id> to edit it. With /cad?projectId=<id>, CAD selects the project's newest drawing; a named drawing takes precedence.
Drawing creation needs project access and a name. An individual drawing read/update requires the project owner or an organization member; deletion adds an owner/organization-ADMIN check. JSON data is not validated as a complete drawable geometry document by the generic endpoint. A successful write therefore does not prove every supplied element can render.
Save, duplicate, and export design data
On an already opened stored drawing, the CAD save action updates that drawing and adds missing local-library components to its project. The CAD page implements its own Cmd/Ctrl+S handler. A document without a stored destination opens the export/save flow; Save as Project requires geometry or a selected element and creates a new project and drawing.
The drawing snapshot includes elements, layers, selected-element identifier, measuring settings, and a format version. The current load path restores elements and valid units/tolerance settings, maps unknown layer identifiers to the active layer, and clears selection. Do not infer complete layer restoration from the fields present in an export.
Project component synchronization matches trimmed, case-insensitive names and adds missing names; it does not overwrite a same-name definition. Component creation is best effort and can fail independently. A saved drawing does not prove every local component was copied, and the multi-request save flow is not one database transaction.
From a drawing card, Duplicate loads its stored data and creates a new identifier. Download exports that drawing's data as JSON; it does not export an entire project. Generic drawing updates apply truthy name/data/thumbnail values and allow an explicit null description. They do not expose the repository's compare-and-swap hash parameter.
Store a reusable component
curl --fail-with-body -X POST "$CADCAM_URL/api/projects/$CADCAM_PROJECT_ID/components" -H "Authorization: Bearer $CADCAM_TOKEN" -H 'Content-Type: application/json' -d '{"name":"Bracket insert","description":"Reusable empty template","data":{"name":"Bracket insert","type":"custom","elements":[]}}' -o "$CADCAM_DEMO_DIR/component.json"
CADCAM_COMPONENT_ID=$(bun --no-env-file -e 'console.write((await Bun.file(process.argv[1]).json()).id)' "$CADCAM_DEMO_DIR/component.json")
curl --fail-with-body -X PUT "$CADCAM_URL/api/components/$CADCAM_COMPONENT_ID" -H "Authorization: Bearer $CADCAM_TOKEN" -H 'Content-Type: application/json' -d '{"type":"custom","isPublic":false,"data":{"name":"Bracket insert","type":"custom","elements":[]}}' Creation returns HTTP 201. The current repository create operation stores name, description, data, and project association; it does not persist top-level type/public/thumbnail options at creation. The explicit update above sets the intended top-level type and visibility. Update validates truthy component data, normalizes missing optional fields, and rejects an empty patch.
Open /components/:id for the detail screen or browse /components with a project filter. Visibility currently includes project owners, organization members, and public components. The component handlers do not enforce an additional owner-only gate for visible-component edits/deletion; do not treat public visibility as a protected read-only share.
Prepare machining resources
The resource screens are /materials, /tools, and /machine, with individual detail routes. Materials require a name and a properties value. Tools require name, type, nonzero diameter, and material. The UI supplies positive diameter constraints, but integrations should validate engineering dimensions explicitly rather than relying on truthiness checks.
{
"name": "6 mm end mill",
"type": "endmill",
"diameter": 6,
"material": "carbide",
"numberOfFlutes": 3,
"cuttingLength": 18,
"shankDiameter": 6,
"isPublic": false
} A machine configuration needs type mill, lathe, printer, or laser; positive work-volume dimensions, maximum spindle speed, and maximum feed rate; and optional controller/axes data. Four/five-axis configurations require compatible rotary configuration checked by the handler. These records describe equipment; their existence does not verify a particular generated program.
Use Materials, Tools, and Machines for exact bodies. Material/tool lists return arrays; machine routes use a success envelope. Read the selected endpoint's response instead of assuming one universal collection shape.
Save and reopen a CAM program
A toolpath can be created under a project with POST /api/toolpaths using name and projectId, or attached to a drawing with POST /api/drawings/:id/toolpaths using name and data. The project dashboard uses the top-level family and opens the result through /cam?loadToolpath=<id>. The CAM page reads the saved original through the API.
The CAM save modal offers existing project, new project, or update of the saved program. G-code export downloads text. Portable export downloads .cam.json with format: cadcamfun-cam, settings, program, stock, drawing context, and matching evidence where available. Application save persists that document in the toolpath record.
Portable documents are validated and matched to their NC. Editing code can remove stale evidence or require reparsing. New kernel artifacts are rechecked against captured source requests; arbitrary client-supplied verification claims are not accepted as proof. Owner/ADMIN/MANAGER restrictions also apply to saving portable programs into a project.
When a new-project save partially succeeds, the modal retains its created destination for retry. Inspect that identifier before restarting the flow and creating another project. See Toolpaths and saved program documents.
Choose the history you need
| Mechanism | Preserves | Use |
|---|---|---|
| Editor undo/redo | Local interaction/session snapshots | Undo the current editing action. |
| Toolpath versions | Recorded data, NC, author, and change message for one program | Review or restore a program revision. |
| Project history | Committed project snapshots and changed entity files | Compare, branch, review, or restore project state. |
| Downloaded JSON/NC | The particular exported content | Keep or transfer an external artifact. |
Updating through the CAM save modal records the previous program first. Manual save on the toolpath detail screen records the updated state as a version. Version restore records the current state before replacing it, so a restore can be reversed through another version operation. These are distinct flows; a generic PUT is not automatically a project commit.
Open /toolpaths/:id for Details, G-code, Versions, and Comments. Open /projects/:id/history for project history; per-item buttons filter by paths such as drawings/<id>.json. The toolpath history API accepts versionId for restoration.
History changes are also published as live events (commit, pr.*, workflow.started/workflow.finished, ai.session). A new subscriber first receives up to 20 recent matching events.
Verify persistence and resolve failures
- Read the created project, drawing, and component through their identifier endpoints.
- Open the corresponding desktop routes and confirm the actual design/program contents.
- Restart the same sidecar data directory, update the ephemeral URL, and repeat the reads.
- Download an export separately and inspect its fields; a database row does not prove a file was exported.
A 400 usually points to missing required business fields or invalid document data; a 403 indicates a refused operation; a 404 can mean absent or inaccessible data. The project dashboard currently turns individual resource-list failures into empty arrays, so an empty tab alone is insufficient evidence of deletion. Inspect the direct API result before creating duplicates.
Implementation anchors are projects.ts and groups/{drawings,components,toolpaths}.ts in the API package; the corresponding sidecar handlers/repositories; projectSnapshot.ts, CadPage.tsx, SaveCamModal.tsx, and ToolpathDetailScreen.tsx. Use an isolated project for deletion tests and preserve user work before restoration.
Recipe: the whole lifecycle with curl
A=(--header "Authorization: Bearer $CADCAMFUN_TOKEN" --header "Content-Type: application/json")
U="$CADCAMFUN_URL"
project=$(curl -sS "${A[@]}" -d '{"name":"Bracket"}' "$U/api/projects" | jq -r .id)
drawing=$(curl -sS "${A[@]}" -d '{"name":"Plate","data":{}}' "$U/api/projects/$project/drawings" | jq -r .id)
curl -sS "${A[@]}" "$U/api/projects/$project/drawings" | jq 'length'
curl -sS "${A[@]}" -X PUT -d '{"name":"Plate v2"}' "$U/api/drawings/$drawing"
curl -sS "${A[@]}" -d '{"name":"Program","gcode":"G21\nM30","data":{}}' \
"$U/api/drawings/$drawing/toolpaths" | jq .id
curl -sS "${A[@]}" -d '{"message":"First version"}' "$U/api/projects/$project/vcs/commit"
curl -sS "${A[@]}" "$U/api/projects/$project/vcs/log" | jq | Step | Operation | Body fields |
|---|---|---|
| Create project | POST /api/projects | name (required by the handler), description, organizationId, isPublic |
| Create drawing | POST /api/projects/{id}/drawings | name, description, data, thumbnail |
| Update drawing | PUT /api/drawings/{id} | same fields, partial |
| Create toolpath | POST /api/drawings/{id}/toolpaths | name, data, gcode, type, operationType, materialId, toolId, machineConfigId, … |
| Commit | POST /api/projects/{id}/vcs/commit | message (required), authorKind, files |
| Branch | POST /api/projects/{id}/vcs/branches | name (required), from |
| Restore | POST /api/projects/{id}/vcs/restore | rev (required), paths |
Run against a fresh sidecar, the commit answers with the new commit object, and the log lists it:
{"oid":"44d2d03d23b27fbbc4b388e32e15dfb0f39fff87","parents":[],"message":"First version\n","author":{"name":"Local user","email":"local@cadcamfun.local","time":1792037121}} Without a session header the author is the installation's local profile. The recipe uses jq. Free-form toolpath data is accepted as is; a portable cadcamfun-cam document is validated strictly, as described in the native manufacturing guide.