Projects
Group and move related workspace resources.
A project groups related resources inside a workspace. Jobs, files, workflows, workflow runs, Studio sessions, and AI conversations can belong to one project or remain unassigned.
Every resource still belongs to the API key workspace. A project never changes workspace permissions, billing, credits, or storage ownership.
Project routes
The Projects API supports project metadata, resource assignment, and deletion operations.
Use GET /api/v1/projects to list projects and PATCH /api/v1/projects/assignments to organize supported resources.
Project lists are cursor paginated. Use limit (1–100, default 50), then pass the returned next_cursor as starting_after while has_more is true. The optional q parameter searches names, descriptions, and project IDs.
| Method | Route | Purpose |
|---|---|---|
GET | /api/v1/projects | List active projects |
POST | /api/v1/projects | Create a project |
GET | /api/v1/projects/{projectId} | Read project metadata and resource counts |
PATCH | /api/v1/projects/{projectId} | Update a project name or description |
DELETE | /api/v1/projects/{projectId} | Start a project deletion operation |
PATCH | /api/v1/projects/assignments | Move resources to a project or leave them unassigned |
GET | /api/v1/project-deletions/{operationId} | Read a deletion operation |
POST | /api/v1/project-deletions/{operationId} | Retry a recoverable deletion operation |
Read routes require projects:read. Write routes require projects:write and the matching workspace permission.
Create a project
Use POST /api/v1/projects with a name and optional description.
curl --fail-with-body --silent --show-error -X POST \
-H "Authorization: Bearer $PROTEINIQ_API_KEY" \
-H "Content-Type: application/json" \
"https://proteiniq.io/api/v1/projects" \
-d '{
"name": "Kinase screening",
"description": "Docking and follow-up analysis"
}'Names are trimmed, contain 1 to 80 characters, and are unique within a workspace without regard to case. Descriptions may contain up to 500 characters.
Project resources include counts for every supported resource type. A newly created project starts with zero counts; this example shows an existing project:
{
"id": "project_123",
"object": "project",
"name": "Kinase screening",
"description": "Docking and follow-up analysis",
"counts": {
"jobs": 4,
"files": 8,
"workflows": 1,
"workflow_runs": 3,
"studio_sessions": 0,
"ai_conversations": 2
},
"created_at": "2026-07-25T08:00:00.000Z",
"updated_at": "2026-07-25T09:00:00.000Z"
}Update a project
Send PATCH /api/v1/projects/{projectId} with name, description, or both. Names follow the creation limits; use description: null to clear a description. The response is the updated project resource.
Assign resources
Use PATCH /api/v1/projects/assignments to move jobs, files, workflows, workflow runs, Studio sessions, or AI conversations. Set target_project_id to null to leave the selected resources unassigned.
{
"target_project_id": "project_123",
"resources": [
{
"type": "job",
"ids": ["job_123"]
},
{
"type": "file",
"ids": ["file_123"]
}
],
"move_linked_files": true
}The request requires projects:write plus the write scope for every resource type:
- Jobs, workflows, and workflow runs:
jobs:write - Files:
files:write - Studio sessions and AI conversations: no additional resource scope
move_linked_files defaults to false and applies when jobs are selected. When true, saved files linked to those jobs move to the same destination. Moving a file never moves its source job.
Each request can select at most 500 resources. Every selected resource and destination project must belong to the API key workspace.
Accepted resources[].type values are job, file, workflow, workflow_run, studio_session, and ai_conversation.
Use projects with other resources
Job, file, workflow, and workflow-run resources include project_id. A null value means the resource is unassigned.
- Create a job: Add optional
project_idtoPOST /api/v1/jobs - Upload a file: Add optional
project_idto the multipart form forPOST /api/v1/files - Start a workflow run: Add optional
project_idtoPOST /api/v1/workflows/{workflowId}/runs - Filter lists: Add
project_id=<id>to job, file, or workflow list routes - Find unassigned resources: Add
unassigned=trueto those list routes
Do not combine project_id and unassigned=true in one list request.
Creating a resource with a non-null project_id requires projects:write in addition to that resource's normal write scope. Omitting project_id keeps direct jobs and uploads unassigned. A workflow run inherits its workflow project unless the request supplies an override; any explicit workflow-run project_id, including null, requires projects:write.
Delete a project
Project deletion requires owner or admin permission. Send one of three modes to DELETE /api/v1/projects/{projectId}:
move: Move direct project resources totarget_project_id, then delete the projectunassign: Leave direct project resources in the workspace without a projectdelete_contents: Delete the project and its contents using the existing resource deletion behavior
{
"mode": "move",
"target_project_id": "project_456"
}Deletion is rejected while affected jobs or workflow runs are active, including paused runs that still block deletion. Resolve these before retrying. Collections remain workspace-wide. Deleting project files removes their collection links but does not delete the collections.
The endpoint returns a project_deletion resource. Read it through GET /api/v1/project-deletions/{operationId} when a client needs to confirm the final state. If a failed operation returns "retryable": true, retry its cleanup with POST /api/v1/project-deletions/{operationId}. Retrying requires projects:write and owner or admin permission.
{
"id": "deletion_123",
"object": "project_deletion",
"project_id": "project_123",
"project_name": "Kinase screening",
"mode": "unassign",
"target_project_id": null,
"status": "completed",
"error": null,
"attempt_count": 1,
"next_attempt_at": null,
"retryable": false,
"created_at": "2026-07-25T10:00:00.000Z",
"started_at": "2026-07-25T10:00:00.000Z",
"completed_at": "2026-07-25T10:00:01.000Z",
"updated_at": "2026-07-25T10:00:01.000Z"
}