# Update exactly ONE row, addressed by its `id` (the system UUID) or its `path`; refused when

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

Update exactly ONE row, addressed by its `id` (the system UUID) or its `path`; refused when
the row does not exist (or id and path disagree). Same validation, journal and history as
`update_rows`.

- `set` / `unset`: columns to set or remove.
- `body`: replace the whole note body; or edit it keeping the rest:
  `body_sections` `{"History": "…"}` replaces the text under that heading (any level, up to the
  next heading of the same level; added as `## History` when missing), `body_prepend` /
  `body_append` add text before / after.
- `expect`: compare-and-set — `{"status": "open"}` writes only if the row still has those
  values (a person may have changed it since you read it); otherwise refused, nothing written.
- `autolink`: the text written links the notes it mentions by name, at their first mention
  (off with `autolink: false` or the database's setting). `links_added` lists the links made;
  `ambiguous_mentions` the mentions left as text with the notes they may name: read those, then
  link the right one with update_row and `autolink: false`, or leave the text.

## 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 |
| --- | --- | --- | --- | --- |
| `table` | `string` | yes |  |  |
| `id` | `string \| null` | no | `null` |  |
| `path` | `string \| null` | no | `null` |  |
| `set` | `object \| null` | no | `null` |  |
| `unset` | `string[] \| null` | no | `null` |  |
| `body` | `string \| null` | no | `null` |  |
| `body_sections` | `object \| null` | no | `null` |  |
| `body_prepend` | `string \| null` | no | `null` |  |
| `expect` | `object \| null` | no | `null` |  |
| `body_append` | `string \| null` | no | `null` |  |
| `dry_run` | `boolean` | no | `false` |  |
| `autolink` | `boolean \| null` | no | `null` | Automatic links for this write: true links the notes the text written (`body`, `body_sections`, `body_prepend`, `body_append`) mentions by name, false writes it as given. Default: the calling token's `autolink` default, else the database's `autolink` setting (on unless configure_database turned it off). |

## 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) |
| 404 | Unknown table, view, migration, note or folder. | `Error` (application/json) |
| 409 | Ambiguous table name: it matches several tables (`paths` or `tables` lists them). | `AmbiguousName` (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`).
