# Sprint

A sprint: a time-boxed slice of a project's backlog. A sprint has three states and they are derived, not stored (`completed_at` set means completed, otherwise `is_active` `true` means active, otherwise upcoming), so there is no `state` field to read and no `filter[state]` to send. Starting and completing a sprint are not field writes: they live at POST `sprints/{sprint}/actions/start` and .../actions/complete, because either can fail for a state reason rather than an invalid-value reason.

## Used by
- GET /api/v2/sprints: List sprints (SprintList)
- POST /api/v2/sprints: Create a sprint (Sprint)
- GET /api/v2/sprints/{sprint}: Read a sprint (Sprint)
- PATCH /api/v2/sprints/{sprint}: Update a sprint (Sprint)
- POST /api/v2/sprints/{sprint}/actions/complete: Complete a sprint (Sprint)
- POST /api/v2/sprints/{sprint}/actions/start: Start a sprint (Sprint)

## Sprint fields
- id (integer, required): Unique identifier.
- project_id (integer, required): The project this sprint belongs to. A sprint cannot be moved between projects, so this is accepted on create only.
- name (string ≤ 255, required): Sprint name as shown in plnnrly, e.g. `Sprint 14`.
- description (string or null, required, multiline): Free-text sprint goal. Plain text, not markup.
- start_date (date (YYYY-MM-DD) or null, required): Planned first day of the sprint. Independent of `started_at`: planning a date does not start the sprint.
- end_date (date (YYYY-MM-DD) or null, required): Planned last day of the sprint. Independent of `completed_at`.
- started_at (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required, server-assigned): When the sprint was actually started. Written only by POST `sprints/{sprint}/actions/start`; `null` on an upcoming sprint.
- completed_at (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required, server-assigned): When the sprint was actually completed. Written only by POST `sprints/{sprint}/actions/complete`; `null` until then. Non-`null` is what makes the derived state `completed`.
- is_active (boolean, required, server-assigned): Whether this is the project's running sprint. Set by actions/start and cleared by actions/complete, never by PATCH. At most one sprint per project is active at a time.
- order (integer, required, server-assigned): Display position within the project. Assigned on create as the last position in that project's sprint order, changed only by reordering in plnnrly, and not writable.
- work_items_count (integer ≥ 0, required): Number of work items currently on this sprint, resolved and unresolved alike.
- 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.
- project (Ref or ProjectSummary, required): The project this sprint belongs to. Expanding it additionally requires the projects prefix's read grant and a token scope covering it.
- links (SprintLinks, required)
  - self (url, required): The canonical URI of this record.
  - work_items (url, required): Work items on this sprint.

## Variants

### SprintList

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

15 fields.
Identical to the detail object above.

### SprintSummary

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

3 fields.
Omits: project_id, description, start_date, end_date, started_at, completed_at, order, work_items_count, created_at, updated_at, project, links

### SprintLinks

The `links` object on this resource.

Keys: self, work_items

## Example
```json
{
  "id": 1,
  "project_id": 1,
  "name": "Sprint 14",
  "description": "Finish the invoicing export.",
  "start_date": "2026-03-02",
  "end_date": "2026-03-13",
  "started_at": "2026-03-04T09:15:00Z",
  "completed_at": "2026-03-04T09:15:00Z",
  "is_active": false,
  "order": 1,
  "work_items_count": 1,
  "created_at": "2026-03-04T09:15:00Z",
  "updated_at": "2026-03-04T09:15:00Z",
  "project": {
    "id": 1
  },
  "links": {
    "self": "https://<your-url>/api/v2/work-items/1",
    "work_items": "https://<your-url>/api/v2/work-items/1"
  }
}
```
