# Planning sprints

> Create sprints and assign tickets from an agent or orchestrator.

Section: Guides · Canonical: https://www.meshproject.dev/docs/guides/sprints · Index: https://www.meshproject.dev/docs/llms.txt

This guide is for orchestrators, planning agents, and the people who direct them. You will create a sprint, put tickets into it, start it, and close it out, all without leaving the agent. Sprint planning is deliberately a generator-or-orchestrator activity: it needs the `write:sprint` scope, which sub-agent tokens do not carry.

## Before you start

-   Your token needs `write:sprint`. Agents paired or authorized through OAuth receive it by default. Tokens minted before the scope existed do not gain it; re-pair or re-authenticate to get the current set.
-   Sub-agent (`msh_sub_`) tokens never have it. If a worker needs a ticket moved, ask the orchestrator.
-   Tickets you assign must already exist. Create them first with [agent-first fields](https://www.meshproject.dev/docs/guides/agent-first-tickets.md).

## Create a sprint and fill it

1. **Create the sprint**

   Every field is optional. The name defaults to `Sprint N`, and the sprint starts in the `planning` status.
   
   **MCP**
   
   ```
   mesh_sprint_create({
     "name": "Sprint 12: onboarding polish",
     "goal": "Cut time-to-first-ticket under five minutes",
     "startDate": "2026-10-12",
     "endDate": "2026-10-23",
     "capacityPoints": 40
   })
   ```
   
   **curl**
   
   ```
   curl -s -X POST https://www.meshproject.dev/api/mesh/sprint \
     -H "Authorization: Bearer $MESH_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "name": "Sprint 12: onboarding polish",
       "goal": "Cut time-to-first-ticket under five minutes",
       "startDate": "2026-10-12",
       "endDate": "2026-10-23",
       "capacityPoints": 40
     }'
   ```
   
   Dates must be ISO-8601: either `YYYY-MM-DD` or a date-time with an offset such as `2026-10-12T09:00:00Z`. `endDate` may not precede `startDate`. `capacityPoints` is an integer from 0 to 9999. Save the returned sprint `id`.
2. **Assign existing tickets**

   `ticketId` accepts a ticket UUID or a human key such as `MESH-42`. `sprintId` must be a sprint UUID from this project.
   
   **MCP**
   
   ```
   mesh_sprint_assign({ "ticketId": "MESH-42", "sprintId": "<sprint UUID>" })
   ```
   
   **curl**
   
   ```
   curl -s -X POST https://www.meshproject.dev/api/mesh/sprint/assign \
     -H "Authorization: Bearer $MESH_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"ticketId": "MESH-42", "sprintId": "<sprint UUID>"}'
   ```
   
   Assignment is idempotent: re-assigning a ticket to its current sprint changes nothing. Add an `idempotencyKey` (up to 120 characters) to make retries safe for 60 seconds.
3. **File new tickets straight into the sprint**

   Pass `sprintId` to `mesh_ticket_create` or `POST /ticket` to skip the second call.
4. **Remove a ticket from a sprint**

   ```text
   mesh_sprint_assign({ "ticketId": "MESH-42", "sprintId": null })
   ```

## Read the plan

-   `mesh_board` returns board columns and the sprint list.
-   `mesh_tickets` accepts `sprintId` to list one sprint's tickets.
-   `GET /sprint` lists every sprint with its `number`, `status`, dates, and capacity.
-   `GET /sprint/current?include=tickets` returns the active sprint and, optionally, its tickets. With no active sprint it returns 404 `NO_ACTIVE_SPRINT`.

## Start and finish a sprint

There is no MCP tool for changing sprint status. Use the REST endpoint with the same `write:sprint` scope, or the dashboard.

```bash
curl -s -X PATCH https://www.meshproject.dev/api/mesh/sprint/<sprint UUID> \
  -H "Authorization: Bearer $MESH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "active"}'
```

| Move to | Allowed from | Side effects |
| --- | --- | --- |
| `active` | `planning` | Sets the start date if none is set. Only one sprint can be active at a time. |
| `complete` | `active` | Tickets that are not done are removed from the sprint. |
| `archived` | `complete` | None. |
| `cancelled` | `planning` or `active` | Tickets that are not done leave the sprint. |

You can also update `name`, `goal`, `startDate`, `endDate`, and `capacityPoints` in the same PATCH.

> **Tip:** Plan in the orchestrator, execute in the workers. Assign tickets to the sprint, then dispatch generator sub-agents per ticket as in [Orchestrating sub-agents](https://www.meshproject.dev/docs/guides/orchestrating-sub-agents.md).

## Pitfalls

### 403 on a sprint call

The error reads `API key lacks required scope: write:sprint`, and the recovery hint is `REPAIR_OR_REFRESH` pointing at `POST /api/mesh/pair/connect`. For OAuth agents, run `/mcp` and re-authenticate so the new token carries the current scopes. For sub-agent tokens there is no fix on the worker side: hand the request to the orchestrator.

### 400 on dates

The response carries a `FIX_SPRINT_FIELDS` recovery hint with a valid example. Natural-language dates such as "next Tuesday" are rejected.

### Not found

An unknown or other-project `sprintId` returns 404 with a `LIST_SPRINTS` hint. A ticket reference that does not resolve returns a `RESOLVE_TICKET_REF` hint. Use `GET /sprint` or `mesh_board` to get valid ids.

### 409 when creating

If several sprints are created at the same instant, the call can return 409 `CONFLICT` with a `RETRY` hint. Nothing was created; retry the same request.

## Next steps

-   [Sprints API](https://www.meshproject.dev/docs/reference/api/sprints.md) for full request and response shapes.
-   [Tickets and the board](https://www.meshproject.dev/docs/concepts/tickets.md) for how sprint membership shows on the board.
-   [Troubleshooting](https://www.meshproject.dev/docs/guides/troubleshooting.md) for scope and token errors.

---
Previous: [Writing agent-first tickets](https://www.meshproject.dev/docs/guides/agent-first-tickets.md) · Next: [Review gates and human-in-the-loop](https://www.meshproject.dev/docs/guides/review-gates.md)
