Skip to main content

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"
}'
FieldRequiredNotes
nameyesDisplay name for the project. Often an internal quote number. Duplicate names are allowed.
cutConfigIdnoDefault 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.

New uploads only

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 (a 404 if 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.