-
Notifications
You must be signed in to change notification settings - Fork 1
QUA-1806: Platform Audit documentation #1150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
RafaelOsiro
wants to merge
5
commits into
main
Choose a base branch
from
qua-1806-platform-audit
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,176
−29
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
b837209
docs(settings): add Platform Audit documentation and restructure over…
RafaelOsiro 00947d1
docs(security): add Audit and Defaults tabs to user role permissions
RafaelOsiro 946acd9
chore(claude): consolidate conventions and sanitize internal references
RafaelOsiro 5e4af7b
docs(audit): apply Greptile review feedback
RafaelOsiro fd35e78
docs(audit): fix login-claim in best-practices timeframe table
RafaelOsiro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+417 KB
docs/assets/settings/audit/how-tos/filter-activity/timeframe-dropdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+434 KB
docs/assets/settings/audit/how-tos/summary-section/summary-cards-annotated.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| # :material-api:{ .middle style="color: var(--q-brick)" } Audit API | ||
|
|
||
| The activity endpoints power the Platform Audit page in the UI. Both endpoints require the **Admin** user role and accept a shared set of filters so the activity log, summary cards, and chart can stay in sync. | ||
|
|
||
| !!! tip | ||
| For complete API documentation, including request and response schemas, visit the [API docs](https://your-instance.qualytics.io/api/docs){:target="_blank"} on your Qualytics deployment. | ||
|
|
||
| All endpoints are served from your Qualytics deployment (e.g., `https://your-instance.qualytics.io`). The paths below include the `/api` prefix. | ||
|
|
||
| --- | ||
|
|
||
| ## List Activity | ||
|
|
||
| Return a paginated list of activity records, optionally filtered by user, action, date range, or timeframe window. | ||
|
|
||
| **Endpoint**: `GET /api/activity` | ||
|
|
||
| **Permission**: Admin user role | ||
|
|
||
| ### Query parameters | ||
|
|
||
| | Parameter | Type | Description | | ||
| | :--- | :--- | :--- | | ||
| | `users` | `list[int]` | One or more user IDs. When set, only activity attributed to the listed users is returned. | | ||
| | `actions` | `list[str]` | One or more action verbs (`create`, `update`, `delete`, and others). Filters to the listed verbs only. | | ||
| | `start_date` | `date` | The earliest date to include. Use with `end_date` for an explicit range. | | ||
| | `end_date` | `date` | The latest date to include. Use with `start_date` for an explicit range, or with `timeframe` as the anchor. | | ||
| | `timeframe` | `string` | One of `week`, `month`, `quarter`, `year`. When set, derives `start_date` from `end_date`. | | ||
| | `offset` | `int` | Timezone offset in minutes. Applied to the timeframe window boundaries so the date range honors the caller's local calendar. | | ||
| | `include_internal` | `bool` | When `true` (the default), includes activity from internal users. Set to `false` to exclude them. | | ||
| | `page` | `int` | Page number, starting at 1. | | ||
| | `size` | `int` | Page size. | | ||
|
|
||
| ??? example "Example request and response" | ||
|
|
||
| **Request**: | ||
|
|
||
| ```bash | ||
| curl -X GET "https://your-instance.qualytics.io/api/activity?timeframe=week&end_date=2026-06-10&include_internal=false&page=1&size=20" \ | ||
| -H "Authorization: Bearer YOUR_TOKEN" | ||
| ``` | ||
|
|
||
| **Response**: | ||
|
|
||
| ```json | ||
| { | ||
| "items": [ | ||
| { | ||
| "transaction": { | ||
| "id": 184221, | ||
| "issued_at": "2026-06-10T18:42:11Z", | ||
| "user": { | ||
| "id": 14, | ||
| "name": "Yannique Kameka", | ||
| "user_name": "yannique", | ||
| "email": "yannique@example.com" | ||
| } | ||
| }, | ||
| "verb": "update", | ||
| "object_type": "QualityCheck", | ||
| "object_id": 9821 | ||
| } | ||
| ], | ||
| "total": 134, | ||
| "page": 1, | ||
| "size": 20, | ||
| "pages": 7 | ||
| } | ||
| ``` | ||
|
|
||
| ### Response schema | ||
|
|
||
| | Field | Type | Description | | ||
| | :--- | :--- | :--- | | ||
| | `items` | `list[object]` | The page of activity records. | | ||
| | `items[].transaction.id` | `integer` | Unique transaction identifier. | | ||
| | `items[].transaction.issued_at` | `string` | The action timestamp in UTC. | | ||
| | `items[].transaction.user` | `object` or `null` | The actor (display name, username, email). `null` when the user account no longer exists. | | ||
| | `items[].verb` | `string` | The action verb (`create`, `update`, `delete`, `activate`, `archive`, and so on). | | ||
| | `items[].object_type` | `string` | The CamelCase entity type (`Datastore`, `QualityCheck`, `Anomaly`, and others). | | ||
| | `items[].object_id` | `integer` or `null` | The ID of the affected entity, when applicable. | | ||
| | `total` | `integer` | Total number of records matching the filters. | | ||
| | `page` | `integer` | Current page number. | | ||
| | `size` | `integer` | Page size. | | ||
| | `pages` | `integer` | Total number of pages. | | ||
|
|
||
| !!! info | ||
| For the UI equivalent, see [Filter Activity](how-tos/filter-activity.md){:target="_blank"}. | ||
|
|
||
| --- | ||
|
|
||
| ## Activity Insights | ||
|
|
||
| Return aggregated activity statistics for the timeframe window. This is the endpoint that powers the summary cards and the activity chart. | ||
|
|
||
| **Endpoint**: `GET /api/activity/insights` | ||
|
|
||
| **Permission**: Admin user role | ||
|
|
||
| ### Query parameters | ||
|
|
||
| | Parameter | Type | Description | | ||
| | :--- | :--- | :--- | | ||
| | `report_date` | `date` | The end of the timeframe window. Defaults to today when omitted. | | ||
| | `timeframe` | `string` | One of `week`, `month`, `quarter`, `year`. Defaults to `month`. | | ||
| | `offset` | `int` | Timezone offset in minutes. Applied before date extraction so daily bucketing in `actions_per_day` groups by the caller's local calendar date. | | ||
| | `include_internal` | `bool` | When `true` (the default), includes internal users in `total_actions`, `top_actor`, `top_entity_type`, and `actions_per_day`. The `unique_actors` count always excludes internal users. | | ||
| | `users` | `list[int]` | Limit the aggregation to one or more user IDs. | | ||
| | `actions` | `list[str]` | Limit the aggregation to one or more action verbs (`create`, `update`, `delete`, and so on). | | ||
|
|
||
| ??? example "Example request and response" | ||
|
|
||
| **Request**: | ||
|
|
||
| ```bash | ||
| curl -X GET "https://your-instance.qualytics.io/api/activity/insights?timeframe=week&report_date=2026-06-10&include_internal=true" \ | ||
| -H "Authorization: Bearer YOUR_TOKEN" | ||
| ``` | ||
|
|
||
| **Response**: | ||
|
|
||
| ```json | ||
| { | ||
| "unique_actors": 7, | ||
| "total_actions": 134, | ||
| "top_actor": { | ||
| "id": 14, | ||
| "name": "Yannique Kameka", | ||
| "user_name": "yannique", | ||
| "email": "yannique@example.com", | ||
| "action_count": 48 | ||
| }, | ||
| "top_entity_type": { | ||
| "object_type": "Anomaly", | ||
| "count": 62 | ||
| }, | ||
| "actions_per_day": [ | ||
| { "date": "2026-06-04", "user_count": 18, "system_count": 4 }, | ||
| { "date": "2026-06-05", "user_count": 11, "system_count": 6 }, | ||
| { "date": "2026-06-06", "user_count": 0, "system_count": 0 }, | ||
| { "date": "2026-06-07", "user_count": 0, "system_count": 0 }, | ||
| { "date": "2026-06-08", "user_count": 22, "system_count": 7 }, | ||
| { "date": "2026-06-09", "user_count": 25, "system_count": 8 }, | ||
| { "date": "2026-06-10", "user_count": 28, "system_count": 5 } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ### Response schema | ||
|
|
||
| | Field | Type | Description | | ||
| | :--- | :--- | :--- | | ||
| | `unique_actors` | `integer` | Number of distinct *human* users with at least one action in the window. Always excludes internal users. | | ||
| | `total_actions` | `integer` | Total number of actions in the window. Respects `include_internal`. | | ||
| | `top_actor` | `object` or `null` | The user with the highest action count in the window. Includes `id`, `name`, `user_name`, `email`, and `action_count`. | | ||
| | `top_entity_type` | `object` or `null` | The entity type with the highest action count. Includes `object_type` (CamelCase) and `count`. | | ||
| | `actions_per_day` | `list[object]` | One entry per day in the window, with `date`, `user_count`, and `system_count`. The page aggregates these into wider buckets (week, month, quarter) when needed. | | ||
|
|
||
| !!! info | ||
| For the UI equivalent, see [Summary Section](how-tos/summary-section.md){:target="_blank"}. | ||
|
|
||
| --- | ||
|
|
||
| ## Error Responses | ||
|
|
||
| | Status Code | Description | | ||
| | :--- | :--- | | ||
| | `401 Unauthorized` | Missing or invalid API token. | | ||
| | `403 Forbidden` | User does not have the **Admin** role. | | ||
| | `422 Unprocessable Entity` | Invalid `timeframe`, `offset`, or date value. | | ||
|
|
||
| ??? example "Error response examples" | ||
|
|
||
| **403 Forbidden**: | ||
|
|
||
| ```json | ||
| { "detail": "Not enough permissions" } | ||
| ``` | ||
|
|
||
| **422 Unprocessable Entity** (invalid timeframe): | ||
|
|
||
| ```json | ||
| { | ||
| "detail": [ | ||
| { | ||
| "loc": ["query", "timeframe"], | ||
| "msg": "value is not a valid enumeration member; permitted: 'week', 'month', 'quarter', 'year'", | ||
| "type": "type_error.enum" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Permission Summary | ||
|
|
||
| | Operation | Minimum Permission | | ||
| | :--- | :--- | | ||
| | List activity | Admin user role | | ||
| | Get activity insights | Admin user role | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # Best Practices | ||
|
|
||
| Recommendations for getting the most out of the Platform Audit page during rollout, governance reviews, and day-to-day operations. | ||
|
|
||
| ## Use the Report Date as a fixed anchor | ||
|
|
||
| The Report Date is the *end* of the timeframe window, not the start. When investigating a specific incident, set the Report Date to the day after the event you are looking into and then choose the timeframe that gives you the right amount of context (Week for a localized review, Month for a longer trend). | ||
|
|
||
| If you change the Report Date while keeping the Timeframe constant, both the cards and the chart slide together: they always represent the same window. | ||
|
|
||
| ## Match the timeframe to the question | ||
|
|
||
| | Question | Suggested timeframe | | ||
| | :--- | :--- | | ||
| | Did this user take any actions yesterday? | Week | | ||
| | Who has been most active this month? | Month | | ||
| | Is platform usage trending up or down? | Quarter or Year | | ||
| | Are there spikes in delete activity around a release? | Week, anchored to the day after the release | | ||
|
|
||
| A smaller timeframe gives finer chart bars (daily) at the cost of less historical context; a larger timeframe summarizes more history but coarsens the chart to weekly, monthly, or quarterly bars. | ||
|
|
||
| ## Separate user activity from automation early | ||
|
|
||
| When trying to understand human adoption, enable the **Hide Qualytics** toggle so automated scheduled activity is removed from both the chart and the log. The **Active users** metric ignores internal users in both modes, but the other cards and the chart segments are easier to read with automation hidden. | ||
|
|
||
| When investigating an operational anomaly (a runaway loop, a misconfigured schedule, an unexpected backfill), leave the toggle off so the **Automated** chart segment is visible alongside user activity. | ||
|
|
||
| ## Filter by actor or action before exporting | ||
|
|
||
| The CSV export is **page-scoped**: it exports only the rows currently visible on the page. To export a focused dataset, narrow the log with the filters first (specific users, specific actions) and pick a page size that fits the population. Each export is a point-in-time snapshot. For full retrospective analysis, prefer the API. | ||
|
|
||
| ## Cross-reference with entity history | ||
|
|
||
| The Audit page is a workspace-wide log. When you spot something suspicious, drill into the affected entity (datastore, check, anomaly) and use its dedicated **History** tab to see the per-entity change record with full before/after values. The Audit page tells you *who, when, and what kind of action*; the entity history tells you *exactly which fields changed*. | ||
|
|
||
| ## Use the chart as a leading indicator | ||
|
|
||
| The stacked bar chart makes step-changes in activity easy to spot at a glance: | ||
|
|
||
| - **Tall user bars** clustered on a few days suggest concentrated rollout activity (training, onboarding, bulk imports). | ||
| - **Tall automated bars** spread evenly across the window indicate steady scheduled workloads. | ||
| - **Empty days** between bars in a Week or Month view often signal weekends or quiet periods. | ||
|
|
||
| Pair a glance at the chart with a click into the corresponding day in the log when an unusually tall bar warrants explanation. | ||
|
|
||
| ## Schedule regular reviews | ||
|
|
||
| For governance, schedule a recurring review of the Audit page: for example, the first business day of each month with the Timeframe set to **Month** and the Report Date set to the prior month's end. Export the relevant pages for the audit trail you maintain outside of Qualytics, and document any unexpected actor or action concentrations in your change-management notes. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| # :material-file-cog:{ .middle style="color: var(--q-brick)" } How It Works | ||
|
|
||
| This page explains the mechanics behind the Platform Audit page: how the timeframe drives what activity the page shows, how rows are grouped, how the chart adjusts its bucketing, and how internal (automated) activity is separated from user activity. | ||
|
|
||
| ## How the page is populated | ||
|
|
||
| The Audit page combines two complementary data sources into a single screen: | ||
|
|
||
| | Source | Drives | | ||
| | :--- | :--- | | ||
| | Activity records | The paginated activity log: one row per recorded action, sorted by issue time. | | ||
| | Activity insights | The four summary cards and the stacked activity chart. | | ||
|
|
||
| Both sources share the same set of filters (date, timeframe, users, actions, and the **Hide Qualytics** toggle), so the cards, chart, and log always agree on the population they represent. | ||
|
|
||
| ## Report Date and Timeframe | ||
|
|
||
| The page is anchored to a **Report Date** and a **Timeframe**. Together, these define the window of activity that the cards, chart, and log all show. | ||
|
|
||
| | Timeframe | Window | | ||
| | :--- | :--- | | ||
| | Week | The seven days ending on the Report Date. | | ||
| | Month | The prior month ending on the Report Date (roughly 30 days, varying by calendar month). | | ||
| | Quarter | The prior three months ending on the Report Date (roughly 90 days). | | ||
| | Year | The prior year ending on the Report Date (365 or 366 days on leap years). | | ||
|
|
||
| The Report Date defaults to today and cannot be set to a future date. Changing either control refetches the summary cards, chart, and activity log, and resets pagination to page 1. | ||
|
|
||
| ## Day grouping in the activity log | ||
|
|
||
| Rows in the activity log are grouped by **the local calendar day on which each action was issued**. Each group has a header showing the day label and the count of items in that group *on the current page*. | ||
|
|
||
| This means: | ||
|
|
||
| - A single day can span multiple pages if it has more activity than the current page size allows. In that case, the same day header appears on each page, with a count of items shown on that page only. | ||
| - A page can contain multiple day groups when activity is light, in which case each group header marks the boundary. | ||
|
|
||
| The day used for grouping comes from your local timezone, not raw UTC, so each row is grouped under the day it appeared to happen for you. | ||
|
|
||
| ## Timezone handling | ||
|
|
||
| The page uses your browser's local timezone in two places: | ||
|
|
||
| - Day grouping in the activity log. | ||
| - Bucket grouping in the activity chart. | ||
|
|
||
| This means the same audit log opened from two different timezones can show different day groupings for actions that occurred close to midnight UTC. That is intentional: each viewer sees activity grouped by their own local day. | ||
|
|
||
| ## Activity chart bucketing | ||
|
|
||
| The chart is a stacked bar showing **User** vs **Automated** activity. The bucket width adjusts to keep the chart readable for the selected timeframe: | ||
|
|
||
| | Timeframe | Bucket | | ||
| | :--- | :--- | | ||
| | Week | One bar per **day** | | ||
| | Month | One bar per **week** | | ||
| | Quarter | One bar per **month** | | ||
| | Year | One bar per **quarter** | | ||
|
|
||
| Activity is recorded as daily counts and aggregated into wider buckets based on the selected timeframe. Buckets with zero activity display as low, muted bars so the time axis stays continuous. | ||
|
|
||
| ## User vs Automated split | ||
|
|
||
| Each action is attributed to a user. The platform tags certain accounts as **internal**, for example the Qualytics system account that runs scheduled operations and other platform-driven automation. The chart and summary cards treat internal users as **Automated** activity (the secondary bar segment) and external human users as **User** activity (the primary bar segment). | ||
|
|
||
| The **Hide Qualytics** toggle in the filter menu removes internal activity from both the chart and the log. When the toggle is off (the default), internal activity is shown alongside user activity. The **Active users** card always reports the count of unique *human* actors, regardless of the toggle; the toggle only affects the rest of the page. | ||
|
|
||
| ## The masking audit overlay | ||
|
|
||
| The activity log can also include data-masking audit rows. These appear when a user toggles "Show masked values" in surfaces such as Dry Run results or source-records views. They use the same row layout as standard activity items, but clicking them opens a dedicated masking-audit side panel rather than the generic activity side panel. | ||
|
|
||
| ## Empty and loading states | ||
|
|
||
| - **First load**: the log shows a stack of skeleton rows while the first page is fetched. | ||
| - **Subsequent loads**: the previously visible page stays in place while the new page is fetched and shown. The chart keeps the previously loaded data to avoid intermediate flicker. | ||
| - **No results**: when no activity matches the filters, the log shows a "No activity found" empty state with a history icon. The Active users and Total actions cards display `0`, the Most active user and Top category cards fall back to dash placeholders, and the chart shows a zero-only bar series. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.