A run can stop twice for a person. First before it builds, so you can check its spec and plan. Then after it builds, if its change is held before it lands. This page covers both, and says at each step who may act.

Every path below starts with `https://api.syndai.ai/api/v1/external/coding`.

## Who may act

Some steps take an API key. Others need a person who is signed in at [app.syndai.ai](https://app.syndai.ai).

| step | API key | signed-in person |
|---|---|---|
| read the spec, plan, criteria and held diff | yes | yes |
| confirm, edit, skip or extend the spec review | no: `401` | the person who started the run, or an owner or admin |
| ask for changes to the spec or plan | no: `401` | the same |
| co-sign criteria that were approved on their own | no: `401` | a person allowed to review the run |
| go live with a change that is not held | yes | an owner or admin |
| retry or close a held landing | yes | an owner or admin |
| go live anyway with a held change | no: `403 HUMAN_APPROVAL_REQUIRED` | an owner or admin |
| revert a change that went live | yes | an owner or admin |

When a key gets `HUMAN_APPROVAL_REQUIRED`, the error has a `review_url`. Send that link to the owner. Nothing changed. See [Actions only a person can take](/docs/api/authentication#actions-only-a-person-can-take).

## 1. The spec review

Before it builds, a run drafts a spec: a brief, a goal and a list of acceptance criteria. The run's `spec_review` policy says if it waits for you:

| `spec_review` | what the run does |
|---|---|
| not set | builds at once. A large plan waits like `timed`. |
| `auto` | never waits |
| `timed` | waits for you, then builds on its own if no one acts in time |
| `hold` | waits until a person confirms. If no one does, the run ends without building. |

A run with no one to answer never waits. That covers scheduled runs and `unattended` runs. See [Policy](/docs/api/policy#spec_review).

When the spec is ready, Syndai sends `coding.run.spec_review_ready`. Read it from `GET /runs/{id}`:

- `spec_review.round` is the round you are looking at. The review calls below take it as `base_round`.
- `criteria` lists each criterion with its `criterion_key`, `text` and `status`.
- `spec_review_wait_until` is when a `timed` review builds on its own.

A person then does one of these:

| call | what it does |
|---|---|
| `POST /runs/{id}/criteria/ratify?base_round=N` | confirms the criteria, signs the spec and starts the build now |
| `POST /runs/{id}/criteria/{key}/edit` | rewrites one criterion. A `timed` wait starts again. |
| `POST /runs/{id}/spec/request-changes` | sends notes. The run writes the next round. |
| `POST /runs/{id}/spec/skip-review?base_round=N` | builds now on the spec as it is. The criteria stay drafted. |
| `POST /runs/{id}/spec/extend-review` | gives a `timed` review 15 more minutes |

A stale `base_round`, a closed review or a cancelled run answers `409`. Read the run again and use the new round. Each call is on the [Run review](/docs/api) reference page.

### When no one confirmed

A `timed` review that runs out builds on the drafted criteria. A person can sign them later with `POST /runs/{id}/criteria/cosign`. If the run already delivered, its receipt is issued again with the signed criteria. It is safe to call twice.

## 2. The plan review

The same wait covers the plan the agent writes. Read it in `engineering_plan` on `GET /runs/{id}`. When it is cut short, `GET /runs/{id}/plan/{plan_sha256}` returns the whole text.

To ask for a new plan, a person sends notes to `POST /runs/{id}/plan/request-changes`. The body has the `plan_sha256` they read, an `idempotency_key`, and an `overall_comment` or notes on parts of the text.

The run wakes and writes a new plan. The same `idempotency_key` with the same notes does nothing twice. Confirming the criteria is what ends the wait. See [Request changes to a run's plan](/docs/api/run-review/request-plan-changes).

## 3. The build

Now the run builds, checks its work and passes its [review gates](/docs/api/review-gates). It needs no one here, unless it asks a question. See [Run lifecycle](/docs/guides/run-lifecycle#3-answer-it).

## 4. Land

The `autonomy` policy says what happens once the checks pass:

| `autonomy` | what happens |
|---|---|
| `auto` | opens a pull request for you to review |
| `gated` | stops before the pull request until a person approves it |
| `merge` | opens the pull request and merges it once every gate is green |
| `push` | moves the default branch to the checked commit (API only) |

`run.land` on `GET /runs/{id}` tracks the change from there. Its `state` is an open string. Syndai sends `coding.run.land_changed` when it turns `held`, `live`, `failed` or `reverted`. On `live`, read `land.live_url`.

### Go live

For a finished change on a project Syndai hosts, `POST /runs/{id}/land/go-live` puts it live. It answers `202` with the landing, which then runs every check. Follow the run until `land.state` moves on.

A `409` names one of three cases: the change is still too early, already live, or already landing. See [Send a finished change live](/docs/api/landing/go-live-run).

### A held landing

Syndai holds a change before it lands when a check is red, or when it touches protected files or agent settings. `land.reason` says why, and `land.can_merge_anyway` says if a person may push past it.

| call | who | what it does |
|---|---|---|
| `GET /runs/{id}/land/held-diff` | key or person | each held file with its diff, and the `head_sha` an approval must name |
| `POST /runs/{id}/land/retry` | key or person | starts the landing again. It never merges twice. |
| `POST /runs/{id}/land/close` | key or person | gives up and marks the landing failed. An open pull request stays open. |
| `POST /runs/{id}/land/merge-anyway` | person only | lands it past the checks that held it, then checks it live |

Reading the diff is not approving it. To go live anyway, a person sends the `head_sha` they read as `{"head_sha": "…"}`. They do this from the run page in the app, or from the `review_url` a key was given.

`409` means the change moved since they read it, or the hold is not one a person may push past. A retry drops an earlier go-live-anyway. Each call is on the [Landing](/docs/api) reference page.

### After it is live

To undo a change that went live, see [Undo a change that went live](/docs/guides/run-lifecycle#7-undo-a-change-that-went-live).
