Milestones API
Milestones are project-scoped planning units that group test runs and track release progress. Each milestone has a server-assigned ULID, a status lifecycle (upcoming → active → completed | archived), optional date fields, and an optional parent for 1-level hierarchies.
Create
Section titled “Create”/api/v1/projects/{projectId}/milestones{projectId} is the project code (for example ACME).
Request body
Section titled “Request body”| Field | Required | Notes |
|---|---|---|
name | yes | Non-empty string, max 255 characters. Must be unique among live milestones in the project |
description | no | Free-text description, max 2000 characters |
status | no | One of upcoming, active, completed, archived. Defaults to upcoming |
startAt | no | Unix epoch milliseconds. Must be ≤ dueAt when both are provided |
dueAt | no | Unix epoch milliseconds |
forecastAt | no | Unix epoch milliseconds |
parentId | no | ULID of the parent milestone in the same project (1 level only) |
Client-supplied ulid fields are rejected.
Duplicate live name within the project returns 409 conflict. Date ordering violation (dueAt < startAt) or missing required fields return 422 validation_failed.
Response
Section titled “Response”201 with the created milestone object. See Response fields.
Example
Section titled “Example”curl -sS -X POST \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"v2.0 Release","status":"upcoming","startAt":1700000000000,"dueAt":1700600000000}' \ "https://probara.net/api/v1/projects/ACME/milestones"/api/v1/projects/{projectId}/milestonesReturns the live (non-deleted) milestones for the project, ordered by ULID ascending. Page-based pagination.
Query parameters
Section titled “Query parameters”| Parameter | Notes |
|---|---|
page | Page number (1-based, default 1) |
pageSize | Items per page (default 50, max 200) |
q | Case-insensitive substring search on milestone name |
status | Filter by exact status: upcoming, active, completed, or archived |
view | Response shape: flat (default) or tree. See List views |
List views
Section titled “List views”The view parameter selects the response shape. It is additive — omitting it (or passing flat) returns the exact same response as before.
view | items | total | Pagination unit |
|---|---|---|---|
flat (default) | Every live milestone (parents and children as siblings) | Count of all live milestones | Individual milestone |
tree | Parent milestones only (those with no parent), each carrying a children array of its live direct children | Count of parent milestones only (children are never counted) | Parent milestone |
In tree mode:
itemscontains only parents. Each parent item is the standard milestone object plus achildrenarray.- Each entry in
childrenis a plain milestone object (the same response shape); children never carry their ownchildrenkey (the hierarchy is one level). qandstatusfilter parents only. A parent matched by the filter shows all of its live children regardless of the children’s own status.totalis the parent count, so the pager (X–Y of Z) counts parent sections. A parent and its children are never split across pages.- A parent with no children has
children: [].
Response
Section titled “Response”200 with { items, page, pageSize, total }. In flat mode each item follows the response shape; in tree mode each item is that shape extended with a children array (see List views).
Example
Section titled “Example”# Flat (default)curl -sS \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/projects/ACME/milestones?status=active"
# Grouped parent sections with nested childrencurl -sS \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/projects/ACME/milestones?view=tree"Example tree response (truncated):
{ "items": [ { "ulid": "01JPARENT...", "name": "v2.4 Release", "status": "active", "children": [{ "ulid": "01JCHILD...", "name": "Auth 2.4", "status": "active" }] } ], "page": 1, "pageSize": 50, "total": 1}/api/v1/milestones/{milestoneUlid}Returns a single live milestone scoped to the active organization, including real-time progress aggregates.
Returns 404 not_found when the milestone is outside the active organization or has been deleted.
Example
Section titled “Example”curl -sS \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/milestones/01JXXXXXXXXXXXXXXXXXXXXXXXXX"Update
Section titled “Update”/api/v1/milestones/{milestoneUlid}Partial update. At least one field must be provided; an empty body returns 422 validation_failed.
Request body
Section titled “Request body”| Field | Required | Notes |
|---|---|---|
name | no | Non-empty string, max 255 characters |
description | no | Max 2000 characters, or null to clear |
status | no | Target status. Must follow valid status transitions (see Status lifecycle) |
startAt | no | Unix epoch milliseconds, or null to clear |
dueAt | no | Unix epoch milliseconds. Must be ≥ startAt when both are set |
forecastAt | no | Unix epoch milliseconds, or null to clear |
parentId | no | ULID of the new parent in the same project, or null to detach |
Invalid status transitions return 422 validation_failed. Duplicate live name returns 409 conflict. Returns 404 not_found when the milestone is outside the active organization.
Example
Section titled “Example”curl -sS -X PATCH \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"status":"active","dueAt":1700700000000}' \ "https://probara.net/api/v1/milestones/01JXXXXXXXXXXXXXXXXXXXXXXXXX"Delete
Section titled “Delete”/api/v1/milestones/{milestoneUlid}Soft-deletes the milestone. Returns 204 on success. Subsequent GET returns 404 not_found and the milestone is excluded from list results.
Deletion atomically unassigns all linked test runs, test cases, and child milestones.
Returns 404 not_found when the milestone is outside the active organization.
Example
Section titled “Example”curl -sS -X DELETE \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/milestones/01JXXXXXXXXXXXXXXXXXXXXXXXXX"Status lifecycle
Section titled “Status lifecycle”Milestones follow a directed state machine. Only the transitions listed below are accepted; all others return 422 validation_failed.
| From | Allowed transitions |
|---|---|
upcoming | active, archived |
active | completed, archived |
completed | active (reopen), archived |
archived | — (terminal) |
Transitioning to completed automatically sets completedAt to the current timestamp. Transitioning away from completed clears completedAt.
Response fields
Section titled “Response fields”All mutating endpoints (POST, PATCH) and GET /api/v1/milestones/{milestoneUlid} return a single milestone object.
| Field | Type | Notes |
|---|---|---|
ulid | string (ULID) | Server-assigned unique identifier |
projectId | string | The project code this milestone belongs to |
name | string | Display name |
description | string | null | Optional description |
status | string | Current status: upcoming, active, completed, or archived |
startAt | number | null | Unix epoch milliseconds |
dueAt | number | null | Unix epoch milliseconds |
forecastAt | number | null | Unix epoch milliseconds |
completedAt | number | null | Set automatically when status transitions to completed |
parentId | string (ULID) | null | Parent milestone ULID, or null for top-level |
progress.totalRuns | number | Total test runs linked to this milestone |
progress.closedRuns | number | Closed (finished) test runs |
progress.percentComplete | number | closedRuns / totalRuns × 100, rounded; 0 when no runs |
progress.passRate | number | passed / totalResults × 100, rounded; 0 when no results |
progress.passed | number | Test case results that passed across all linked runs |
progress.failed | number | Test case results that failed |
progress.blocked | number | Test case results that were blocked |
progress.skipped | number | Test case results that were skipped |
progress.untested | number | Test cases not yet executed |
author | object | null | User who created the milestone, or null for API-token-created and legacy milestones |
author.kind | string | Always "user" when present |
author.ulid | string (ULID) | User identifier |
author.displayName | string | Full name or email of the author |
author.avatarKey | string | null | Storage key for the author’s avatar image |
childCount | number | Number of direct child milestones |
testCaseCount | number | Number of test cases linked to this milestone |
createdAt | number | Unix epoch milliseconds |
updatedAt | number | Unix epoch milliseconds |
Audit events
Section titled “Audit events”Milestone mutations emit the following events on the organization activity feed:
| Action | Trigger |
|---|---|
milestone.created | Milestone created |
milestone.updated | Any field updated via PATCH |
milestone.status_changed | Status field changed via PATCH |
milestone.deleted | Milestone soft-deleted |
See Audit events.