These are the terms the rest of the API docs use.

## Workspace

A workspace is your account in Syndai. The API calls it a customer, so you see `customer_id` in responses. API keys, projects, webhook endpoints, members and your plan all belong to one workspace. Every API call acts as the workspace that owns the key.

A member has one of four roles: `owner`, `admin`, `member` or `viewer`.

## Project

A project is one code repository. Runs refer to it by `coding_repository_id`. It is either hosted by Syndai or connected from GitHub, and its `provider` field says which. `GET /repos` lists the projects a key can see.

## Run

A run is one task on one project. It starts from a `prompt` and moves through phases that Syndai owns. It ends as `completed`, `failed` or `cancelled`. Runs are the main resource of the API.

## Phase

`run.current_phase` names the step a run is on. Each change sends a `coding.run.phase_changed` event. Today the phases are `intake`, `provision`, `clarify`, `plan`, `execute`, `validate`, `docs`, `commit`, `preflight` and `pr`, then one of the three end phases.

Treat the phase as an open string. New phases can appear. Match `completed`, `failed` and `cancelled` as the end, and do not list every other phase in your code.

## Result kind

`run.result_kind` says what a completed run produced:

| value | meaning |
|---|---|
| `pull_request` | the run opened a pull request; `run.pr_url` links to it |
| `no_changes` | the run found nothing to change |
| `null` | the run has not completed, or ended some other way |

## Final response

The run detail route returns `final_response`, the run's own words about what it did. Its `status` is one of:

| status | meaning |
|---|---|
| `pending` | the run is still going |
| `ready` | `content` holds the text |
| `failed` | writing the response failed; `error_code` says so |
| `unavailable` | the run ended and has no response |

A failed or cancelled run also gets a response that explains why it stopped. Right after a run ends, the status can read `unavailable` for a moment while the text is saved. The `coding.run.final_response_ready` event is the signal to read it.

The text is only on `GET /runs/{id}`. List rows, events and webhooks never carry it.

## Readiness

`run.readiness` shows how the first read of your prompt went. `readiness` is `green` when the prompt was clear. It is `red` when Syndai wants you to confirm something first, and `clarifying_questions` lists what it asks. Treat it as an open string.

## Questions

When a run needs a person, it pauses and sends `coding.run.question_required`. The run detail lists the open asks in `pending_hitl_requests`. Each one has a `hitl_request_id` and `inputs`. Answer with `POST /runs/{id}/responses`, as the [quickstart](/docs/api/quickstart#6-answer-a-question) shows.

A question at intake waits up to 24 hours. Then the run goes on with its best reading of the prompt.

## Validation level

`policy_config.validation.level` sets how much testing Syndai runs after the code is written. It is `smoke`, `standard` or `strict`, and `standard` is the default. Syndai picks the test tools for your stack. You never name a test runner. See [Validation](/docs/api/validation).

## Review gate

A review gate is a check that reads the plan or the change and gives a verdict. It never edits code. The gates are `planning_review`, `adversarial_review`, `security_review` and `preflight`. Every run needs `planning_review`, `security_review` and `preflight`. Your policy can ask for `adversarial_review` too.

A gate's `status` is one of `pending`, `running`, `passed`, `failed`, `waived`, `blocked` or `cancelled`. A required gate that fails holds the run back from finishing. See [Review gates](/docs/api/review-gates).

## Webhook event

A webhook event is a signed HTTP POST that Syndai sends to your endpoint when a run changes. Each one has an event type, such as `coding.run.completed`. It is signed with HMAC-SHA256 in the `X-Syndai-Signature` header. See [Webhooks](/docs/api/webhooks) for the full list and how to check a signature.

## Idempotency key

`external_request_id` is required on `POST /runs`. Syndai keeps one run per key in each workspace.

- Send the same key and the same body again, and you get `409 IDEMPOTENCY_REPLAY`. The body has `existing_run_id`, and the `Location` header points at the run. No new run starts and nothing more is charged.
- Send the same key with a different body, and you get `409 CONFLICT`.

So after a network timeout, retry with the same key and follow `existing_run_id`. A key is used up for good once a run has it. A follow-up is a new run, so it needs a new key.
