Goals and Projects
Goals answer “why are we doing this?” Projects answer “what concrete deliverable are we organizing around?” In Paperclip, goals form the higher-level intent tree, while projects group work, workspaces, and runtime behavior around a deliverable.
At a Glance
Section titled “At a Glance”| Concept | Use it for | Key fields | Notes |
|---|---|---|---|
| Goal | Company mission, team objective, or agent/task objective | title, description, level, status, parentId, ownerAgentId | level defaults to task; status defaults to planned |
| Project | A deliverable with linked goals and workspaces | name, description, status, goalId / goalIds, leadAgentId, targetDate, env, executionWorkspacePolicy | goalIds is preferred; goalId is kept in sync for legacy callers |
| Workspace | A repository or local folder attached to a project | name, sourceType, cwd, repoUrl, repoRef, defaultRef, visibility, runtimeConfig, isPrimary | The first workspace becomes primary automatically |
All of these endpoints are company-scoped. If you cannot access the company, you cannot list or mutate its goals or projects.
Goals are the planning layer. They are useful when you want to express strategy, break objectives into sub-goals, or attach ownership to a specific agent.
Goal levels are:
companyteamagenttask
Goal statuses are:
plannedactiveachievedcancelled
Fields
Section titled “Fields”titleis required.descriptionis optional.leveldefaults totaskif you omit it.statusdefaults toplannedif you omit it.parentIdlinks the goal to another goal.ownerAgentIdlinks the goal to an agent.
Routes
Section titled “Routes”List Goals
Section titled “List Goals”GET /api/companies/{companyId}/goalsReturns all goals for the company.
Get Goal
Section titled “Get Goal”GET /api/goals/{goalId}Returns a single goal by ID. The API checks company access after the goal is found.
Create Goal
Section titled “Create Goal”Use this when you want to define a new objective or sub-objective.
curl -X POST "http://localhost:3100/api/companies/{companyId}/goals" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "title": "Launch onboarding revamp", "description": "Reduce time-to-first-success for new users", "level": "company", "status": "active" }'await fetch(`http://localhost:3100/api/companies/${companyId}/goals`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ title: "Launch onboarding revamp", description: "Reduce time-to-first-success for new users", level: "company", status: "active", }),});import requests
requests.post( f"http://localhost:3100/api/companies/{company_id}/goals", headers={"Authorization": f"Bearer {token}"}, json={ "title": "Launch onboarding revamp", "description": "Reduce time-to-first-success for new users", "level": "company", "status": "active", },)Update Goal
Section titled “Update Goal”Use this to move a goal forward, change ownership, or re-parent it.
PATCH /api/goals/{goalId}{ "status": "achieved", "description": "Updated description", "ownerAgentId": "{agentId}"}This is a partial update. Any field you omit stays unchanged.
Delete Goal
Section titled “Delete Goal”DELETE /api/goals/{goalId}Deletes the goal and returns the deleted row.
Projects
Section titled “Projects”Projects sit one level below goals in day-to-day execution. Use a project when you want to organize a deliverable, attach workspaces, and group related issues under one practical container.
Projects have a few important implementation details:
goalIdsis the preferred way to link a project to one or more goals.goalIdis still accepted for compatibility, but the service keeps it in sync with the first linked goal.- Project names are normalized so shortname-style references stay unique within a company.
- If you omit
color, Paperclip assigns one automatically from its project palette. - The returned project includes
urlKey,goalIds,goals,codebase,workspaces,primaryWorkspace, andexecutionWorkspacePolicy.
Fields
Section titled “Fields”nameis required.descriptionis optional.statusdefaults tobacklog.leadAgentIdis optional.targetDateis optional.goalIdsis preferred for new code.workspacecan be supplied during create to seed the first workspace in the same request.envis the project environment binding config. Secret bindings are normalized before storage.executionWorkspacePolicyis an advanced runtime policy object.archivedAtcan be set when archiving a project.
Project statuses are:
backlogplannedin_progresscompletedcancelled
Routes
Section titled “Routes”List Projects
Section titled “List Projects”GET /api/companies/{companyId}/projectsReturns all projects in the company.
Get Project
Section titled “Get Project”GET /api/projects/{projectId}You can pass either a UUID or a unique project shortname in :projectId. If the shortname is ambiguous, the API returns 409 Conflict and asks you to use the UUID.
Create Project
Section titled “Create Project”If you provide workspace, the project is created and the workspace is created in the same request. If the workspace payload is invalid, the project creation is rolled back and the API returns 422.
curl -X POST "http://localhost:3100/api/companies/{companyId}/projects" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "name": "Auth System", "description": "End-to-end authentication and session flow", "goalIds": ["{goalId}"], "status": "planned", "workspace": { "name": "auth-repo", "cwd": "/path/to/workspace", "repoUrl": "https://github.com/org/repo", "repoRef": "main", "isPrimary": true } }'await fetch(`http://localhost:3100/api/companies/${companyId}/projects`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ name: "Auth System", description: "End-to-end authentication and session flow", goalIds: [goalId], status: "planned", workspace: { name: "auth-repo", cwd: "/path/to/workspace", repoUrl: "https://github.com/org/repo", repoRef: "main", isPrimary: true, }, }),});import requests
requests.post( f"http://localhost:3100/api/companies/{company_id}/projects", headers={"Authorization": f"Bearer {token}"}, json={ "name": "Auth System", "description": "End-to-end authentication and session flow", "goalIds": [goal_id], "status": "planned", "workspace": { "name": "auth-repo", "cwd": "/path/to/workspace", "repoUrl": "https://github.com/org/repo", "repoRef": "main", "isPrimary": True, }, },)Update Project
Section titled “Update Project”Use this to change status, goals, target date, environment bindings, archive state, or other project metadata.
PATCH /api/projects/{projectId}{ "status": "in_progress", "goalIds": ["{goalId}"], "archivedAt": null}This endpoint also accepts the legacy goalId field, but goalIds should be preferred.
Delete Project
Section titled “Delete Project”DELETE /api/projects/{projectId}Deletes the project and returns the deleted row.
Project Workspaces
Section titled “Project Workspaces”A project can have one or more workspaces. The primary workspace is the default workspace for project-scoped work.
Workspace source types supported by the code are:
local_pathgit_reporemote_managednon_git_path
Validation rules:
- A workspace must include at least one of
cwdorrepoUrl. - A
remote_managedworkspace must includeremoteWorkspaceReforrepoUrl. - The first workspace in a project becomes primary automatically.
- Passing
isPrimary: truemakes that workspace primary. - If you remove the primary workspace, Paperclip promotes another workspace automatically.
List Workspaces
Section titled “List Workspaces”GET /api/projects/{projectId}/workspacesReturns all workspaces for the project.
Create Workspace
Section titled “Create Workspace”Workspaces can be created after the project already exists.
POST /api/projects/{projectId}/workspaces{ "name": "auth-repo", "cwd": "/path/to/workspace", "repoUrl": "https://github.com/org/repo", "repoRef": "main", "isPrimary": true}Update Workspace
Section titled “Update Workspace”PATCH /api/projects/{projectId}/workspaces/{workspaceId}{ "name": "auth-repo-main", "isPrimary": true}Updating a workspace can also change its cwd, repoUrl, repoRef, defaultRef, visibility, setupCommand, cleanupCommand, remoteProvider, remoteWorkspaceRef, sharedWorkspaceKey, metadata, and runtimeConfig.
Delete Workspace
Section titled “Delete Workspace”DELETE /api/projects/{projectId}/workspaces/{workspaceId}Deletes the workspace and returns the deleted row.
Workspace Runtime Services
Section titled “Workspace Runtime Services”This endpoint controls runtime services attached to a specific project workspace.
POST /api/projects/{projectId}/workspaces/{workspaceId}/runtime-services/{action}action must be one of:
startstoprestart
Important behavior from the code:
startandrestartrequire the workspace to have a local path (cwd).startandrestartalso require workspace runtime configuration.- The route updates the workspace
desiredStatetorunningorstopped. - The response includes the updated workspace and an operation record.
curl -X POST "http://localhost:3100/api/projects/{projectId}/workspaces/{workspaceId}/runtime-services/start" \ -H "Authorization: Bearer <token>"await fetch( `http://localhost:3100/api/projects/${projectId}/workspaces/${workspaceId}/runtime-services/start`, { method: "POST", headers: { Authorization: `Bearer ${token}` }, },);import requests
requests.post( f"http://localhost:3100/api/projects/{project_id}/workspaces/{workspace_id}/runtime-services/start", headers={"Authorization": f"Bearer {token}"},)Practical Model
Section titled “Practical Model”Use a goal when you want to capture intent and hierarchy. Use a project when you want to attach actual work: workspaces, runtime behavior, and issue execution. In practice, most teams create a company goal, add one or more supporting goals, then create a project that points at the work they want the agents to execute against.
When you are unsure which one to create, ask:
- “Is this about why we exist?” Create or update a goal.
- “Is this a deliverable with real code or files?” Create or update a project.
- “Do I need a repo or directory to run work against?” Add a workspace.