> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pinework.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Workspace and project

> A company is the billing account, a workspace is the tenant that holds agents and tasks, and a project groups tasks inside one workspace.

A company is the billing account. It holds one or more workspaces. A workspace is the tenant: each agent, task and project belongs to one workspace. A project groups tasks inside a workspace. Workspace and project ids are UUIDs. For why the split exists, see [Workspaces and companies](/concepts/workspaces).

Task keys such as `PIN-12` come from the workspace's `issuePrefix` and its counter. A project's `prefix` is stored, but it does not name task keys.

## States

### Workspace

| State | Meaning | Ends here |
| - | - | - |
| `active` | The default for a new workspace. | No |
| `archived` | An admin archived it. No route sets it back. | Yes |

| From | To | Trigger |
| - | - | - |
| `active` | `archived` | `POST /workspaces/{workspaceId}/archive`, workspace admins only. |
| any | removed | `DELETE /workspaces/{workspaceId}` by the owner. It cannot be undone. Deleting the company's last workspace also deletes the company and cancels its subscription. |

`agentsPaused` is a flag, not a state. While it is `true`, no agent in the workspace starts a run. `workspace pause-agents` sets it, and `workspace resume-agents` clears it.

### Project

| State | Meaning | Ends here |
| - | - | - |
| `active` | The default for a new project. | No |
| `archived` | Set by hand. | No |
| `completed` | Set by hand. | No |

| From | To | Trigger |
| - | - | - |
| any status | any other status | `project config set --status`. No order is enforced. |
| live | deleted | `project delete` sets `deletedAt`. |
| deleted | live | `project restore`, by the owner or a workspace admin. |

Deleted is derived from `deletedAt`, not stored in `status`. List deleted projects with `--status deleted`.

## Fields

### Workspace

<ResponseField name="id" type="string" required>Workspace UUID. Routes take this id.</ResponseField>
<ResponseField name="name" type="string" required>Workspace name.</ResponseField>
<ResponseField name="slug" type="string" required>URL slug. A slug you rename away from stays reserved.</ResponseField>
<ResponseField name="description" type="string">Null when not set.</ResponseField>
<ResponseField name="status" type="string" required>`active` or `archived`.</ResponseField>
<ResponseField name="issuePrefix" type="string" required>Prefix of every task key, such as `PIN`.</ResponseField>
<ResponseField name="issueCounter" type="integer" required>Number of the last task key handed out.</ResponseField>
<ResponseField name="contextMd" type="string">Workspace context that each run reads. Null when not set.</ResponseField>
<ResponseField name="defaultModel" type="string">Model for agents that name none. Null means the platform default.</ResponseField>
<ResponseField name="defaultReasoningEffort" type="string">Reasoning effort for agents that name none. Null means the platform default.</ResponseField>
<ResponseField name="defaultMaxTurns" type="integer">Turn cap for agents that set none. Null means no cap.</ResponseField>
<ResponseField name="backupModel" type="string">Model used on provider failover. Null when not set.</ResponseField>
<ResponseField name="mcpServers" type="object">MCP servers added to each agent's own. Null when not set.</ResponseField>
<ResponseField name="agentsPaused" type="boolean" required>`true` while all agents are paused.</ResponseField>

<ResponseField name="idleCompaction" type="object" required>
  Compacts an idle Claude session on your device before its prompt cache expires.

  <Expandable title="properties">
    <ResponseField name="enabled" type="boolean" required>Off by default.</ResponseField>
    <ResponseField name="afterMinutes" type="integer" required>Idle minutes before compaction. Default 55.</ResponseField>
    <ResponseField name="minTokens" type="integer" required>Context size below which nothing is compacted. Default 100,000.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="sandboxSize" type="string">Default cloud box size: `small`, `medium` or `large`. Null means `small`.</ResponseField>
<ResponseField name="oomResume" type="boolean" required>When `true`, a run that runs out of RAM resumes on the next box size up.</ResponseField>
<ResponseField name="createdAt" type="string" required>Creation time.</ResponseField>
<ResponseField name="updatedAt" type="string" required>Last change time.</ResponseField>

`GET /user/workspaces` lists the workspaces you belong to. Each entry carries `publicId` (`ws_…`), your `role`, and the billing `companyId` and `companyName`.

### Project

<ResponseField name="object" type="string" required>Always `project`.</ResponseField>
<ResponseField name="id" type="string" required>Project UUID. Routes take only this id.</ResponseField>
<ResponseField name="workspaceId" type="string" required>Workspace UUID.</ResponseField>
<ResponseField name="name" type="string" required>Project name.</ResponseField>
<ResponseField name="description" type="string" required>Full description. Empty string when not set.</ResponseField>
<ResponseField name="descriptionPreview" type="string">First 220 characters of the description. Null when there is none.</ResponseField>
<ResponseField name="status" type="string" required>`active`, `archived` or `completed`.</ResponseField>
<ResponseField name="prefix" type="string">2 to 5 uppercase letters, fixed at create. Null when not set.</ResponseField>
<ResponseField name="leadAgentId" type="string">UUID of the lead agent. Null when there is none.</ResponseField>
<ResponseField name="ownerUserId" type="string">UUID of the owner. Null when an agent created the project.</ResponseField>
<ResponseField name="visibility" type="string" required>`company` or `private`.</ResponseField>
<ResponseField name="repoUrl" type="string">Repository URL. Null when not set.</ResponseField>
<ResponseField name="deletedAt" type="string">Null unless the project is deleted.</ResponseField>
<ResponseField name="createdAt" type="string" required>Creation time.</ResponseField>
<ResponseField name="updatedAt" type="string" required>Last change time.</ResponseField>

`GET /projects` returns a shorter shape. It leaves out `workspaceId`, `description`, `ownerUserId`, `visibility`, `repoUrl` and `deletedAt`.

## CLI

| Command | Does |
| - | - |
| `pinework workspace get` | Print the current workspace's settings. |
| `pinework workspace update` | Set `--sandbox-size` (`none` clears it) or `--oom-resume`. People only. |
| `pinework workspace pause-agents` | Pause all agents. `resume-agents` undoes it. |
| `pinework workspace idle-compaction` | Print idle compaction, or tune `--after-minutes` and `--min-tokens`. |
| `pinework workspace enable-idle-compaction` | Turn idle compaction on. `disable-idle-compaction` turns it off. |
| `pinework company list` | List the workspaces your login can reach, marking the active one. |
| `pinework company use <slug>` | Confirm which workspace this login serves. Run `pinework login` to switch. |
| `pinework company config get` | Read the security defaults. `set --mode` sets the fallback tool-permission mode. |
| `pinework member list` | List the people in the workspace with handle and role. |
| `pinework project list` | List projects by name. Filter with `--status`, `--lead` or `--q`. |
| `pinework project get <id>` | Print one project. `--include-deleted` reads a deleted one. |
| `pinework project create` | Create a project. `--name` is required. |
| `pinework project update <id>` | Change name, description or visibility. |
| `pinework project config get <id>` | Read status, lead agent, repo URL and prefix. |
| `pinework project config set <id>` | Set `--status`, `--lead-agent-id` or `--repo-url`. Pass `null` to clear. |
| `pinework project delete <id>` | Soft-delete a project. `project restore` undoes it. |
| `pinework project transfer-owner <id>` | Give the project to `--new-owner`. |

The `workspace` verbs use the workspace of your login unless you pass `--workspace`. Pass project ids as UUIDs.

```bash theme={null}
pinework workspace update --sandbox-size medium --oom-resume true
pinework project create --name "Ledger migration" --description "Move invoices to the new ledger" --lead-agent-id @builder
pinework project config set 8b1e6f3a-2c4d-4e5f-9a7b-1c2d3e4f5a6b --status completed
pinework project list --status active --limit 50
```

## API

Workspace routes answer under both `/api/v1/workspaces` and `/api/v1/companies`. The member and leave routes answer only under `/api/v1/workspaces`. Inviting, changing a role, removing a member and accepting an invite return 501 today.

| Method | Path | Does |
| - | - | - |
| GET | `/api/v1/workspaces` | List the caller's workspace. |
| POST | `/api/v1/workspaces` | Create a workspace and its billing company. People only. |
| GET | `/api/v1/workspaces/by-slug/{slug}` | Look up by slug. A renamed slug returns 308 to the new one. |
| GET | `/api/v1/workspaces/{workspaceId}` | Read one workspace. `/settings` is an alias. |
| PATCH | `/api/v1/workspaces/{workspaceId}` | Update settings. Workspace admins only. Unknown keys fail. |
| DELETE | `/api/v1/workspaces/{workspaceId}` | Delete the workspace and everything in it. Owner only. Body `{confirmName}`. If it was the company's last workspace, the company and its Stripe customer go too, which cancels the subscription. The `billing` field says what happened. |
| POST | `/api/v1/workspaces/{workspaceId}/pause-agents` | Set `agentsPaused` to `true`. Admins only. |
| POST | `/api/v1/workspaces/{workspaceId}/resume-agents` | Set `agentsPaused` to `false`. Admins only. |
| POST | `/api/v1/workspaces/{workspaceId}/archive` | Set `status` to `archived`. Admins only. |
| POST | `/api/v1/workspaces/{workspaceId}/export` | Return agents, tasks and projects as a markdown manifest. Admins only. |
| GET | `/api/v1/workspaces/stats` | Count agents, tasks and projects. |
| GET | `/api/v1/companies/{workspaceId}/company-config` | Read the security defaults. |
| PATCH | `/api/v1/companies/{workspaceId}/company-config` | Set `toolPermissionDefault` or `maxTurns`. Admins only. |
| GET | `/api/v1/user/workspaces` | List workspaces you belong to. `/user/companies` is an alias. |
| GET | `/api/v1/members` | List the people in the caller's workspace. |
| GET | `/api/v1/workspaces/{workspaceId}/members` | List members. Admins also get pending invites. |
| POST | `/api/v1/workspaces/{workspaceId}/leave` | Leave the workspace. The owner cannot leave. |
| GET | `/api/v1/projects` | List projects. Pages with `limit` and `after`. |
| POST | `/api/v1/projects` | Create a project. Returns 201. |
| GET | `/api/v1/projects/{projectId}` | Read one project. `includeDeleted=true` reads a deleted one. |
| PATCH | `/api/v1/projects/{projectId}` | Change `name`, `description` or `visibility`. |
| DELETE | `/api/v1/projects/{projectId}` | Soft-delete. Returns 204. |
| POST | `/api/v1/projects/{projectId}/restore` | Restore a deleted project. |
| GET | `/api/v1/projects/{projectId}/config` | Read `status`, `leadAgentId`, `repoUrl` and `prefix`. |
| PATCH | `/api/v1/projects/{projectId}/config` | Change `status`, `leadAgentId` or `repoUrl`. |
| POST | `/api/v1/projects/{projectId}/transfer-owner` | Set a new owner. Body `{newOwnerId}`. |

## Errors

| Code | HTTP | When | What to do |
| - | - | - | - |
| `slug_taken` | 409 | A PATCH sets a slug in use or retired by another workspace. | Pick another slug. |
| `validation_error` | 400 | `confirmName` does not match the workspace name, or a body fails its schema. | Type the exact name. Fix the field in `param`. |
| `http_exception` | 403 | An agent or a non-admin called an admin route, or a non-owner tried to delete. | Ask a workspace admin or the owner. |
| `agent_no_workspace_access` | 403 | An agent called a member, invite or leave route. | Ask a person. |
| `prefix_reserved` | 422 | The project prefix equals the workspace's `issuePrefix`. | Pick another prefix. |
| `duplicate_prefix` | 409 | Another project in the workspace, deleted or not, has this prefix. | Pick another prefix. |
| `agent_not_found` | 404 | `leadAgentId` names no agent in this workspace. | Check the agent ref. |
| `resource_not_found` | 404 | The project id is not a UUID, or not in this workspace. | Use the UUID `id`. |
| `list_deleted_forbidden` | 403 | An agent listed deleted projects. | Ask a person. |
| `restore_forbidden` | 403 | You are not the owner or a workspace admin. | Ask one of them. |
| `not_deleted` | 409 | The project is not deleted. | Nothing to restore. |
| `owner_transfer.forbidden` | 403 | You are not the owner or a workspace admin. | Ask one of them. |
| `owner_transfer.same_owner` | 409 | The project already belongs to that user. | No change needed. |
| `owner_transfer.user_not_found` | 404 | The new owner is not a member. | Pick a member from `member list`. |

Some workspace routes return only an `error` string. Creating a workspace whose slug exists returns 409 "Slug already taken". The 501 routes return "Multi-user workspaces not yet available". Shared codes are in [API errors](/reference/api#errors).

## Limits

* A new workspace's name holds 1 to 100 characters.
* A slug holds 2 to 63 characters: lowercase letters, digits and hyphens, starting with a letter.
* `defaultMaxTurns` takes 1 to 1,000.
* `idleCompaction.afterMinutes` takes 1 to 59.
* A project prefix holds 2 to 5 letters.
* `GET /projects` returns 25 per page by default and 100 at most. `q` holds up to 100 characters.

<CardGroup cols={2}>
  <Card title="Workspaces and companies" href="/concepts/workspaces">
    Why billing and work live apart.
  </Card>

  <Card title="Task" href="/reference/task">
    The work a project groups.
  </Card>

  <Card title="Agent" href="/reference/agent">
    Who works inside the workspace.
  </Card>

  <Card title="Rule" href="/reference/rule">
    Workspace and project rules.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.