Releases
List releases, create a release, and place builds into them — everything a CI/CD pipeline needs to line a build up behind a release without touching the UI.
Human-only: the API can create a release and move builds between release buckets, but it can never pass a manual test, complete a release, or delete anything. Those stay in the UI.
Scopes#
| Scope | Endpoint |
|---|---|
releases:read | GET /releases, GET /releases/{id} |
releases:create | POST /releases |
builds:update | PATCH /builds/{id} |
Existing API keys do not gain these scopes automatically — an admin enables them under Org Settings → API Keys.
Project resolution#
There is no default project. A project-scoped key operates on its own project. An org-scoped key must name the project with project_key (project UUID or project_code) on GET /releases and POST /releases, or gets a 400. On ID routes (GET /releases/{id}, PATCH /builds/{id}), the resource's project must simply belong to the key's org — a project-scoped key only ever sees its own project, and anything else is a 404, never a 403.
List releases#
GET /api/v1/releases?project_key=MOB&status=active&limit=20&offset=0
Returns a paginated list, newest first:
{
"data": [
{
"id": "b15c105a-8472-5a71-8d27-299e7d6fb6fe",
"name": "v2.4 - Spring Release",
"status": "active",
"target_date": "2026-09-30",
"completed_at": null,
"created_at": "2026-09-01T09:00:00Z",
"project_id": "6c7fe55a-42b2-5170-b88e-9db7b12d911f"
}
],
"pagination": { "total": 1, "limit": 20, "offset": 0, "has_more": false }
}
status accepts planned, active, or completed; anything else is a 400.
Get a release#
GET /api/v1/releases/{id}
Returns the release plus a build_count — the number of non-deleted builds currently sitting in it. Nothing else: no update, no status change, no delete.
Create a release#
POST /api/v1/releases
Content-Type: application/json
{
"project_key": "MOB",
"name": "v2.5 - Summer Release",
"description": "Optional",
"target_date": "2026-10-15",
"status": "planned"
}
statusacceptsplanned(default) oractive.completedis refused with 400VALIDATION_ERROR,details.reason: 'release_status_not_allowed'— creating a release that is already completed is completing one, and completing is human-only.- Release names are unique per project (case-insensitive). A duplicate is refused with 409
CONFLICT,details.reason: 'release_name_taken'. Releases are never auto-suffixed — unlike builds. target_datemust be today or a future date (same rule as the UI).- Success: 201 Created with a
Location: /api/v1/releases/{id}header. - Retries: send an
Idempotency-Keyheader — an identical retry returns 200 with the original release (no second insert); the same key with a different body returns 409.
Place a build in a release (or back to "No release")#
PATCH /api/v1/builds/{id}
Content-Type: application/json
{ "release_id": "b15c105a-8472-5a71-8d27-299e7d6fb6fe" }
The body is exactly one field — release_id (a release UUID, or null to move the build back to the project's "No release" bucket). Any other field is a 400, and no other build field is editable through the API.
The move behaves exactly like the UI's Place in release:
- The build's test runs follow it in the database — nothing else to update.
- A name collision in the target release suffixes the build's name instead of refusing: the response
namemay come back as"v2.4.1 (2)". - A completed release refuses the move in either direction — in or out — with 409
CONFLICT,details.reason: 'release_completed'. - A
release_idfrom another project is a 404.
Response (200):
{ "id": "aa7cd7d3-c5e2-56b0-b658-41d41fbc33a2", "name": "v2.4.1", "release_id": "b15c105a-8472-5a71-8d27-299e7d6fb6fe", "status": "active" }
List builds by release bucket#
GET /api/v1/builds?release=none # only release-less builds
GET /api/v1/builds?release={releaseId} # only that release's builds
The release filter accepts none (the project's "No release" bucket) or a release UUID — which must belong to the resolved project, else 404. GET /builds also accepts project_key as an alternative to project_id; sending both with different values is a 400.
Errors#
All errors use the standard shape { "error": { "code", "message", "details?" } }. See Error Reference.
| Status | When |
|---|---|
| 400 | Missing project_key on an org-scoped key; status: "completed"; past target_date; extra body fields; unknown status filter |
| 403 | Key lacks releases:read / releases:create / builds:update |
| 404 | Resource in another project/org; release naming a release outside the resolved project |
| 409 | Duplicate release name (release_name_taken); completed release (release_completed); Idempotency-Key reuse |