> For the complete documentation index, see [llms.txt](https://docs.opendatadiscovery.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.opendatadiscovery.org/developer-guides/api-reference/reference-data.md).

# Reference Data

The Lookup Tables API surface is exposed under `/api/referencedata/` — 16 endpoints across four groups (table CRUD, column CRUD, row CRUD, search). The underlying Spring controller is named `ReferenceDataController` even though the user-facing concept is "Lookup Tables". Every endpoint requires authentication. The **mutating** endpoints (table / column / row create, update, delete) additionally require the corresponding `LOOKUP_TABLE_*` permission documented under [Lookup Tables → RBAC permissions](/features/master-data-management/lookup-tables.md#rbac-permissions) — but those permissions are **global, not per-table or per-owner scoped**, so a holder of (for example) `LOOKUP_TABLE_DEFINITION_UPDATE` can edit columns on any lookup table. The **read and search** endpoints require only authentication: there is no `LOOKUP_TABLE_*_READ` permission, so any authenticated caller can read every lookup table's structure and rows. For the feature description, the creation flow, supported PostgreSQL field types, and direct database access via `lookup_tables_schema`, see the [Lookup Tables](/features/master-data-management/lookup-tables.md) page.

## Table CRUD (4 endpoints)

| Method   | Path                                         | Operation ID           | Purpose                                                                                                                                                                                                                                                                                                                                                                      |
| -------- | -------------------------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST`   | `/api/referencedata/table`                   | `createReferenceTable` | Create a new lookup table. Body: `LookupTableFormData` (`name`, `namespace_name`, optional `description`). Requires `LOOKUP_TABLE_CREATE`. The `name` is normalised (lowercased, spaces → underscores) to form the physical table name; a name that normalises to one that already exists in the same namespace is rejected with `400` ("already exists in this namespace"). |
| `GET`    | `/api/referencedata/table/{lookup_table_id}` | `getLookupTableById`   | Get a single lookup table including its column schema.                                                                                                                                                                                                                                                                                                                       |
| `PUT`    | `/api/referencedata/table/{lookup_table_id}` | `updateLookupTable`    | Rename or re-describe a table. Body: `LookupTableUpdateFormData` (`name`, optional `description`). Requires `LOOKUP_TABLE_UPDATE`.                                                                                                                                                                                                                                           |
| `DELETE` | `/api/referencedata/table/{lookup_table_id}` | `deleteLookupTable`    | Delete a lookup table. Requires `LOOKUP_TABLE_DELETE`.                                                                                                                                                                                                                                                                                                                       |

## Column (definition) CRUD (4 endpoints)

| Method   | Path                                                             | Operation ID                  | Purpose                                                                                                                                                                                                                                                                                                                                                                                                         |
| -------- | ---------------------------------------------------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST`   | `/api/referencedata/table/{lookup_table_id}/columns`             | `createColumnsForLookupTable` | Add one or more columns. Body: array of `LookupTableFieldFormData` (`name`, `field_type`, optional `description`, `is_nullable`, `is_unique`, `default_value`). Requires `LOOKUP_TABLE_DEFINITION_CREATE`.                                                                                                                                                                                                      |
| `GET`    | `/api/referencedata/table/{lookup_table_id}/columns/{column_id}` | `getLookupTableField`         | Get a single column.                                                                                                                                                                                                                                                                                                                                                                                            |
| `PATCH`  | `/api/referencedata/table/{lookup_table_id}/columns/{column_id}` | `updateLookupTableField`      | Edit column metadata. Body: `LookupTableFieldUpdateFormData`. The `{column_id}` must belong to the `{lookup_table_id}` in the path, or the request is rejected with `400` ("doesn't belong to"). Nominally requires `LOOKUP_TABLE_DEFINITION_UPDATE`, but see the authorization caveat below — the permission gate may not fire. The `field_type` is **not** in the update form — types are immutable once set. |
| `DELETE` | `/api/referencedata/table/{lookup_table_id}/columns/{column_id}` | `deleteLookupTableField`      | Delete a column. The `{column_id}` must belong to the `{lookup_table_id}` in the path, or the request is rejected with `400`. Nominally requires `LOOKUP_TABLE_DEFINITION_DELETE` — see the authorization caveat below.                                                                                                                                                                                         |

{% hint style="danger" %}
**The column `PATCH` and `DELETE` permission gate may not fire — treat these two endpoints as authentication-only.** The authorization rule for column mutation is registered against the singular path `.../column/{column_id}`, while the live route is the plural `.../columns/{column_id}`. The matcher does not fire, so the request falls through to the catch-all "any authenticated user" rule. Combined with the global (not per-table) scope of `LOOKUP_TABLE_DEFINITION_UPDATE` / `_DELETE`, any authenticated caller can edit or delete lookup-table columns regardless of whether they actually hold the `LOOKUP_TABLE_DEFINITION_*` permission. If this is a concern, restrict who can reach `/api/referencedata/` at the network perimeter (reverse-proxy rules, ingress filtering) until it is corrected upstream.

(The related cross-table defect — passing a mismatched `{lookup_table_id}` to mutate a column on a *different* table — was closed in 0.29.0: the `PATCH` and `DELETE` column endpoints now validate the column-to-table relationship and reject a mismatch with `400`, mirroring the read endpoint.)
{% endhint %}

## Row (data) CRUD (4 endpoints)

| Method   | Path                                                       | Operation ID            | Purpose                                                                                                |
| -------- | ---------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------ |
| `GET`    | `/api/referencedata/table/{lookup_table_id}/data`          | `getLookupTableRowList` | Page through rows. Query params: `page`, `size`.                                                       |
| `POST`   | `/api/referencedata/table/{lookup_table_id}/data`          | `addDataToLookupTable`  | Insert one or more rows. Body: array of `LookupTableRowFormData`. Requires `LOOKUP_TABLE_DATA_CREATE`. |
| `PATCH`  | `/api/referencedata/table/{lookup_table_id}/data/{row_id}` | `updateLookupTableRow`  | Edit an existing row. Requires `LOOKUP_TABLE_DATA_UPDATE`.                                             |
| `DELETE` | `/api/referencedata/table/{lookup_table_id}/data/{row_id}` | `deleteLookupTableRow`  | Delete a row. Requires `LOOKUP_TABLE_DATA_DELETE`.                                                     |

## Search (4 endpoints)

The Master Data list uses a faceted-search flow shared with other catalog search surfaces — create a search, then page results.

| Method | Path                                            | Operation ID                         | Purpose                                                                                                      |
| ------ | ----------------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `POST` | `/api/referencedata/search`                     | `referenceDataSearch`                | Create a new search and get back a `search_id` plus the initial facets. Body: `ReferenceDataSearchFormData`. |
| `GET`  | `/api/referencedata/search/{search_id}`         | `getReferenceDataSearchFacetList`    | Re-fetch the facets for an existing search.                                                                  |
| `PUT`  | `/api/referencedata/search/{search_id}`         | `updateReferenceDataSearchFacetList` | Update the search query and re-aggregate facets.                                                             |
| `GET`  | `/api/referencedata/search/{search_id}/results` | `getReferenceDataSearchResults`      | Page through the matching tables. Query params: `page`, `size`.                                              |

## See also

* [API Reference hub](/developer-guides/api-reference.md) — the full per-feature index.
* [Lookup Tables](/features/master-data-management/lookup-tables.md) — feature description, creation flow, PostgreSQL field types, RBAC, direct database access.
