# catalog

Discovered ODCS data contracts for project catalog sources.

**Amfelt REST API** · v1.13.0 · `https://api.amfelt.com`

---
### POST `/v1/catalog/profiles` {#ingestcatalogprofile}

**Ingest an in-flight profile of one schema object (agent-auth).**

The agent-harvest write seam for profiles (ADR-159, Decision 3). The agent posts the profile its in-flight profiler took of one schema object while loading it to staging. The api stores it as an attachment of the source's contract, linked to the contract version stored at that moment; the org is derived from the agent JWT, never the body. Storing a profile never changes the contract and never creates a contract revision. Answers 404 when the source has no contract, and 409 when the stored contract cannot be parsed, so the version to link the profile to is unknown.

#### Request body _(required)_

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `environment` | string | Yes |  |
| `exact` | boolean | Yes | True when every row was seen. |
| `mode` | [ProfileMode](#profile-mode) | Yes |  |
| `object_name` | string | Yes | The profiled schema object's ODCS name (schema[].name). |
| `profile` | object | Yes | The profile as the agent's profiler produced it: row count and per-column statistics, signals, PII evidence and verdicts. Counts and labels only; temporal min/max are the only raw values. |
| `profiled_at` | string | Yes | When the load the profile was taken from landed. |
| `project_id` | string | Yes |  |
| `run_id` | [integer null] | No | The run whose load the profile was taken from, if any. |
| `source_slug` | string | Yes |  |


#### Responses

**200** — The stored profile, without its body.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `contract_version` | string | Yes | The version field of the stored contract the profile is linked to. |
| `created_at` | string | Yes |  |
| `exact` | boolean | Yes |  |
| `id` | string | Yes |  |
| `mode` | [ProfileMode](#profile-mode) | Yes |  |
| `object_name` | string | Yes |  |
| `profiled_at` | string | Yes |  |
| `run_id` | [integer null] | No |  |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**409** — The request conflicts with the current state of the resource.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### POST `/v1/catalog/object-advice` {#ingestobjectadvice}

**Ingest LLM-advisor object-type recommendations (agent-auth).**

The agent-harvest write seam (ADR-092). The agent posts LLM-advisor object-type recommendations for a (project, environment, source); the org is derived from the agent JWT, never the body. Upserts on the natural key (source, object_name). It never sets classification or PII-label state: the stored state is carried over (ClassificationStatus). A recommendation is stored apart from the source's contract and is not a proposal.

#### Request body _(required)_

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `environment` | string | Yes |  |
| `model` | string | No |  |
| `project_id` | string | Yes |  |
| `provider` | string | No |  |
| `recommendations` | object[] | Yes |  |
| `source_slug` | string | Yes |  |


#### Responses

**200** — The number of recommendations ingested.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `ingested` | integer | Yes | The number of recommendations upserted. |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### GET `/v1/catalog/history-states` {#listhistorystates}

**List the recorded built history modes of a source (agent-auth).**

The agent's read of the built history modes the api records: for each object of a source and each target its history is on, the history mode that history was built in (HistoryState). It returns exactly the records of the source that project_id, environment and source_slug name: at most one per object and target, ordered by object_name and then target, and an empty data when there are none. The org is derived from the agent JWT, never the query. The environment must be a project environment whose runs dispatch to the calling agent (setProjectEnvironmentAgent), of the project project_id names; otherwise the api answers 403 (code permission_denied) and lists nothing.

This list is the built mode the run-time refusal compares against (HistoryRebuildConflict). The api is the source of truth for it. The built mode is never read from the history table's structure. An object and target for which this list holds no record has no built mode yet: the agent adopts the history mode its run resolves and loads the history in that mode, and that run is not refused; the api records the mode that run's finished, non-skipped history task reports with TaskUpdate.history_mode and target, so a later read lists it. History loaded before the api recorded built modes cannot be verified. The recorded mode is the one reported in the run with the highest run_id by a finished history task that was not skipped, or by a completed rebuild; within that run, the first report stands (HistoryState).

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `project_id` | query | Yes | string | Control-plane project identifier. |
| `environment` | query | Yes | string | DTAP environment slug. |
| `source_slug` | query | Yes | string | Catalog source slug. |

#### Responses

**200** — The source's recorded built history modes; empty when there are none.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `data` | [HistoryState](#history-state)[] | Yes |  |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**403** — Code permission_denied: the environment is not assigned to the calling agent (no project environment of project_id whose runs dispatch to it). Nothing is listed.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### GET `/v1/catalog/history-rebuilds` {#listpendinghistoryrebuilds}

**List the pending history rebuilds of a source (agent-auth).**

The agent's read of confirmed history rebuilds: a history-mode change on an object whose history already existed on a target, confirmed with rebuild_history (HistoryRebuildConflict). It returns exactly the rebuilds of the source that project_id, environment and source_slug name whose status is pending: at most one per object and target, oldest requested_at first, and an empty data when there are none. A completed or canceled rebuild is never returned. Each rebuild names one object on one target, so two jobs that load the same object to different targets each get their own. A run reads the list for its source before loading history, and acts on a listed rebuild only as HistoryRebuild says: when it loads that object to the rebuild's target and resolves the object's history mode to new_history_mode, it drops the object's history and dependent bridge on that target, rebuilds them from current staging in new_history_mode, and reports it with completeHistoryRebuild; otherwise it drops nothing and the rebuild stays pending. The org is derived from the agent JWT, never the query. The environment must be a project environment whose runs dispatch to the calling agent (setProjectEnvironmentAgent), of the project project_id names; otherwise the api answers 403 (code permission_denied) and lists nothing.

This list is also what the run-time refusal reads (HistoryRebuildConflict): a run loads an existing history table in a history mode other than the one it was built in (the built mode listHistoryStates lists) only when this list names a pending rebuild of that object on that target to that mode, and otherwise refuses. When listHistoryStates lists no built mode for that object on that target, there is nothing to compare against: the run adopts the history mode it resolves and reports it, and that run is not refused.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `project_id` | query | Yes | string | Control-plane project identifier. |
| `environment` | query | Yes | string | DTAP environment slug. |
| `source_slug` | query | Yes | string | Catalog source slug. |

#### Responses

**200** — The source's pending history rebuilds; empty when there are none.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `data` | [HistoryRebuild](#history-rebuild)[] | Yes |  |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**403** — Code permission_denied: the environment is not assigned to the calling agent (no project environment of project_id whose runs dispatch to it). Nothing is listed.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### POST `/v1/catalog/contracts` {#ingestcatalogcontract}

**Ingest a harvested ODCS contract (agent-auth).**

The agent-harvest write seam (ADR-063). The agent posts a discovered ODCS contract for a (project, environment, source); the org is derived from the agent JWT, never the body. Upserts on the natural key.

A harvested contract never sets classification or PII-label state: the stored state is carried over (ClassificationStatus). The table classifications and PII labels inline in contract_yaml, proposed or approved, a property's ODCS classification field included, are ignored: the stored revision takes the stored state by object and property identity (ClassificationStatus), so an object or property a harvest no longer finds keeps its stored state and gets it back when a later harvest finds it again, and one with no stored state has none. So this write never changes an object's classification or history mode, and it takes no rebuild_history.

#### Request body _(required)_

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `contract_yaml` | string | Yes | The ODCS v3 contract document, verbatim YAML. Classification and PII-label state inline in it is ignored; the stored state is carried over (ingestCatalogContract). |
| `description` | string | No |  |
| `environment` | string | Yes |  |
| `name` | string | No |  |
| `plugin` | string | No |  |
| `project_id` | string | Yes |  |
| `role` | string | No |  Valid values are: `source`, `target`. |
| `source_slug` | string | Yes |  |


#### Responses

**200** — The upserted contract source.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `description` | string | No | Short description. |
| `id` | string | Yes | Catalog source slug. |
| `name` | string | Yes | Source display name. |
| `plugin` | string | No | Plugin key (e.g. postgres, snowflake). |
| `role` | string | Yes | Whether the asset is an inbound source or the outbound target. Valid values are: `source`, `target`. |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### POST `/v1/catalog/classifications` {#ingestcatalogclassifications}

**Propose table classifications and PII labels, or file verified auto-approvals (agent-auth).**

The agent-harvest write seam for classifications (ADR-159, Decision 4). The agent files a table classification per schema object and a PII label per property; the api writes them into the source's contract, with their confidence, and stores the result as a new contract revision. Each is stored as proposed, except a table classification filed with status approved that passes the checks below (a verified auto-approval), which is stored approved unless the history hold below applies.

It is one of the operations that own classification state (ClassificationStatus), and the only one an agent calls.

Auto-approval: a table classification the advisor answered with confidence the agent may file with status approved and proposed_by advisor, and must then name the call that produced it in advisor_call_id. Before it writes anything the api checks every object filed with status approved or naming an advisor_call_id, in this order, and the first check that fails answers for the whole request:
1. An object with status approved names an advisor_call_id, and the request's proposed_by is advisor. Otherwise 400 (code invalid_argument).
2. The call is one of the caller's organization's advisor calls. Otherwise 400 (code invalid_argument).
3. The call's outcome was answered. Otherwise 400 (code invalid_argument).
4. The call was about the same project, environment, source and object. Otherwise 400 (code invalid_argument).
5. The call has no result yet. Otherwise 409 (code conflict), so one advisor answer approves at most one classification.

Whichever check fails, nothing is written. A verified object with status approved is stored approved, with approved_by advisor and its confidence, and sets the call's result to auto_applied. A verified object with status proposed is stored as a proposal and sets the call's result to awaiting_approval.

History hold: an object filed with status approved whose history the api knows was built on a target in another history mode (HistoryRebuildConflict) is never auto-applied. The api stores it as proposed instead, counts it in proposed_objects, sets the call's result to awaiting_approval, and names it in held_for_rebuild; a person approves it with rebuild_history. Where the api knows no built mode, a verified auto-approval is stored approved, and the run that filed it adopts the history mode it resolves: the classified mode when no job override sets one, and the override's mode when one does (an explicit historyMode, ADR-068). That run loads the history in that mode and is not refused (HistoryRebuildConflict), and the api records the mode its finished, non-skipped history task reports with TaskUpdate.history_mode and target (HistoryState). History loaded before the api recorded built modes cannot be verified.

PII labels carry no status and are always proposed. A proposal never replaces an approved classification or label: those are skipped and reported, as are objects and properties the stored contract does not have. The org is derived from the agent JWT, never the body. Answers 404 when the source has no contract, and 409 when the stored contract cannot be parsed or holds a classification or label that cannot be read; neither is overwritten, since it might be an approval.

#### Request body _(required)_

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `environment` | string | Yes |  |
| `objects` | [ObjectClassificationProposal](#object-classification-proposal)[] | No | Proposed table classifications, one per schema object. |
| `pii` | [PiiLabelProposal](#pii-label-proposal)[] | No | Proposed PII labels, one per property. |
| `project_id` | string | Yes |  |
| `proposed_by` | string | No | What proposed the table classifications, for example "advisor". Defaults to "agent". An object with status approved is accepted only when this is "advisor". |
| `source_slug` | string | Yes |  |


#### Responses

**200** — What was proposed, what was approved, what was held for a rebuild, and what was skipped.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `approved_objects` | integer | No | Table classifications written as approved by the advisor. Every api that accepts status approved sends it, 0 included. It is not required, so absence stays distinguishable from 0: an api older than auto-approval omits it and stores every table classification as proposed, whatever status was filed. So when approved_objects is present, an object filed with status approved was stored approved exactly when it is named neither in skipped nor in held_for_rebuild; when it is absent, none was stored approved. |
| `contract_version` | [string null] | No | The version field of the stored contract, or null when nothing was written. |
| `held_for_rebuild` | [HistoryModeChange](#history-mode-change)[] | No | Objects filed with status approved that were stored as proposed instead, because their history already exists on a target in another history mode: an auto-approval never drops history. They are counted in proposed_objects and wait for a person, who approves them with rebuild_history. Empty or absent when there are none. |
| `proposed_objects` | integer | Yes | Table classifications written as proposed. |
| `proposed_pii` | integer | Yes | PII labels written as proposed. |
| `skipped` | [ClassificationSkip](#classification-skip)[] | Yes | Proposals that were not written, and why. |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**409** — The request conflicts with the current state of the resource.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### POST `/v1/catalog/history-rebuilds/{rebuild_id}/complete` {#completehistoryrebuild}

**Report a history rebuild done (agent-auth).**

The agent reports that a run dropped and rebuilt the object's history on the rebuild's target in the new mode. The rebuild moves to completed with that run_id and is no longer listed, so a later run does not drop the history again. Idempotent by run_id: reporting a completed rebuild again with the run_id that completed it answers 200 with the record unchanged, so a retry after a lost response is safe; reporting it with another run_id answers 409 (code conflict) and changes nothing. The org is derived from the agent JWT.

Before it completes a pending rebuild the api verifies the report. The calling agent must be the agent the rebuild's project environment dispatches to (setProjectEnvironmentAgent); otherwise it answers 403 (code permission_denied) and changes nothing. The run must be one of the organization's runs, of the rebuild's project, environment and source, executed by the calling agent (Run.agent_id); it must have started after the rebuild was requested (Run.started_at later than requested_at); and it must have loaded the rebuild's target (its job's target, or the environment's platform when the job names none, is the rebuild's target). A run that fails any of these checks answers 409 (code conflict) and changes nothing, as does a report of a canceled rebuild.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `rebuild_id` | path | Yes | string | The history rebuild's id. |

#### Request body _(required)_

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `run_id` | integer | Yes | The run that dropped and rebuilt the object's history. |


#### Responses

**200** — The completed rebuild: moved to completed by this report, or already completed by the same run_id, in which case it is returned unchanged.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `canceled_at` | string | No | When a newer confirmed change or a change back to the built mode canceled the rebuild; set exactly when status is canceled. |
| `completed_at` | string | No | When a run reported the rebuild done; set exactly when status is completed. |
| `completed_run_id` | integer | No | The run that rebuilt the history; set exactly when status is completed. A repeat of completeHistoryRebuild with this run_id answers the record unchanged. |
| `environment` | string | Yes | DTAP environment slug. |
| `history_mode` | string | Yes | The history mode the existing history was built in: the mode the guard compared against. As HistoryModeChange.history_mode. Examples: `scd1`. |
| `id` | string | Yes |  |
| `new_history_mode` | string | Yes | The history mode to rebuild in; never equal to history_mode. As HistoryModeChange.new_history_mode. Examples: `scd2`. |
| `object_name` | string | Yes | The schema object's ODCS name (schema[].name). |
| `project_id` | string | Yes |  |
| `requested_at` | string | Yes |  |
| `requested_by` | string | Yes | The id of what confirmed the rebuild: the account of the person who confirmed it (requested_by_type account). |
| `requested_by_type` | [HistoryRebuildRequesterType](#history-rebuild-requester-type) | Yes |  |
| `source_slug` | string | Yes |  |
| `status` | [HistoryRebuildStatus](#history-rebuild-status) | Yes |  |
| `target` | string | Yes | The target platform slug whose history is rebuilt: the job's target, or the environment's platform when the job names none (Job.target). |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**403** — Code permission_denied: the calling agent is not the agent the rebuild's project environment dispatches to. Nothing is changed.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**409** — Code conflict: the rebuild was already completed by another run_id, it was canceled, or the run is not one of the organization's runs of the rebuild's project, environment and source, executed by the calling agent, started after the rebuild was requested, that loaded its target. Nothing is changed.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

