> 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/configuration-and-deployment/enable-security/authorization/permissions.md).

# Permissions

There are 5 types of permissions in ODD Platform:

* [Data entity permissions](#data-entity-permissions): Actions related to specific data assets, such as tables, data streams, or dashboards.
* [Term permissions](#term-permissions): Actions concerning the management of the [Business Glossary](/features/data-glossary/business-glossary.md), e.g. terms and their definitions.
* [Query Example permissions](#query-example-permissions): Actions for creating and managing SQL query examples linked to datasets and terms.
* [Lookup table permissions](#lookup-table-permissions): Actions for creating and maintaining operator-managed reference tables — both the table schema and the rows stored in it.
* [Management permissions](#management-permissions): High-level administrative actions for managing the platform's infrastructure and configuration, such as creating data sources, managing users, or defining access control rules.

{% hint style="info" %}
This list is generated from the [`Permission` enum](https://github.com/opendatadiscovery/odd-platform/blob/main/odd-platform-specification/components.yaml) in the Platform's OpenAPI spec (`odd-platform-specification/components.yaml`). If a new permission appears in the API but is missing from this page, or vice versa, it is a bug — please open an issue or PR.
{% endhint %}

This is the full list of permissions divided by types:

#### Data entity permissions

* `DATA_ENTITY_ADD_TERM`. Allows adding a term to a data entity.
* `DATA_ENTITY_ADD_TO_GROUP`. Allows adding a data entity to a manually created group. **Operator caveat**: the permission is scoped against the **child entity** in the URL, not the parent group — a caller with this permission against entity X can place X into **any** manually-created DEG in the catalog, including DEGs owned by other teams. There is no per-DEG authorisation today, and the mutation emits no Activity Feed event. See [Data Entity Groups & Domains → Managing DEG Membership](/features/data-discovery/groups-domains.md#managing-deg-membership).
* `DATA_ENTITY_ALERT_CONFIG_UPDATE`. Allows configuring alert settings for a data entity (e.g., backwards-incompatible schema change alert, failed data quality test, failed job, distribution anomaly) and the time period to disable notifications.
* `DATA_ENTITY_ALERT_RESOLVE`. Allows resolving alerts for a data entity.
* `DATA_ENTITY_ATTACHMENT_MANAGE`. Allows adding, deleting, and managing file attachments and links for a data entity. (See [Attachments and links](/features/data-discovery/attachments.md).)
* `DATA_ENTITY_CUSTOM_METADATA_CREATE`. Allows creating custom metadata field values on a data entity, and minting new INTERNAL field rows in the deployment-wide field catalogue as a side effect of the create path. **Operator caveat**: the auto-create-on-miss side-channel makes this an indirect grant of catalogue-write — a caller can mint new field names visible to every other authenticated user on their next autocomplete keystroke. See [Custom metadata → Known limitations](/features/data-discovery/custom-metadata.md#known-limitations-and-operator-caveats).
* `DATA_ENTITY_CUSTOM_METADATA_DELETE`. Allows deleting a custom metadata field value from a data entity. The catalogue row is not affected by this operation (the field remains visible in the autocomplete picker on other entities). See [Custom metadata](/features/data-discovery/custom-metadata.md).
* `DATA_ENTITY_CUSTOM_METADATA_UPDATE`. Allows editing an existing custom metadata field value on a data entity. **Operator caveat**: the platform's update endpoint is declared `upsert*` in the API spec but the underlying SQL is a pure `UPDATE` — issuing a PUT for a field that has not previously been assigned on the target entity silently no-ops (HTTP 200 OK, empty body, "Metadata successfully updated." toast). Pre-flight with a GET, or switch to the Create endpoint on miss. See [Custom metadata → Known limitations](/features/data-discovery/custom-metadata.md#known-limitations-and-operator-caveats).
* `DATA_ENTITY_DELETE_FROM_GROUP`. Allows removing a data entity from a manually created group. **Operator caveat**: same write-collaborative posture as `DATA_ENTITY_ADD_TO_GROUP` — the permission is bound to the child entity in the URL, not the parent group; `DELETE` is silently idempotent (returns `204` on no-op without an Activity Feed event). See [Data Entity Groups & Domains → Managing DEG Membership](/features/data-discovery/groups-domains.md#managing-deg-membership).
* `DATA_ENTITY_DELETE_TERM`. Allows removing a term from a data entity.
* `DATA_ENTITY_DESCRIPTION_UPDATE`. Allows editing and deleting a data entity's custom description. **Operator caveat**: description text is persisted verbatim with no write-time HTML sanitisation, and the Markdown renderer is configured with `rehype-raw` and no `rehype-sanitize` — raw HTML embedded in the description renders as live markup for every catalog reader. The same write-collaborative posture applies to five sibling Markdown surfaces (per-column description, term definition, Query Example body, Lookup Table cell values, Slack notification body). See [Entity description → Security caveat](/features/data-discovery/entity-description.md#security-caveat-stored-xss-family-across-six-markdown-surfaces) for the operator-trust framing.
* `DATA_ENTITY_GROUP_UPDATE`. Allows editing a manually created data entity group.
* `DATA_ENTITY_INTERNAL_NAME_UPDATE`. Allows editing and deleting a data entity's business name. (See [Business names](/features/data-discovery/business-names.md).)
* `DATA_ENTITY_OWNERSHIP_CREATE`. Allows creating ownership for a data entity.
* `DATA_ENTITY_OWNERSHIP_DELETE`. Allows deleting ownership from a data entity.
* `DATA_ENTITY_OWNERSHIP_UPDATE`. Allows editing the title of a data entity ownership.
* `DATA_ENTITY_STATUS_UPDATE`. Allows changing the lifecycle status of a data entity (e.g., stable, deprecated, deleted, draft, unassigned). (See [Data entity statuses](/features/data-discovery/statuses.md).)
* `DATA_ENTITY_TAGS_UPDATE`. Allows editing a data entity's tags. **Operator caveat**: this is one of four permissions through which novel tag names mint new rows in the global tag directory — granting it to rank-and-file users widens vocabulary governance beyond `TAG_CREATE`. See [Manual Object Tagging → Known limitations and operator caveats](/features/data-discovery/tagging.md#known-limitations-and-operator-caveats).
* `DATASET_FIELD_ADD_TERM`. Documented as: allows linking a business glossary term to a specific field within a dataset. **Operator caveat — silently-misgated endpoint pair**: the platform's authorization wiring crosses two endpoints at adjacent lines today. The dataset-field term-add endpoint (`POST /api/datasetfields/{dataset_field_id}/terms`) is enforced against `DATA_ENTITY_ADD_TERM` at runtime, not `DATASET_FIELD_ADD_TERM` — UI gates on the documented permission, so the Add-term button is enabled for `DATASET_FIELD_ADD_TERM` holders but the server returns `403` silently. Separately, the alert-status PUT (`PUT /api/alerts/{alert_id}/status`) is enforced against `DATASET_FIELD_ADD_TERM` rather than `DATA_ENTITY_ALERT_RESOLVE` — granting this permission also grants alert-resolution on any entity. See [Per-column annotation → Known limitations](/features/data-discovery/per-column-annotation.md#known-limitations-and-operator-caveats) for the wiring-bug pair and the workaround.
* `DATASET_FIELD_DELETE_TERM`. Allows removing a linked business glossary term from a specific field within a dataset. See [Per-column annotation](/features/data-discovery/per-column-annotation.md).
* `DATASET_FIELD_DESCRIPTION_UPDATE`. Allows editing the description of an individual dataset field. The description is rendered through the same Markdown pipeline as the entity-level description and inherits the same security caveat — see [Entity description → Security caveat](/features/data-discovery/entity-description.md#security-caveat-stored-xss-family-across-six-markdown-surfaces) and [Per-column annotation](/features/data-discovery/per-column-annotation.md).
* `DATASET_FIELD_ENUMS_UPDATE`. Allows editing a dataset field's enum values. **Operator caveat**: the endpoint behind this permission is operationally bulk-replace (despite its `createEnumValue` operationId) — a partial submission soft-deletes every pre-existing enum row not present in the body. See [Per-column annotation → Known limitations](/features/data-discovery/per-column-annotation.md#known-limitations-and-operator-caveats).
* `DATASET_FIELD_INTERNAL_NAME_UPDATE`. Allows editing the business name of an individual dataset field. (See [Business names](/features/data-discovery/business-names.md) and [Per-column annotation](/features/data-discovery/per-column-annotation.md).)
* `DATASET_FIELD_TAGS_UPDATE`. Allows adding or removing tags from an individual dataset field. **Operator caveat**: novel tag names mint new rows in the global tag directory through this path; submitting an empty tag list silently clears every operator-curated (INTERNAL-origin) tag on the column. The audit feed event for column-level tag changes is `DATASET_FIELD_TAGS_UPDATED` (full before/after payload). See [Manual Object Tagging → Known limitations and operator caveats](/features/data-discovery/tagging.md#known-limitations-and-operator-caveats) and [Per-column annotation → Known limitations](/features/data-discovery/per-column-annotation.md#known-limitations-and-operator-caveats).
* `DATASET_TEST_RUN_SET_SEVERITY`. Allows setting severity for a dataset's quality tests.

#### Term permissions

* `TERM_CREATE`. Allows creating a new term in the business glossary.
* `TERM_DELETE`. Allows deleting a term from the business glossary.
* `TERM_OWNERSHIP_CREATE`. Allows creating ownership for a term.
* `TERM_OWNERSHIP_DELETE`. Allows deleting ownership from a term.
* `TERM_OWNERSHIP_UPDATE`. Allows editing the title of a term ownership.
* `TERM_TAGS_UPDATE`. Allows editing tags for a term. **Operator caveat**: novel tag names mint new rows in the global tag directory through this path. **No activity-feed event is emitted today** when term tags change — compliance / audit workflows depending on tag-change history must instrument this path externally. See [Manual Object Tagging → Known limitations and operator caveats](/features/data-discovery/tagging.md#known-limitations-and-operator-caveats).
* `TERM_UPDATE`. Allows editing the name, namespace, and definition of a term.

#### Query Example permissions

* `QUERY_EXAMPLE_CREATE`. Allows creating a query example.
* `QUERY_EXAMPLE_DATASET_CREATE`. Allows linking a query example to a dataset.
* `QUERY_EXAMPLE_DATASET_DELETE`. Allows unlinking a query example from a dataset.
* `QUERY_EXAMPLE_DELETE`. Allows deleting a query example.
* `QUERY_EXAMPLE_TERM_CREATE`. Allows linking a query example to a term.
* `QUERY_EXAMPLE_TERM_DELETE`. Allows unlinking a query example from a term.
* `QUERY_EXAMPLE_UPDATE`. Allows editing a query example.

#### Lookup table permissions

* `LOOKUP_TABLE_CREATE`. Allows creating a lookup table.
* `LOOKUP_TABLE_DATA_CREATE`. Allows adding data rows to a lookup table.
* `LOOKUP_TABLE_DATA_DELETE`. Allows deleting data rows from a lookup table.
* `LOOKUP_TABLE_DATA_UPDATE`. Allows editing data rows in a lookup table.
* `LOOKUP_TABLE_DEFINITION_CREATE`. Allows defining the structure (columns) of a lookup table.
* `LOOKUP_TABLE_DEFINITION_DELETE`. Allows deleting the structure (columns) of a lookup table.
* `LOOKUP_TABLE_DEFINITION_UPDATE`. Allows modifying the structure (columns) of a lookup table.
* `LOOKUP_TABLE_DELETE`. Allows deleting a lookup table.
* `LOOKUP_TABLE_UPDATE`. Allows editing the name and description of a lookup table.

#### Management permissions

* `COLLECTOR_CREATE`. Allows registering a new metadata collector.
* `COLLECTOR_DELETE`. Allows deleting a collector.
* `COLLECTOR_TOKEN_REGENERATE`. Allows regenerating the security token for a collector. **Operational caveat**: regeneration invalidates the prior token immediately — there is no grace period. The Collector process must be redeployed with the new token before existing ingestion stops.
* `COLLECTOR_UPDATE`. Allows editing a collector's configuration.
* `DATA_ENTITY_GROUP_CREATE`. Allows creating a new data entity group.
* `DATA_SOURCE_CREATE`. Allows creating a new data source connection.
* `DATA_SOURCE_DELETE`. Allows deleting a data source.
* `DATA_SOURCE_TOKEN_REGENERATE`. Allows regenerating the security token for a data source. **Operational caveat**: regeneration invalidates the prior token immediately — there is no grace period. The push-client must be redeployed with the new token before existing ingestion stops.
* `DATA_SOURCE_UPDATE`. Allows editing an existing data source's configuration.
* `DIRECT_OWNER_SYNC`. Allows associating a user with an owner without an approval request. **Composition caveat**: when the holder submits the home-page form with an owner name that does not exist in the catalog, the platform creates the owner on the requester's behalf and immediately binds them to it — in a single POST. Grant this permission only to principals you also trust to mint owner names (typically a service identity, not an end-user policy). See [User-owner association → How DIRECT\_OWNER\_SYNC changes the user-side flow](/configuration-and-deployment/enable-security/authorization/user-owner-association.md#how-direct_owner_sync-changes-the-user-side-flow).
* `NAMESPACE_CREATE`. Allows creating a new namespace via `POST /api/namespaces`. **Operator caveat — four sister-service side-doors**: four other parent permissions (`DATA_SOURCE_CREATE`, `DATA_SOURCE_UPDATE`, `TERM_CREATE`, `COLLECTOR_CREATE`, `DATA_ENTITY_GROUP_CREATE`) silently mint namespace rows through their `namespace_name` form field. Granting any of those parent permissions is an indirect grant of namespace-creation rights. See [Namespaces → Auto-create side-door](/features/management/namespaces.md#auto-create-side-door) for the four-vertex cluster and the operator-side mitigation.
* `NAMESPACE_DELETE`. Allows soft-deleting a namespace. **Operator caveat — cascade-block**: the delete is blocked with `CascadeDeleteException` (HTTP 400, error code `USR004`) when any of four referent tables (live data sources, live collectors, live terms, non-deleted data entities) still references the namespace. See [Namespaces → Cascade-on-delete guard](/features/management/namespaces.md#cascade-on-delete-guard) for the operator workflow and the partial-unique-index reincarnation behaviour after a successful soft-delete.
* `NAMESPACE_UPDATE`. Allows editing an existing namespace.
* `OWNER_ASSOCIATION_MANAGE`. Allows approving or denying user-owner association requests on the **Management → Associations → New requests** sub-tab. Also gates visibility of the Associations Management tab itself. Does **not** grant the admin direct-bind affordance — that requires `OWNER_RELATION_MANAGE` (see below). See [User-owner association → Approving incoming requests](/configuration-and-deployment/enable-security/authorization/user-owner-association.md#approving-incoming-requests-new-requests-tab).
* `OWNER_CREATE`. Allows creating a new owner entity via `POST /api/owners`. **Note:** three other service-tier code paths also mint Owner rows without consulting this permission — see [Owners → Three service-tier side-doors](/configuration-and-deployment/enable-security/authorization/owners.md#3-three-service-tier-side-doors-mint-owner-rows-without-owner_create).
* `OWNER_DELETE`. Allows deleting an owner via `DELETE /api/owners/{id}`. The delete is gated on no remaining ownership relations, term-ownership relations, or user-Owner bindings — operators see a `CascadeDeleteException` if any of those are still attached.
* `OWNER_RELATION_MANAGE`. Allows directly creating or removing the binding between a user and an owner — the **Create association** button in the Management → Associations header, and the per-row **Remove** on the Active associations sub-tab. Operators with this permission bypass the user-self-request and admin-approve workflow entirely. See [User-owner association → Creating a binding directly](/configuration-and-deployment/enable-security/authorization/user-owner-association.md#creating-a-binding-directly-create-association) and [Removing an existing binding](/configuration-and-deployment/enable-security/authorization/user-owner-association.md#removing-an-existing-binding-active-associations-tab). **Downstream-impact note:** the binding this permission creates is the single load-bearing anchor for the `/api/dataentities/my[/upstream|/downstream]` lineage triplet — every regression at the binding-resolution step has cross-owner blast radius on the lineage neighbourhood; see [Data Lineage → My-objects triplet](/features/data-lineage.md#my-objects-triplet-composition-anchor-architecture) for the architecture.
* `OWNER_UPDATE`. Allows editing an existing owner via `PUT /api/owners/{id}`. **Destructive-API caveat:** PUT with an empty (or absent) `roles` array silently removes every existing role binding from the Owner — see [Owners → PUT with empty roles destroys all role bindings](/configuration-and-deployment/enable-security/authorization/owners.md#4-put-apiownersid-with-empty-or-absent-roles-silently-destroys-all-role-bindings) for the safe-pattern fetch-modify-write workflow.

{% hint style="info" %}
**There is no `OWNER_READ` permission.** `GET /api/owners` is reachable to any authenticated user (and anonymous under `auth.type=DISABLED`) — it is part of the [read-collaborative posture on Management catalogs](#management-permissions) documented in the warning admonition at the bottom of this section.
{% endhint %}

* `POLICY_CREATE`. Allows creating a new access policy.
* `POLICY_DELETE`. Allows deleting an access policy.
* `POLICY_UPDATE`. Allows editing an existing access policy.
* `ROLE_CREATE`. Allows creating a new user role.
* `ROLE_DELETE`. Allows deleting a user role.
* `ROLE_UPDATE`. Allows editing an existing user role.
* `TAG_CREATE`. Allows creating a new tag. **Operator caveat**: `TAG_CREATE` is not the only path that mints new tags — four `*_TAGS_UPDATE` permissions (data entity, dataset field, term) plus collector ingestion all silently create tag rows for novel names. See [Manual Object Tagging → Known limitations and operator caveats](/features/data-discovery/tagging.md#known-limitations-and-operator-caveats).
* `TAG_DELETE`. Allows deleting a tag.
* `TAG_UPDATE`. Allows editing an existing tag.

{% hint style="warning" %}
**Read access on Management catalogs is granted to every authenticated user by design.** None of the Management permissions above gates the corresponding GET endpoint — withholding `OWNER_CREATE`, `NAMESPACE_CREATE`, `DATA_SOURCE_CREATE`, etc. does **not** restrict reads of the matching catalog. The endpoints below fall through to the platform's catch-all "any authenticated user" rule:

* `GET /api/owners` — every owner row (name, roles, provider mapping).
* `GET /api/owners/providers` — the active identity providers list.
* `GET /api/owner_association_request/activity` — the resolved-associations log (the **History** sub-tab; see [User-owner association → Auditing past association requests](/configuration-and-deployment/enable-security/authorization/user-owner-association.md#auditing-past-association-requests-history-tab)).
* `GET /api/namespaces` — every namespace, including names that may reveal organisational structure.
* `GET /api/datasources` — every registered data source (URL, ODDRN, namespace).
* `GET /api/collectors` — every collector, including a partially-redacted token until **Regenerate** is invoked.
* `GET /api/titles` — every catalog title vocabulary entry.

Operators who want to restrict read access on a Management catalog cannot do so through this permission set today. Plan deployments accordingly — anything sensitive enough to require read-side RBAC belongs on a different surface.
{% endhint %}

#### Comprehensive permissions

* `ALL`. Includes all permissions above.

## Permission read surface (two-endpoint orchestration)

Integrators consuming the platform's permission catalogue via the API need both of the endpoints below — the two scopes are orthogonal and the API does not unify them into a single call.

| Endpoint                                            | Scope                             | What it returns                                                                                                                                                                 | Example resource types                               |
| --------------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `GET /api/resource/{type}/{id}/permissions`         | Contextual / resource-scoped      | The permissions the caller holds **in the named resource's context** (per-entity, per-term, per-query-example).                                                                 | `DATA_ENTITY`, `TERM`, `QUERY_EXAMPLE`               |
| `GET /api/identity/whoami` → `Identity.permissions` | Non-contextual / management-scope | The permissions the caller holds **globally** for management operations — Policy / Role / Owner / Datasource / Collector / Namespace / Tag / Lookup Table / Query Example CRUD. | (returned as a flat permission list, no resource ID) |

{% hint style="warning" %}
**`PermissionResourceType.MANAGEMENT` is a valid spec enum value but the runtime rejects it on the contextual endpoint.** The OpenAPI `PermissionResourceType` schema declares four values (`DATA_ENTITY`, `TERM`, `QUERY_EXAMPLE`, `MANAGEMENT`). Calling `GET /api/resource/MANAGEMENT/{id}/permissions` returns **HTTP 400 USR001** with the body `Resource type MANAGEMENT does not have context` — management-scope permissions are intentionally returned by the `/api/identity/whoami` endpoint, not the contextual one. SDK code generated from the spec that builds a 4-way switch over `PermissionResourceType` and dispatches all four to the contextual endpoint will see unexpected 400 responses on `MANAGEMENT` calls; route those to `whoami` instead. A spec-side tightening is tracked upstream.
{% endhint %}

### Five categories versus four resource types

The five permission groupings on this page (Data entity / Term / Query Example / Lookup table / Management) are an operator-readable taxonomy — they correspond to where permissions appear in the Management UI and how the runtime services consume them. The `PermissionResourceType` enum in the OpenAPI spec has **four values** because `LOOKUP_TABLE_*` permissions are stored within the Management bucket on the read surface (the runtime treats them as non-contextual / management-scope and returns them from `/api/identity/whoami`, not from `GET /api/resource/LOOKUP_TABLE/...` — there is no `LOOKUP_TABLE` resource type at the API contract).

An integrator dispatching from the four-value enum gets every permission the caller holds; the five-category page taxonomy is the framing operators use when reading the catalogue.

## Surfaces without per-resource permission gating today

Some read surfaces that an operator might assume are gated by a per-resource permission are reachable to any authenticated caller. The Management catalogs above (`GET /api/owners`, `GET /api/namespaces`, `GET /api/datasources`, …) are one such cluster — documented in the read-collaborative-posture warning above. The other cluster operators routinely meet is **dataset structure / version reads**:

* `GET /api/datasets/{data_entity_id}/structure` — the latest schema of any dataset.
* `GET /api/datasets/{data_entity_id}/structure/{version_id}` — any historical schema revision.
* `GET /api/datasets/{data_entity_id}/structure/diff?first_version_id=…&second_version_id=…` — the side-by-side diff between any two revisions.

The path component `{data_entity_id}` is consumed by the controller but **not** used by the underlying query — the query filters on `version_id` alone. Any authenticated caller enumerating `version_id` integers reads any dataset's schema; under `auth.type=DISABLED`, anonymously. The page-side detail (and the multi-tenant operator-mitigation table) lives on [Dataset schema diff → Known limitations](/features/data-discovery/schema-diff.md#known-limitations-and-operator-caveats).
