# Create or replace a base: a named set of columns (same spec as `create_table`) that tables

`POST /datanotes/operations/define_base/` (operation `define_base`)

Create or replace a base: a named set of columns (same spec as `create_table`) that tables
inherit, like a parent class (`bases` in `metastore.yaml`). Tables using it get the columns
with the same name and meaning; `ref: "self"` means the inheriting table (e.g. a `parent`
column with `hierarchy: true`). `bases` builds on other bases. Descriptions are required, as in
create_table: `description` says what the tables of the base share, and each column has its `description`
(a column given without one keeps the one the base had for it). Replacing a base changes
every inheriting table: `rename_columns` (`{"old": "new"}`) renames the column on their rows,
`drop_columns` removes it from their rows; dropping a column without listing it is refused.
A renamed column is followed into the CHECKs and `ref_where` / `ref_by` that read it (the base's
own included); dropping one they read is refused, naming them. All affected rows are validated
before any write, inherited `ref_where` / `ref_by` and CHECKs that follow links included.
Enum columns: `values` and, for each, its meaning in `value_descriptions` (`{"stated": "the user said it", …}`): describe_table shows them and a refused value lists them.

The schema changes only through operations like this one: never edit metastore.yaml by hand (no validation, no undo).

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `X-Datanotes-Actor` | header | `string` | no | JSON: on whose behalf the write is made, recorded in the journal (`harness`, `model`, `session`). |

## Request body

`application/json`, required

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | `string` | yes |  |  |
| `columns` | `object[]` | yes |  |  |
| `bases` | `string[] \| null` | no | `null` |  |
| `description` | `string \| null` | no | `null` | What the tables of the base share (required, as a table's description is). |
| `rename_columns` | `object \| null` | no | `null` |  |
| `drop_columns` | `string[] \| null` | no | `null` |  |
| `dry_run` | `boolean` | no | `false` |  |

## Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | Done. | `OpResult` (application/json) |
| 400 | Bad input: the body is not a JSON object, or an argument is unknown, missing or of the wrong type. | `Error` (application/json) |
| 401 | Missing or wrong bearer token. | `Error` (application/json) |
| 403 | A named token beyond its permissions (the engine's administration, tables outside its `write` patterns, schema changes without `ddl`). | `Error` (application/json) |
| 422 | Refused (validation failures, conflicts, an unmet `expect`): nothing written. Same body as 200, with `ok: false` and the reasons. | `OpResult` (application/json) |
| 500 | Unexpected error (`{error}`), or a write that failed partway and could not be rolled back: `rolled_back: false`, `failed` lists the files left (undo_migration on `migration_id` retries the rollback). | `OpResult \| Error` (application/json) |
| 503 | A write that failed partway (a file another program holds) and was rolled back: `rolled_back: true`, nothing written. Send it again later. Same body as 200, with `ok: false` and `failed`. | `OpResult` (application/json) |

The schemas named here are in [openapi.json](https://datanotes.dev/openapi.json) (`components.schemas`).
