# Get system event logs

`GET /api/system/event-logs`

- **Success:** `200 OK`
- **Products:** Operator (`FOH`)
- **Features:** Front of House Generic Access (`FOHGENERIC`)

HTML version: [Get system event logs](https://support.gladstonesoftware.cloud/reference/configuration/get-system-event-logs)

## Overview

Pages through the operator's system-level audit trail — the record of changes that are not attached to any one member. Events that *are* attached to a member, such as a member logging in or a member's status changing, live in a separate log with its own endpoint, [Get member event logs](../members/get-member-event-logs.md); nothing here is filtered by member.

Each entry names an event type and, where it was supplied, the record and field that changed with their before and after values. Typical system events are third-party integrations being activated or deactivated and booking-sheet exceptions being created, updated or deleted.

**There is no filtering.** The only query parameters are `limit`, `continuation-token` and `sort-by`. You cannot ask for a date range, an event type, an entity or a user, and there is no default window — the endpoint pages the entire log from the beginning of time. If you want events since your last poll, sort by `eventLogId` or `eventDateTimeUtc` and keep the continuation token between runs, or filter client-side after fetching. Budget for that: an established operator's log is long, and you are paging all of it.

**Paging is keyset-based, and `links.next` is always present.** Each page is ordered by the sort property and then by `eventLogId`, and the continuation token encodes the last entry of the page you just received. There is no total count, no page number and no "last page" flag — and crucially, when a page comes back empty the endpoint echoes the token you sent rather than dropping the link. **Stop when `data` is empty, not when `links.next` disappears**, because it never disappears. Following it forever otherwise gives you an infinite loop.

**Results are not capped.** `limit` defaults to `100` and no maximum is enforced, so a large value is honoured as sent. Keep pages to a size your client can handle.

## Endpoint

`GET {base_url}/api/system/event-logs`

`{base_url}` is the environment URL issued to your integration. See [Authenticating with Gladstone APIs](../../guides/authenticating-with-gladstone-apis.md).

## Query parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `limit` | integer (int32) | No | How many entries to return in this page. Defaults to `100` when omitted. No upper bound is enforced, so you get as many entries as you ask for. The value is also echoed into `links.next`, so a limit you set on the first call carries through the whole traversal without you resending it. |
| `continuation-token` | string | No | Where to resume from. Omit it on the first call. Afterwards, take it from the `continuation-token` parameter inside `links.next` — or just request `links.next` whole. The format is the sort value and the entry's `eventLogId` joined by an underscore, with the literal `null` standing in for an absent sort value; a token that is not two underscore-separated parts, or whose second part is not an integer, is rejected. Because it is keyset-based rather than offset-based, entries written between your calls do not shift the pages you have already read. |
| `sort-by` | string | No | Orders the results, in the form `propertyName:asc` or `propertyName:desc`. Only three properties are accepted, and they are matched case-sensitively: `eventDateTimeUtc`, `eventId` and `eventLogId`. Anything else is rejected with `400` and the message `Invalid sort-by property.` Omitting the parameter sorts by `eventLogId` ascending, which is effectively insertion order. Omitting the direction — `?sort-by=eventDateTimeUtc` — also sorts ascending. Do not change `sort-by` part-way through a traversal: the continuation token is interpreted against the sort property, so mixing them silently skips or repeats entries. |

## Sample request

```bash title="cURL"
curl -X GET '{base_url}/api/system/event-logs?limit=50&sort-by=eventDateTimeUtc:desc' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true'
```

## Response fields

**`statusCode` and `errorMessage` never appear in the body.** They are listed below only for completeness. On success you get `data` and `links` and nothing else; on failure the body is a bare string, not this envelope. Ignore both rows.

| Field | Type | Description |
|-------|------|-------------|
| `statusCode` | integer (int32) | Never present in the response body. |
| `errorMessage` | string | Never present in the response body. When the request fails, the message is returned as the entire body of the `400` instead, as a plain string. |
| `data` | array of object | The page of log entries, ordered by the sort property and then by `eventLogId`. An empty array means you have reached the end of the log — this is the only reliable end-of-traversal signal. |
| `data[].eventLogId` | integer (int32) | The entry's unique, platform-assigned identifier, increasing in insertion order. It is the tie-breaker in every sort and the id half of the continuation token, so it is also the value to store if you are resuming a poll later. |
| `data[].eventDateTimeUtc` | string (date-time) | When the event happened, in UTC, set at the point the event was raised rather than when it was recorded. Because entries are recorded asynchronously, ordering by this field can differ slightly from ordering by `eventLogId`. |
| `data[].eventId` | integer (int32) | The event type code. Codes below `1000` are inherited from the older platform — for example `1` user login, `2` user logout, `43` user password reset, `64` booking deleted — and codes from `1000` up are current-platform events, including `1010` third-party integration activated, `1011` third-party integration deactivated and `1013`–`1015` booking-sheet exceptions created, updated and deleted. Match on this rather than on `eventDescription`, which is free text that can differ between operators. |
| `data[].eventDescription` | string | The human-readable name of the event type, taken from the platform's list of event types — `Third Party Integration Activated`, `User Login`. Use it for display only. |
| `data[].entityChanged` | string | Which record the event concerns, up to 50 characters, supplied by whatever raised the event. There is no fixed vocabulary and no guaranteed format: an integration event puts the third-party id in here, while other events put a name. Null when none was supplied, which is common for events like a login that do not change a record. |
| `data[].fieldChanged` | string | Which field of that record changed, up to 50 characters, again free text chosen by whatever raised the event. Null for events that are not field-level changes. |
| `data[].changedFrom` | string | The value before the change, up to 500 characters, rendered as text whatever its underlying type. Null when the event records no previous value — a creation, or an event that is not a field change. Longer values are truncated at 500 characters, so do not treat this as a complete before-image. |
| `data[].changedTo` | string | The value after the change, up to 500 characters, with the same caveats as `changedFrom`. Null on deletions and on events that are not field changes. |
| `data[].userId` | string | **Always null.** Although the log records the acting user against each entry, this endpoint never returns it. |
| `links` | object | Pagination links. Always present, and currently contains only `next`. |
| `links.next` | string | An absolute URL for the following page, built from the URL you called and carrying `limit`, your `sort-by` if you sent one, and the continuation token. Request it as-is rather than assembling your own. **It is present on every response, including the last one** — when there are no more entries the endpoint echoes back the token you sent, so this link is not an indicator that more data exists. Stop on an empty `data` array. |

## Sample response

```json title="200 OK"
{
  "data": [
    {
      "eventLogId": 12345,
      "eventDateTimeUtc": "2026-08-14T19:00:00Z",
      "eventId": 1010,
      "eventDescription": "Third Party Integration Activated",
      "entityChanged": "42",
      "fieldChanged": null,
      "changedFrom": null,
      "changedTo": null,
      "userId": null
    }
  ],
  "links": {
    "next": "https://{gateway}/api/system/event-logs?limit=100&continuation-token=null_12345"
  }
}
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | Two different shapes, so do not assume one parser handles both. An unrecognised `sort-by` property returns the plain string `Invalid sort-by property.` as the whole body — only `eventDateTimeUtc`, `eventId` and `eventLogId` are accepted, case-sensitively. A malformed `sort-by` or `continuation-token` instead returns a validation problem document (`application/problem+json`) with an `errors` map keyed by field name; the triggers are a `sort-by` with more than one colon or an empty property name, a direction that is neither `asc` nor `desc`, and a continuation token that is not exactly two underscore-separated parts with an integer second part. |
| `500` | Internal server error | Returned with no body. Besides an unexpected server-side failure, a known cause is calling a host other than the published API host, because the endpoint cannot then build `links.next`. If you see this consistently on a request that used to work, check you are calling through the published `{base_url}`. |
