# Project

A project: the unit customers are billed against. Carries a workflow mode that decides whether work is planned in sprints or on a continuous board.

## Used by
- GET /api/v2/projects: List projects (ProjectList)
- POST /api/v2/projects: Create a project (Project)
- GET /api/v2/projects/{project}: Read a project (Project)
- PATCH /api/v2/projects/{project}: Update a project (Project)
- POST /api/v2/projects/{project}/actions/restore: Restore a deleted project (Project)

## Project fields
- id (integer, required): Unique identifier.
- title (string ≤ 255, required): Project title as shown in the UI.
- full_title (string, required): Application name plus title. Never carries the project number, even when the organisation shows numbers in plnnrly.
- description (html or null, required, detail only): Free-text description. HTML from the rich editor, sanitised.
- status (string, required): Lifecycle status. Setting it to invoiced finishes every task on every budget of the project.
- workflow_mode (string or null, required): Whether work is planned in sprints or on a simple board.
- start_date (date (YYYY-MM-DD) or null, required): First day of the project.
- end_date (date (YYYY-MM-DD) or null, required): Last day, `null` if open-ended.
- is_billable (boolean, required): Whether time on this project is billable.
- is_continuous (boolean, required): Whether the project has no planned end.
- last_activity (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required): Most recent write anywhere in the project.
- created_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Creation timestamp.
- updated_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Last-write timestamp; use for incremental sync.
- deleted_at (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required): Soft-delete timestamp, `null` while live.
- customer_id (integer, required): Owning customer.
- sub_customer_id (integer or null, required): Optional sub-customer.
- project_manager_id (integer or null, required): Responsible project manager.
- account_manager_id (integer or null, required, server-assigned): Responsible account manager. Set to the creating user and not writable.
- application_id (integer or null, required): Linked application. Writable only while the organisation has the Infrastructure feature: sending a non-`null` value without it is a `422` `prohibited`. Null clears the link regardless.
- workflow_id (integer or null, required): Workflow whose statuses this project's work items move through.
- web_url (string, required): Absolute URL that opens this project's work in plnnrly, on the organisation's own hostname. It is a stable link, safe to store: it redirects to the first of the project's backlog, all-work list or board the signed-in user may open, and to the project page when none of them applies. Read-only.
- customer (Ref or CustomerSummary, required): Owning customer.
- sub_customer (Ref or CustomerSummary or null, required): Optional sub-customer.
- project_manager (Ref or UserSummary or null, required): Responsible project manager.
- account_manager (Ref or UserSummary or null, required): Responsible account manager.
- sprints_count (integer ≥ 0, required): Number of sprints.
- budgets_count (integer ≥ 0, required): Number of budgets.
- tags_count (integer ≥ 0, required): Number of tags.
- tasks_count (integer ≥ 0, required): Number of kanban tasks on this project that the caller may see.
- workflow_mode_options (array of string, required, detail only): Workflow modes this project may switch to. Empty until a workflow is linked.
- links (ProjectLinks, required)
  - self (url, required): The canonical URI of this record.
  - tasks (url, required): Kanban tasks on this project.
  - budgets (url, required): Budgets on this project.
  - sprints (url, required): Sprints on this project.
- tags (array of TagSummary ≤ 50, optional, only with include): Tags on this project. A project takes at most 50 tags, so the include lists them all. `tag_ids` replaces the whole set. Wrapped in `{ data, meta }`; `meta.truncated` says whether the cap was hit and `meta.count` is the full count.
  - id (integer, required): Unique identifier.
  - title (string ≤ 255, required): Tag title as shown in the UI.
- logged_minutes (integer or null, optional, on request): Sum of the worklog minutes on this project that the caller may see. Worklogs are per-user unless the caller holds `times::update_others` or `times::delete_others`. Null when the caller lacks `hours::read` or a token covering `api:read:times`.

Only on `?view=detail`: description, workflow_mode_options

## Variants

### ProjectList

What a collection returns, and what `?view=summary` returns on a single record.

31 fields.
Omits: description, workflow_mode_options

### ProjectSummary

The embedded form: what you get when another resource carries this one and you did not `include` it.

3 fields.
Omits: full_title, description, status, workflow_mode, start_date, end_date, is_billable, is_continuous, last_activity, created_at, updated_at, deleted_at, sub_customer_id, project_manager_id, account_manager_id, application_id, workflow_id, web_url, customer, sub_customer, project_manager, account_manager, tags, sprints_count, budgets_count, tags_count, tasks_count, logged_minutes, workflow_mode_options, links

### ProjectLinks

The `links` object on this resource.

Keys: self, tasks, budgets, sprints

## Example
```json
{
  "id": 1,
  "title": "Portal rebuild",
  "full_title": "…",
  "description": "<p>…</p>",
  "status": "active",
  "workflow_mode": "sprints",
  "start_date": "2026-03-04",
  "end_date": "2026-03-04",
  "is_billable": true,
  "is_continuous": false,
  "last_activity": "2026-03-04T09:15:00Z",
  "created_at": "2026-03-04T09:15:00Z",
  "updated_at": "2026-03-04T09:15:00Z",
  "deleted_at": "2026-03-04T09:15:00Z",
  "customer_id": 1,
  "sub_customer_id": 1,
  "project_manager_id": 1,
  "account_manager_id": 1,
  "application_id": 1,
  "workflow_id": 1,
  "web_url": "…",
  "customer": {
    "id": 1
  },
  "sub_customer": null,
  "project_manager": null,
  "account_manager": null,
  "tags": {
    "data": [
      {
        "…": "2 fields of TagSummary"
      }
    ],
    "meta": {
      "truncated": false,
      "…": "1 optional field left out"
    }
  },
  "sprints_count": 1,
  "budgets_count": 1,
  "tags_count": 1,
  "tasks_count": 1,
  "logged_minutes": 1,
  "workflow_mode_options": [
    "sprints"
  ],
  "links": {
    "self": "https://<your-url>/api/v2/work-items/1",
    "tasks": "https://<your-url>/api/v2/work-items/1",
    "budgets": "https://<your-url>/api/v2/work-items/1",
    "sprints": "https://<your-url>/api/v2/work-items/1"
  }
}
```
