> For the complete documentation index, see [llms.txt](https://docs.appmixer.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.appmixer.com/api/audit-logs.md).

# Audit Logs

Read the audit log of your Appmixer tenant: who changed what, and when. The endpoint is only accessible to users with the \`admin\` scope.

Appmixer records who changed what in your tenant, and when, in the audit log. Failed attempts are recorded too. See [Logged Events](/appmixer-backoffice/audit-logs.md#logged-events) for the list of recorded events; the [Audit Logs](/appmixer-backoffice/audit-logs.md) page of the Backoffice shows the same entries.

{% hint style="info" %}
The `userId` and `externalUserId` filters, the `appliedFilters` response property and the [`X-Appmixer-Audit-External-User-Id`](#attribute-changes-to-your-own-users) header are available since Appmixer 6.5.6. Since the same version, a `size` above `100`, a malformed `page`, `size` or `ts`, and a `dateFrom` or `dateTo` that cannot be read as a date are refused with `400` instead of being accepted. The `source` and `threadId` filters are available since Appmixer 6.5.5.
{% endhint %}

## Get Audit Log Entries

<mark style="color:blue;">`GET`</mark> `[TENANT_API_URL]/system/audits`

Return the audit log entries that match the filters, newest first, one page at a time. Requires the `admin` scope.

```bash
curl "[TENANT_API_URL]/system/audits?externalUserId=staff-42&dateFrom=2026-09-01&size=50" \
  -H "Authorization: Bearer [ACCESS_TOKEN]"
```

#### Query Parameters

| Name           | Type   | Description                                                                                                                                                                                                                                       |
| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| eventType      | string | `create`, `update` or `delete`.                                                                                                                                                                                                                   |
| status         | string | `success` or `failure`.                                                                                                                                                                                                                           |
| username       | string | Username of the Appmixer user who made the change. Exact match, not case-sensitive.                                                                                                                                                               |
| userId         | string | ID of the Appmixer user who made the change.                                                                                                                                                                                                      |
| externalUserId | string | ID your application sent in the [`X-Appmixer-Audit-External-User-Id`](#attribute-changes-to-your-own-users) header. Exact match, case-sensitive.                                                                                                  |
| flowId         | string | ID of the flow the change concerns.                                                                                                                                                                                                               |
| source         | string | `copilot` returns the changes made by the [AI Copilot](https://github.com/Appmixer-ai/appmixer-docs-gitbook/tree/6.5/getting-started/ai-copilot.md).                                                                                              |
| threadId       | string | ID of the AI Copilot conversation that made the change.                                                                                                                                                                                           |
| dateFrom       | string | Entries recorded at or after this date or time, in ISO 8601, for example `2026-09-01` or `2026-09-01T08:00:00Z`. A date without a time means midnight UTC.                                                                                        |
| dateTo         | string | Entries recorded on or before this day, for example `2026-09-30`. Send a date without a time; the whole day (UTC) is included.                                                                                                                    |
| ts             | number | Entries recorded at or before this time, as a Unix timestamp in milliseconds. See [Page Through Entries](#page-through-entries).                                                                                                                  |
| search         | string | Full-text search in `action`, `flatDiff`, `status` and the username. Matches whole words; with several words, entries that contain any of them.                                                                                                   |
| sort           | string | Property to sort by, for example `created`, `eventType`, `action`, `status`, `flowId` or `flowName`. Prefix it with `-` for descending order. Default is `-created`, newest first. `source`, `threadId` and `externalUserId` cannot be sorted by. |
| page           | number | Page number, starting at `1`. Default is `1`. `0` returns all matching entries, without paging.                                                                                                                                                   |
| size           | number | Number of entries per page, `1` to `100`. Default is `10`.                                                                                                                                                                                        |
| format         | string | `csv` returns the entries as a CSV file instead of JSON. See [CSV Export](#csv-export).                                                                                                                                                           |

An entry must match all the filters you send. Leave out the filters you do not need.

{% tabs %}
{% tab title="200: OK" %}

```json
{
  "data": [
    {
      "id": "66f28a1c9b3e4d0012a7c5e1",
      "eventType": "update",
      "user": {
        "id": "66d9c1f47a2b3c0011e4f8a2",
        "username": "acme@example.com",
        "lowername": "acme@example.com"
      },
      "flowId": "9089f275-f5a5-4796-ba23-365412c5666e",
      "flowName": "Sync new bookings",
      "action": "Update flow: Sync new bookings [9089f275-f5a5-4796-ba23-365412c5666e]",
      "diff": [
        {
          "path": "flow.name",
          "type": "CHANGE",
          "changes": {
            "before": "Sync bookings",
            "after": "Sync new bookings"
          }
        }
      ],
      "flatDiff": "[{\"path\":\"flow.name\",\"type\":\"CHANGE\",\"changes\":{\"before\":\"Sync bookings\",\"after\":\"Sync new bookings\"}}]",
      "status": "success",
      "created": "2026-09-24T09:12:44.102Z",
      "externalUserId": "staff-42"
    }
  ],
  "contentRange": "0-0/1",
  "appliedFilters": ["dateFrom", "externalUserId"]
}
```

{% endtab %}

{% tab title="400: Bad Request" %}

```json
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Invalid size. Expected 1 to 100."
}
```

{% endtab %}
{% endtabs %}

The response has these properties:

| Property       | Description                                                                                                                                                                                                                                                     |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| data           | The entries on this page.                                                                                                                                                                                                                                       |
| contentRange   | The part of the whole result this page holds, as `<first>-<last>/<total>` counted from zero: `0-49/120` is the first 50 of 120 matching entries. It is also sent in the `Content-Range` response header, which a browser cannot read in a cross-origin request. |
| appliedFilters | Names of the filter parameters that were applied. See [Check That Your Filters Were Applied](#check-that-your-filters-were-applied).                                                                                                                            |

Each entry has these properties:

| Property         | Description                                                                                                                                                                                                                                                                        |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id               | ID of the entry.                                                                                                                                                                                                                                                                   |
| created          | When the entry was recorded, in ISO 8601.                                                                                                                                                                                                                                          |
| eventType        | `create`, `update` or `delete`.                                                                                                                                                                                                                                                    |
| status           | `success` or `failure`.                                                                                                                                                                                                                                                            |
| action           | Description of the event, for example `Update flow: Sync new bookings [9089f275-f5a5-4796-ba23-365412c5666e]`.                                                                                                                                                                     |
| user             | The Appmixer user who made the change: `id`, `username` and `lowername` (the username in lowercase). In a [user group](/api/user-groups.md) context, `user` is the group user, and `originalUserId`, `contextType` and `groupId` identify the person and the group. Can be `null`. |
| flowId, flowName | The flow the change concerns, or `null`.                                                                                                                                                                                                                                           |
| diff             | The changed properties, each with `path`, `type` (`CREATE`, `CHANGE` or `REMOVE`) and `changes` (`before` and `after`).                                                                                                                                                            |
| flatDiff         | `diff` as a JSON string.                                                                                                                                                                                                                                                           |
| source           | `copilot` on changes made by the AI Copilot. Present only on those entries.                                                                                                                                                                                                        |
| threadId         | ID of the AI Copilot conversation that made the change. Present only together with `source`.                                                                                                                                                                                       |
| externalUserId   | ID sent in the [`X-Appmixer-Audit-External-User-Id`](#attribute-changes-to-your-own-users) header. Present only when the request carried a valid one.                                                                                                                              |

### Check That Your Filters Were Applied

An Appmixer version that does not know a filter ignores it and returns the entries as if you had not sent it. A request for the changes of one person then returns the changes of everybody. `appliedFilters` tells the two cases apart: it lists the filter parameters the server recognized and applied, in no particular order. `sort`, `page`, `size` and `format` are not filters and are never listed.

If your code relies on a filter, fail closed:

* A response without `appliedFilters` comes from a version that predates it. Do not treat its entries as filtered.
* A filter you sent that is missing from `appliedFilters` was not applied. Do not treat the entries as filtered by it.

### Page Through Entries

To read all matching entries, request `page=1`, `page=2` and so on until you have read the total from `contentRange`. Send `ts` with the time of your first request in every request, so that entries recorded in the meantime do not shift the pages:

```bash
curl "[TENANT_API_URL]/system/audits?dateFrom=2026-09-01&size=100&page=2&ts=1790240400000" \
  -H "Authorization: Bearer [ACCESS_TOKEN]"
```

`page=0` returns all matching entries in one response and ignores `size`; the Backoffice CSV export uses it. Narrow the result first, for example with a date range: the response holds every entry that matches.

### CSV Export

With `format=csv`, the response is a CSV file (`text/csv`, downloaded as `data.csv`) instead of JSON. Filters, `sort`, `page` and `size` work as for JSON, so add `page=0` to export all matching entries rather than one page. The first row names the columns, in this order:

`id`, `flowId`, `flowName`, `action`, `created`, `eventType`, `user`, `status`, `flatDiff`, `source`, `threadId`, `externalUserId`

The `user` column holds the username. A filter that matches nothing exports the header row alone.

The CSV has no `appliedFilters`. To check that the server applies your filters, send the same request without `format=csv` first.

### Errors

The request fails with `400 Bad Request` when:

* `page` is not a whole number of `0` or more.
* `size` is not a whole number from `1` to `100`. With `page=0`, `size` has no upper limit.
* `eventType`, `status` or `source` has a value other than those listed above.
* `sort` names a property that cannot be sorted by.
* `userId` is not a 24-character hexadecimal ID.
* `dateFrom` or `dateTo` cannot be read as a date, for example because it is a Unix timestamp, or `dateTo` is before `dateFrom`.
* `ts` is not a whole number, or is too large for a date.

It fails with `401 Unauthorized` without a valid access token, and with `403 Forbidden` for a user without the `admin` scope.

## Attribute Changes to Your Own Users

If your application works through one Appmixer user for many people, for example one [virtual user](/tutorials/appmixer-virtual-users.md) per customer organization, every audit log entry names that one Appmixer user. To record which person in your application made a change, send their ID in the `X-Appmixer-Audit-External-User-Id` header of the request that makes it:

```bash
curl -XPUT "[TENANT_API_URL]/flows/9089f275-f5a5-4796-ba23-365412c5666e" \
  -H "Authorization: Bearer [ACCESS_TOKEN]" \
  -H "X-Appmixer-Audit-External-User-Id: staff-42" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Sync new bookings" }'
```

The entry of this change gets `"externalUserId": "staff-42"`. Filter by it with `externalUserId=staff-42`; the CSV export has it in the last column.

With the Appmixer SDK, set its [`externalUserId`](/appmixer-ui-sdk/constructor.md#externaluserid) option instead of sending the header yourself. The SDK then sends it with every request to the Appmixer API, including the requests of the Designer and the AI Copilot.

* The value must be 1 to 128 characters long and use only letters, digits, `.`, `_` and `-`, for example a UUID or a numeric ID. A value in any other format is ignored: the request still succeeds, and its entry is recorded without `externalUserId`.
* You can send the header with every request. Requests that do not write an audit log entry ignore it.
* Send it with [AI Copilot chat](https://github.com/Appmixer-ai/appmixer-docs-gitbook/tree/6.5/getting-started/ai-copilot.md#chat) requests too. The flows the Copilot creates or changes while answering are then recorded with the same `externalUserId`.

{% hint style="warning" %}
**Send an opaque ID, never a name, an email address or other personal data.** Appmixer stores the value exactly as sent, returns it from this endpoint and exports it in the CSV until the entry expires (see [Retention](#retention)). Entries cannot be deleted earlier, and the people behind the IDs are not Appmixer users, so there is no way to erase the entries of one person on request.
{% endhint %}

{% hint style="warning" %}
**The header is attribution, not authentication.** Appmixer records the value it receives without verifying it, so any caller allowed to make a change can name anyone. Use `externalUserId` for reporting and investigations, never to decide who may access what.
{% endhint %}

{% hint style="danger" %}
**From browser code, send the header only to Appmixer 6.5.6 or later.** Older versions refuse it in the CORS preflight, so the browser blocks the whole request. Server-side requests are not affected.
{% endhint %}

## Retention

By default, audit log entries are deleted 100 days after they were recorded. On Appmixer Self-Managed, set the number of days with the `GARBAGE_COLLECTOR_AUDIT_LOGS_TTL` environment variable on the Appmixer engine instances.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

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

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.appmixer.com/api/audit-logs.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
