> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tylon.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Releases and inbox

> Read what a Tylon release ships and what is waiting for a person in the inbox — branch suggestions, refused promotions, grouped errors.

## Releases

<ParamField path="GET /v1/releases" type="endpoint" />

The releases of a product, open and completed.

<ParamField query="projectId" type="string">
  Which product. Optional when the board has one project.
</ParamField>

```json theme={null}
[
  {
    "id": "84391…",
    "name": "2026.08",
    "status": "open",
    "frozenAt": null,
    "taskCount": 6,
    "createdByName": "Marina Duarte",
    "completedAt": null,
    "tags": []
  }
]
```

<ResponseField name="status" type="string">
  `open` or `completed`, lowercase. A frozen release is still `open` — freezing
  is not a third status, it is a release that has taken a branch of its own and
  completes the same way.
</ResponseField>

<ResponseField name="frozenAt" type="string | null">
  When what this release ships stopped changing. A frozen release has had its branch cut, and a card joins it after that only through a deliberate pick — which is the whole point of the moment.
</ResponseField>

This is the reading worth putting on a status page: a frozen release with a date is a promise somebody can act on, and an open one is not yet.

## Inbox

<ParamField path="GET /v1/inbox" type="endpoint" />

What is waiting for a person to decide.

<ParamField query="projectId" type="string">
  Which product. Optional when the board has one project.
</ParamField>

Three kinds turn up here:

<ResponseField name="branch_suggestion" type="kind">
  A branch created by hand whose name matches the team's convention. Tylon offers the link and only offers it — linking the wrong branch to a card on a guess is worse than not noticing it.
</ResponseField>

<ResponseField name="promotion_blocked" type="kind">
  A card whose move was refused, with the forge's own words: conflicts, red checks, nothing to merge.
</ResponseField>

<ResponseField name="error_reported" type="kind">
  Something a monitor grouped in production. It waits here rather than landing on the board — a backlog that fills itself is a backlog nobody reads.
</ResponseField>

An inbox that is not empty is the honest thing to alert on. It is, by construction, the list of things a machine could not decide.

## Creating and shaping a release

<ParamField path="POST /v1/releases" type="endpoint" />

<ParamField body="name" type="string" required>
  1 to 80 characters — `2026.09`, `Checkout rewrite`.
</ParamField>

<ParamField query="projectId" type="string">
  Which product. A release delivers one, so this is required when the board
  holds several.
</ParamField>

<ParamField path="PUT /v1/releases/{id}/cards" type="endpoint" />

<ParamField body="taskNumbers" type="integer[]" required>
  The whole scope, not additions — up to 500. `PUT` because a caller computing
  the scope somewhere else wants to say what it is now, rather than diff it
  against whatever is there.
</ParamField>

Answers `204`.

<Note>
  A flow has to have releases turned on. Otherwise this is `400`: *"This flow
  does not work with releases. Turn it on in the flow settings first."* A team
  shipping continuously never meets any of this.
</Note>

## Freezing

<ParamField path="POST /v1/releases/{id}/freeze" type="endpoint" />

Maintainer. Cuts the branch, and from that second what this ships stops
changing — which is what makes "what is in this release" a question with an
answer.

<ParamField path="DELETE /v1/releases/{id}/freeze" type="endpoint" />

Both answer with the release.

## Picking into a frozen one

<ParamField path="POST /v1/releases/{id}/pick" type="endpoint" />

<ParamField body="taskNumber" type="integer" required />

The only door in after a freeze, one card at a time. That is the point rather
than a limitation: a release you can add to in bulk after freezing is a
release that was never frozen.

## Promoting

<ParamField path="POST /v1/releases/{id}/promote" type="endpoint" />

<ParamField body="statusId" type="string" required>
  The stage to move the release's cards into.
</ParamField>

<ParamField body="taskNumbers" type="integer[]">
  Only these. Left out, the whole release goes.
</ParamField>

Answers `200` with an outcome rather than throwing. A release of twelve cards
where nine promoted and three were refused is news, not an error — the
refusals are in the body, each naming its card and the repository that said
no, and the nine that landed stay landed.

## Completing

<ParamField path="POST /v1/releases/{id}/complete" type="endpoint" />

Maintainer. Tags and closes it.

<ParamField body="versions" type="object[]" required>
  One entry per repository: `{ "repositoryId": "…", "tagName": "v2.4.0" }`.
  Each repository gets its own tag and its own release on the provider,
  because each keeps its own versioning — there is no single number for a
  delivery that spans three of them.
</ParamField>

<ParamField path="DELETE /v1/releases/{id}" type="endpoint" />

Maintainer. `204`.

## The inbox, cleared

<ParamField path="POST /v1/inbox/{itemId}/adopt" type="endpoint" />

Take a branch somebody cut in a terminal and make it the card's.

<ParamField query="card" type="integer">
  Which card. Optional — the Inbox already guessed from the naming
  convention, and this is how you overrule the guess.
</ParamField>

<ParamField path="DELETE /v1/inbox/{itemId}" type="endpoint" />

Put the item away without acting on it. Both answer `204`.
