Milestone Operations (v2)¶
GraphQL operations for managing a goal's milestones. There is no v1 equivalent — milestones are new in the v2 API. See the v2 guide for archive/delete semantics across v2 entities, and Goal Operations for creating milestones alongside a goal.
API Reference¶
MilestoneOperations
¶
Bases: GraphQLOperations, MilestoneOperationsMixin
Class to handle v2 (GraphQL) operations related to goal milestones.
Note
The GraphQL API has no root milestone(id) query, so a milestone is
read through its parent goal. update() and complete() therefore
take goal_id: they check that milestone_id belongs to that goal
before writing anything, then re-read the milestone through it.
Methods:
-
list–List the milestones of a goal.
-
create–Create a new milestone on a goal.
-
update–Update an existing milestone.
-
complete–Mark a milestone as completed.
-
delete–Delete (soft-delete) a milestone.
Methods:¶
list
¶
list(goal_id: int) -> list[Milestone]
List the milestones of a goal.
Parameters:
-
goal_id(int) –The ID of the goal.
Returns:
-
list[Milestone]–A list of
Milestonemodel instances, ordered by due date.
create
¶
create(goal_id: int, title: str, due_date: TimeInput, completed: bool = False) -> Milestone
Create a new milestone on a goal.
Parameters:
-
goal_id(int) –The ID of the goal (rock) to attach the milestone to.
-
title(str) –The title of the milestone.
-
due_date(TimeInput) –The due date, as a
datetime,date, or unix timestamp (seconds). -
completed(bool, default:False) –Whether the milestone starts out completed.
Returns:
-
Milestone–The newly created
Milestone.
update
¶
update(milestone_id: int, *, goal_id: int, title: str | None = None, due_date: TimeInput | None = None, completed: bool | None = None) -> Milestone
Update an existing milestone.
Parameters:
-
milestone_id(int) –The ID of the milestone to update.
-
goal_id(int) –The ID of the milestone's parent goal (see the class
Note). -
title(str | None, default:None) –New title for the milestone.
-
due_date(TimeInput | None, default:None) –New due date, as a
datetime,date, or unix timestamp (seconds). -
completed(bool | None, default:None) –New completed state.
Returns:
-
Milestone–The updated
Milestone.
Raises:
-
ValueError–If no update fields are provided.
complete
¶
complete(milestone_id: int, *, goal_id: int) -> Milestone
Mark a milestone as completed.
Parameters:
-
milestone_id(int) –The ID of the milestone to complete.
-
goal_id(int) –The ID of the milestone's parent goal (see the class
Note).
Returns:
-
Milestone–The updated
Milestone.
delete
¶
Async Version¶
The async version AsyncMilestoneOperations provides the same methods as above, but with async/await support:
AsyncMilestoneOperations
¶
Async class to handle v2 (GraphQL) operations related to goal milestones.
Note
See MilestoneOperations for why update() and complete() require
goal_id.
Async Usage
All methods have the same parameters and return types as their sync counterparts. Simply add await before each method call.
Usage Examples¶
from datetime import date
from bloomy import Client
with Client(api_key="your-api-key") as client:
# Create a milestone on an existing goal
milestone = client.v2.milestone.create(
goal_id=5265048,
title="Draft spec",
due_date=date(2026, 1, 1),
)
# List a goal's milestones
milestones = client.v2.milestone.list(goal_id=5265048)
# Update it (goal_id is keyword-only: verified before anything is
# written, and needed again to re-read the milestone afterward)
updated = client.v2.milestone.update(
milestone.id, goal_id=milestone.goal_id, title="Draft spec v2"
)
# Mark it complete
completed = client.v2.milestone.complete(
milestone.id, goal_id=milestone.goal_id
)
# Delete it (no goal_id needed, and there is no restore)
client.v2.milestone.delete(milestone.id)
import asyncio
from bloomy import AsyncClient
from datetime import date
async def main():
async with AsyncClient(api_key="your-api-key") as client:
# Create a milestone on an existing goal
milestone = await client.v2.milestone.create(
goal_id=5265048,
title="Draft spec",
due_date=date(2026, 1, 1),
)
# Mark it complete
completed = await client.v2.milestone.complete(
milestone.id, goal_id=milestone.goal_id
)
# Delete it
await client.v2.milestone.delete(milestone.id)
asyncio.run(main())
Available Methods¶
| Method | Description | Parameters | Returns |
|---|---|---|---|
list() |
List a goal's milestones, ordered by due date | goal_id |
list[Milestone] |
create() |
Create a milestone on a goal | goal_id, title, due_date, completed |
Milestone |
update() |
Update a milestone | milestone_id, goal_id (keyword-only), title, due_date, completed |
Milestone |
complete() |
Mark a milestone as completed | milestone_id, goal_id (keyword-only) |
Milestone |
delete() |
Delete a milestone | milestone_id |
None |
Why update() and complete() need goal_id
There is no root GraphQL query to read a single milestone by id — a
milestone can only be read through its parent goal
(goal(id){ milestones }). update() and complete() need goal_id
(keyword-only) in addition to milestone_id: both verify that
milestone_id actually belongs to goal_id before sending any edit,
so a wrong goal_id fails without writing anything, and then need
goal_id again to re-read the milestone afterward. delete() needs
neither, since DeleteMilestone does not return a Milestone.
No archive/restore — only delete
Milestones do not archive; delete() permanently removes one
(soft-deleted server-side, but with no SDK-exposed way to bring it back).
Update requirements
At least one field must be provided to update(), or it raises ValueError.