# Activity Feed

Track changes to your data entities by monitoring the global **Activity** page or the **Activity** tab on a data entity's detail page. Every metadata edit the platform observes — entity lifecycle transitions, ownership changes, tag and term assignments, dataset-field edits, alerts — emits a typed event onto the feed. The feed is the catalog's audit trail and the change-driven discovery surface: who did what to which entity, and when.

![](/files/FGiluwwMOYraX6fccjPd)

## Where to find it

* **Global Activity page** — top-level `Activity` entry in the platform's navigation. Shows every event across the catalog with a seven-facet filter panel (see below) and a four-tab axis (`All` / `My Objects` / `Upstream Dependents` / `Downstream Dependents`).
* **Per-entity Activity tab** — every data-entity detail page has an `Activity` tab that scopes the feed to events on that entity only (plus a few additional internal event types — entity overview / metadata / schema / relation updates and custom-metadata create / update / delete — that are recorded on the per-entity tab but hidden from the global filter to keep that view concise). The per-entity tab does **not** carry the four-tab axis the global page exposes — see [Per-entity Activity tab differences](#per-entity-activity-tab-differences) below for the asymmetries you'll hit if you navigate between the two.

## My Objects setup (user-owner association prerequisite)

The `My Objects`, `Upstream Dependents`, and `Downstream Dependents` tabs on the global Activity page narrow the feed to entities the signed-in user is bound to as an [Owner](/configuration-and-deployment/enable-security/authorization/owners.md). The binding lives in the platform's `user_owner_mapping` table; without a row in that table the platform cannot answer the question "what changed on the things you own" — the three personalised tabs silently render an empty feed.

The binding is created in **Management → Associations** and the workflow is documented at [Authorization → User-owner association](/configuration-and-deployment/enable-security/authorization/user-owner-association.md). The same prerequisite applies to the [Alerts → My Objects](/features/active-platform-features/alerting.md) tab.

{% hint style="info" %}
**Pre-flight checklist for platform admins.** If you are setting up Roles, Policies, and Owners for other people and your own `My Objects` tabs (here on Activity, on Alerts, on the recommended entities surface) render empty even though the catalog is populated, you likely have **no `user_owner_mapping` for your own account**. Adding yourself as an owner on the entities you want surfaced — Management → Associations → Create association — fixes the silent-empty failure mode without changing anyone else's access.
{% endhint %}

## Filters on the global Activity page

The Filters panel on the Activity page lets you narrow the feed by seven facets:

* **Calendar** — restrict to a date / date-range window.
* **Datasource** — limit to events on entities from a specific datasource.
* **Namespace** — limit to events on entities in a given namespace.
* **Event type** — pick a single event type from the enumeration in the next subsection (e.g. `OWNERSHIP_CREATED`, `OPEN_ALERT_RECEIVED`).
* **Tag** — show events on entities carrying one or more selected tags (multi-select).
* **Owner** — show events on entities with one or more selected owners (multi-select). Useful for "what happened to my team's data this week".
* **User** — narrows to events on **entities owned by the selected user's owner binding** (the dropdown lists Owners; the query parameter resolves user → owner via `user_owner_mapping` and filters by `OWNER_ID`). **This is an entity-ownership axis, not an actor axis.** It does **not** answer "what did Alice do last week" — for that question, query the API directly against the `created_by` column. The filter label and tooltip currently overstate the axis; a future rename or rebind is on the platform roadmap.

## Event types

The **Event type** filter on the global Activity page exposes the following event types, grouped here by the area of the metadata they describe.

**Data entity lifecycle**

* `DATA_ENTITY_CREATED` – a data entity was created in the platform.
* `DATA_ENTITY_STATUS_UPDATED` – an entity's status changed (`UNASSIGNED`, `DRAFT`, `STABLE`, `DEPRECATED`, `DELETED`). This is the event to filter on to find entity deletions — there is no separate "deleted" event type.
* `BUSINESS_NAME_UPDATED` – the business name of an entity was edited.
* `DESCRIPTION_UPDATED` – the description of an entity was edited.

**Ownership**

* `OWNERSHIP_CREATED` – an owner was added to a data entity.
* `OWNERSHIP_UPDATED` – an owner's role on a data entity was changed.
* `OWNERSHIP_DELETED` – an owner was removed from a data entity.

{% hint style="warning" %}
**Owner changes on a Data Entity Group cascade to the group's members, but only the group emits an event — the member entities do not.** When you add or remove an owner on a [Data Entity Group](/features/data-discovery/groups-domains.md), the platform propagates that ownership to every member entity (each member silently gains or loses the owner). The feed records a single `OWNERSHIP_CREATED` / `OWNERSHIP_UPDATED` / `OWNERSHIP_DELETED` event on the **group**; the per-member ownership writes emit **no event**. An auditor asking "when did this child entity gain owner X" by reading the child's Activity tab sees nothing — the change has to be inferred from the parent group's event and its membership at that time. If you need a per-entity audit of cascaded ownership, reconstruct it from the group event plus the group's member list, or instrument it at the database layer.
{% endhint %}

**Tags and terms**

* `TAG_ASSIGNMENT_UPDATED` – tags on a data entity were added, removed, or changed.
* `DATASET_FIELD_TAGS_UPDATED` – tags on a dataset field (column) were added, removed, or changed. Carries before-and-after tag lists.
* `TERM_ASSIGNMENT_UPDATED` – terms linked to a data entity were added, removed, or changed.

{% hint style="warning" %}
**Tag-change events are non-uniform across the three tag-assign endpoints.** Data-entity tag changes emit `TAG_ASSIGNMENT_UPDATED`; dataset-field (column) tag changes emit `DATASET_FIELD_TAGS_UPDATED` with full before-and-after payload; **term tag changes emit no activity event at all today**. An auditor querying "who changed which tags when" needs different decoders per target type, and must compensate for term tag changes by polling the term's current tag list and diffing externally. See [Manual Object Tagging → Known limitations and operator caveats](/features/data-discovery/tagging.md#known-limitations-and-operator-caveats) for the full operator framing.
{% endhint %}

**Dataset fields (columns)**

* `DATASET_FIELD_VALUES_UPDATED` – enum values configured on a dataset field were edited.
* `DATASET_FIELD_DESCRIPTION_UPDATED` – a field's description was edited.
* `DATASET_FIELD_INTERNAL_NAME_UPDATED` – a field's internal (user-defined) name was edited.
* `DATASET_FIELD_TAGS_UPDATED` – tags on a dataset field were added, removed, or changed.
* `DATASET_FIELD_TERM_ASSIGNMENT_UPDATED` – terms linked to a dataset field were added, removed, or changed.

**Data entity groups**

* `CUSTOM_GROUP_CREATED` – a custom [data entity group](/features/data-discovery/groups-domains.md) was created.
* `CUSTOM_GROUP_UPDATED` – the members or metadata of a custom group were changed.

{% hint style="warning" %}
**DEG-membership mutations (add / remove an entity from a DEG) emit no Activity Feed event.** The `CUSTOM_GROUP_UPDATED` event above fires only when the DEG's own metadata (description, tags, owners, terms) is edited, not when the membership set changes. The two membership-write endpoints (`POST /api/dataentities/{id}/data_entity_group` + `DELETE /api/dataentities/{id}/data_entity_group/{group_id}`) emit nothing — `DATA_ENTITY_RELATION_UPDATED` exists in the enum but is a dead value not triggered by any code path today. Auditors querying activity for a DEG's membership history must instrument it externally. See [Data Entity Groups & Domains → Managing DEG Membership](/features/data-discovery/groups-domains.md#managing-deg-membership) for the operator workflow and the mitigation list.
{% endhint %}

**Alerts**

* `OPEN_ALERT_RECEIVED` – a new alert was opened for a data entity. Driven by the [Alerting](/features/active-platform-features/alerting.md) subsystem.
* `RESOLVED_ALERT_RECEIVED` – an existing alert was resolved.
* `ALERT_STATUS_UPDATED` – the status of an alert was changed manually.
* `ALERT_HALT_CONFIG_UPDATED` – the [per-entity alert halt configuration](/features/active-platform-features/alerting.md#halt-notifications-per-entity) was changed.

{% hint style="info" %}
The platform emits a few additional internal event types (entity overview / metadata / schema / relation updates) that are recorded on the entity's own Activity tab but are intentionally hidden from the global Activity filter to keep the feed concise.
{% endhint %}

{% hint style="warning" %}
**Custom-metadata mutations (create / update / delete a field value on an entity) emit no Activity Feed event today.** The `CUSTOM_METADATA_CREATED`, `CUSTOM_METADATA_UPDATED`, and `CUSTOM_METADATA_DELETED` values exist in the `ActivityEventTypeDto` enum, but no code path emits any of them — they are dead values, same shape as the `DATA_ENTITY_RELATION_UPDATED` DEG-membership case above. An entity's Activity tab will not show metadata-value changes. For compliance teams that need a who-changed-what-when audit on metadata mutations, instrument externally — see [Custom metadata → Known limitations](/features/data-discovery/custom-metadata.md#known-limitations-and-operator-caveats) and [Audit trail scope](/configuration-and-deployment/enable-security/audit-trail-scope.md) for the compensating-controls catalogue.
{% endhint %}

{% hint style="warning" %}
**Scope: the Activity Feed is structurally data-entity-only.** The activity table's primary foreign key — `activity.data_entity_id` — is `NOT NULL`, which means **every emitted event is anchored to a single catalog data entity**. RBAC mutations (Policy / Role / Permission CREATE / UPDATE / DELETE), Owner CRUD, Term CRUD, Namespace CRUD, Datasource registration, Collector token rotation, and the Integration Wizard's configuration writes have no data-entity to anchor against and therefore **cannot emit even if a future code change tried to annotate them**. The schema, not just the absence of `@ActivityLog` annotations, blocks platform-wide audit on the feed.

For compliance teams that need RBAC / lifecycle / configuration audit, this is the absence the platform-side instrumentation does not fill — operators wire external audit at the layer that **can** see the change: an API-gateway access log (records every authenticated mutation), the PostgreSQL WAL via `pgaudit` (records every SQL write), or the application log if the JVM is run with the appropriate framework. For the full audit-vs-no-audit picture and the compensating-controls catalogue, see [Audit trail scope](/configuration-and-deployment/enable-security/audit-trail-scope.md).
{% endhint %}

## Known caveats

A few behaviours of the Activity Feed are non-obvious from the page above. Each item below states what an operator might assume, what the platform actually does, and what to do today.

{% hint style="warning" %}
**By default, every authenticated user reads every entity's activity history.** The repository query behind the global feed has no per-owner predicate — the `All` tab is a cross-team feed. The `My Objects` / `Upstream Dependents` / `Downstream Dependents` tabs scope by entity-ownership (the entities the signed-in user is bound to as an Owner), not by actor-axis (events the signed-in user performed). Treat the Activity Feed as catalog-read-collaborative — anyone who can read the catalog can read every team's activity history. If your deployment requires per-team audit isolation, enforce it at the network perimeter rather than relying on platform RBAC on this surface.
{% endhint %}

{% hint style="warning" %}
**Under `auth.type=DISABLED`, mutations are attributed to no actor and render visually identical to system events.** Anonymous callers under DISABLED mode trigger activity events whose `created_by` column is null; the UI shows these alongside the auto-resolved-alert-style "system event" lozenge. An auditor walking a DISABLED-mode feed cannot distinguish "anonymous user did X" from "platform did X." DISABLED mode is non-production by design (see [DISABLED authentication](/configuration-and-deployment/enable-security/authentication/disabled-authentication.md)); the Activity Feed's null-actor rendering is one more reason not to run DISABLED in production.
{% endhint %}

{% hint style="warning" %}
**A failure writing the activity event rolls back the change the operator was making.** The event is emitted inside the same database transaction as the metadata mutation that triggered it — the platform runs the originating write (an ownership change, a description edit, a tag assignment) and the activity-event insert atomically. If the activity insert fails for any reason (a transient database error, a connection drop mid-write), the **whole transaction rolls back**: the operator's change is reverted and the request returns `500` with no indication that the mutation was undone. This is the opposite of the usual "audit-or-warn" expectation — here the audit write is load-bearing for the mutation itself, not a fire-and-forget side channel. In practice this is rare and self-correcting (retry the edit), but operators debugging a "my change didn't stick and I got a 500" report should know the activity-feed write is in the critical path.
{% endhint %}

## Per-entity Activity tab differences

The per-entity `Activity` tab on a data-entity detail page exposes a smaller filter surface than the global Activity page does:

| Feature                                      | Global Activity page                                                   | Per-entity Activity tab                                   |
| -------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------- |
| Tab axis                                     | `All` / `My Objects` / `Upstream Dependents` / `Downstream Dependents` | None — the tab is implicitly "all events for this entity" |
| Calendar filter                              | Yes                                                                    | Yes                                                       |
| Event type filter                            | Yes                                                                    | Yes                                                       |
| User filter                                  | Yes (entity-ownership axis — see the caveat above)                     | Yes (same axis)                                           |
| Datasource / Namespace / Tag / Owner filters | Yes                                                                    | No (the entity is already fixed)                          |

The per-entity tab is the right surface for "what changed on this one entity, in this window" investigations; the global page is the right surface for "what changed across the catalog this week."

{% hint style="warning" %}
**The per-entity Activity tab is reachable for any data entity, including soft-deleted ones.** The route mounts without a permission gate or status-restriction; the underlying read endpoint applies no per-owner scoping. Any authenticated user can navigate to `/dataentities/{id}/activity` for any catalogued entity, and the tab returns the audit history even when the entity's status is `DELETED` (the catalog list endpoints hide soft-deleted entities, but the activity tab does not). Multi-tenant deployments expecting per-team isolation on entity-level audit history cannot enforce it on this surface today. The same cross-channel read posture applies to other per-entity read surfaces (see [Search and Filtering → Facet aggregators](/features/data-discovery/search.md#facet-aggregators-enumerate-cross-owner-data-by-default) for the wider pattern).
{% endhint %}

## Auto-resolved alert events

Auto-resolution events emitted from the [Alerting](/features/active-platform-features/alerting.md) subsystem are recorded as system events on the feed (no operator identity attached); manually-resolved alerts carry the operator's identity. This lets a reader walking the feed distinguish between alerts that were worked on by a human and alerts that simply cleared themselves on the next ingest.

## Configuration

Activity-feed **partitioning** is controlled by the platform-level setting [`odd.activity.partition-period`](/configuration-and-deployment/odd-platform.md#activity-feed-partitioning-odd-activity-partition-period) (default 30 days) on [Configure ODD Platform](/configuration-and-deployment/odd-platform.md). The key sets the **width** of each time partition on the `activity` table — it controls how the data is chunked, not how long it is kept. Adjust the partitioning cadence per the volume your deployment generates — the operator-side reference is the canonical home for this key.

{% hint style="warning" %}
**`odd.activity.partition-period` is not a retention window — the Activity Feed has no automatic deletion and grows without bound.** The setting governs only the size of each table partition; the platform creates new partitions as time advances but never drops a partition that holds activity rows. The single housekeeping job that touches activity partitions drops only *empty* past partitions, which is a near-no-op on any steadily-used deployment. There is no `activity`-table entry in the platform's TTL configuration (unlike resolved alerts, search facets, and soft-deleted entities, which each have a housekeeping TTL). On a busy catalog the `activity` table grows monotonically; plan for it in your PostgreSQL capacity, and if you need to reclaim space, prune old partitions out-of-band at the database layer. Shrinking `partition-period` makes each partition smaller (so out-of-band pruning is finer-grained) but does not introduce any automatic deletion.
{% endhint %}

## Where to next

* For the alert events that source four of the feed's event types → [Alerting](/features/active-platform-features/alerting.md).
* For per-entity user-authored discussion threads (a per-entity event stream complementary to this system-emitted one) → [Data Collaboration](/features/active-platform-features/data-collaboration.md).
* For the data-entity-group events that power the catalog's group view → [Data Entity Groups & Domains](/features/data-discovery/groups-domains.md).
* For the activity-feed partition setting → [Configure ODD Platform → Activity-feed partitioning](/configuration-and-deployment/odd-platform.md#activity-feed-partitioning-odd-activity-partition-period).


---

# 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/features/active-platform-features/activity-feed.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.
