# 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`. |
| `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`. Nominally requires `LOOKUP_TABLE_DEFINITION_UPDATE`, but see the authorization caveat below — the table in the path is ignored and the permission gate may not apply. 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. Nominally requires `LOOKUP_TABLE_DEFINITION_DELETE` — see the authorization caveat below.                                                                                                                                                                                                         |

{% hint style="danger" %}
**The column `PATCH` and `DELETE` endpoints resolve the column by `{column_id}` alone and ignore the `{lookup_table_id}` in the path.** A caller who knows (or guesses) a `column_id` can edit or delete that column through *any* `{lookup_table_id}` value — the platform never checks that the column actually belongs to the named table. The read endpoint (`GET .../columns/{column_id}`) *does* validate the column-to-table relationship; the two mutating endpoints do not. Combined with the global (not per-table) scope of `LOOKUP_TABLE_DEFINITION_UPDATE` / `_DELETE`, a holder of either permission can mutate columns on every lookup table.

Worse, the permission gate on these two endpoints may not apply at all: the authorization rule is registered against the singular path `.../column/{column_id}` while the live route is the plural `.../columns/{column_id}`, so the matcher does not fire and the request falls through to the catch-all "any authenticated user" rule. Treat column `PATCH` / `DELETE` as **authentication-only** for now, and if cross-table column mutation is a concern restrict who can reach `/api/referencedata/` at the network perimeter (reverse-proxy rules, ingress filtering) until this is corrected upstream.
{% 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.opendatadiscovery.org/developer-guides/api-reference/reference-data.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
