Walkthrough: organize parts into projects
Projects group parts in the Toolpath web app. This guide shows how to create a project with a name you choose (for example an internal quote number), upload parts into it, and rename it later. It ties three calls together:
Overview
POST /projects → create a named project, get its id
POST /parts (projectId) → upload new parts into that project
PUT /projects/{id} → rename the project or change its cut config
Every one of these is a write, so each requires an
Idempotency-Key header so retries are safe, and each
needs a read_write API key. There are no GET project endpoints in v0: keep
the id returned by POST /projects, because that is how you reference the
project on later calls.
1. Create a project
Name the project whatever helps you track it. Duplicate names are allowed, so you can reuse a quote number across revisions without conflict.
curl -X POST https://app.toolpath.com/api/public/v0/projects \
-H "Authorization: Bearer tp_live_xxxxxxxxxxxx" \
-H "Idempotency-Key: 5f3c9b2a-1d4e-4f8a-9c2b-7e1a3b6d8c40" \
-H "Content-Type: application/json" \
-d '{
"name": "Q-1042"
}'
| Field | Required | Notes |
|---|---|---|
name | yes | Display name for the project. Often an internal quote number. Duplicate names are allowed. |
cutConfigId | no | Default cut config id from GET /cut-configs to associate with the project. Parts uploaded into the project without explicit cutConfigIds auto-create their program with it. Omit or pass null for none. 404 if unknown. |
You get back 201 Created with the new project in data:
{
"data": {
"id": "proj1234",
"name": "Q-1042",
"cutConfigId": null,
"createdAt": "2026-06-30T17:00:00.000Z",
"updatedAt": "2026-06-30T17:00:00.000Z"
}
}
Hold on to data.id (proj1234 here). You pass it as projectId when
uploading parts, and as the {id} path segment when renaming.
The first response also carries a Location header with the project's resource
URL. There is no GET for that URL in v0 (it is the URL you PUT to update),
and an idempotent replay returns the same body without the header, so rely on
data.id rather than Location.
2. Upload parts into the project
Uploading a part is the same three-step flow as the
Estimate a part walkthrough (create, PUT the STEP
bytes to the presigned URL, then complete). The only addition is the
projectId field on POST /parts, which attaches the new part to your project
instead of an auto-created one:
curl -X POST https://app.toolpath.com/api/public/v0/parts \
-H "Authorization: Bearer tp_live_xxxxxxxxxxxx" \
-H "Idempotency-Key: 6a4d0c3b-2e5f-4a02-9d1c-3e6f9a2b5d04" \
-H "Content-Type: application/json" \
-d '{
"name": "bracket",
"units": "mm",
"projectId": "proj1234"
}'
An unknown projectId, or one owned by another team, returns 404 before the
part is created, so a bad id never leaves a stray part behind. Repeat POST /parts with the same projectId for each part you want in the project. Parts
keep the order they were added.
If you created the project with a cutConfigId, parts uploaded without
explicit cutConfigIds auto-create their program with the project's cut config
instead of the API key creator's default. Pass cutConfigIds on POST /parts
to override it for a single part.
projectId attaches a brand-new upload to the project. There is no endpoint
to move an already-uploaded part between projects in v0, because a part belongs
to exactly one project. Choose the project at upload time.
3. Rename or update the project
PUT /projects/{id} sends only the fields you want to change. Send name to
rename; the updatedAt timestamp advances while createdAt stays fixed.
curl -X PUT https://app.toolpath.com/api/public/v0/projects/proj1234 \
-H "Authorization: Bearer tp_live_xxxxxxxxxxxx" \
-H "Idempotency-Key: 7b5e1d4c-3f60-4b13-ae2d-4f70ab3c6e15" \
-H "Content-Type: application/json" \
-d '{
"name": "Q-1042-rev2"
}'
Returns 200 with the updated project:
{
"data": {
"id": "proj1234",
"name": "Q-1042-rev2",
"cutConfigId": null,
"createdAt": "2026-06-30T17:00:00.000Z",
"updatedAt": "2026-06-30T18:30:00.000Z"
}
}
The cutConfigId field is three-way, so a partial body can leave it, set it, or
clear it:
- Omit
cutConfigId: the cut config is left unchanged. "cutConfigId": "cfg00001": set it to that cut config (a404if the id is unknown or owned by another team)."cutConfigId": null: clear it.
An empty body ({}) is valid and simply returns the current project without
changing its name or cut config. Renaming a project does not touch the parts
already in it, and a 404 on a missing or other-team {id} keeps project ids
from leaking across teams.