> ## Documentation Index
> Fetch the complete documentation index at: https://docs.assetinfinity.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Security events

> Connecting your SIEM to this organisation's audit trail: sign-ins, refusals, lockouts and every change to data, either fetched by your SIEM or sent to your OpenTelemetry collector.

This screen connects your security team's SIEM to this organisation's audit trail. Everything the
trail records goes to the SIEM: who signed in, who was refused, which accounts were locked, and who
changed what. There are two ways to connect, and both read the same events in the same order:

| Way | Who starts the conversation | Use it when |
| - | - | - |
| **Your SIEM fetches it** | Your SIEM asks for what is new, as often as it likes | Your SIEM can poll an HTTP endpoint, and you would rather nothing reached in from outside |
| **We send it to you** | Every few seconds, new events are posted to your OpenTelemetry collector | You run a collector, or your SIEM accepts OpenTelemetry logs directly |

You can start with one and move to the other later without a gap.

Open it from **Settings** → **Integrations** → **Security events**.

## What your SIEM receives

The audit trail records two kinds of event.

**Security events**, about getting in:

| Event | When it is recorded |
| - | - |
| `SIGNED_IN` | Someone signs in with a password, a second factor or single sign-on. Renewing a session that is already open is not a new sign-in |
| `SIGNED_IN_WITH_API_KEY` | An integration exchanges an [API key](/config/api-keys) for a session |
| `SIGN_IN_FAILED` | A wrong password |
| `ACCOUNT_LOCKED` | The failed attempt that locks the credential. It says how many attempts there were and when the lock lifts |
| `SIGN_IN_REFUSED_WHILE_LOCKED` | Any attempt on a credential that is already locked, including one with the right password |
| `MFA_FAILED` | A wrong second-factor code, when signing in or while setting the factor up |
| `SSO_SIGN_IN_REFUSED` | A sign-in through [single sign-on](/setup/identity-providers), Google or Microsoft that was refused, with the reason |

**Changes to data**, for every record anybody creates, changes, archives or deletes:
`RECORD_CREATED`, `RECORD_MODIFIED`, `RECORD_ARCHIVED` and `RECORD_DELETED`.

A person can hold accounts in several organisations with one password. A wrong password is
therefore an attempt on every organisation that password opens. It is recorded in each of them, and
each record names that organisation's own account. An attempt on an address that nobody holds is not
recorded anywhere, because no organisation owns it.

Each event carries:

| Field | Notes |
| - | - |
| When | To the microsecond |
| What happened | The event type, and a one-line sentence, such as "Sign-in refused: wrong password (attempt 2 of 5)" |
| Who | The person's name and email address, or the name of the service account if a machine did it. Blank for something the system did by itself, such as a scheduled job |
| From where | The address the request came from, and the kind of client (the web app, the field app, the API, an import) |
| What it touched | The kind of record, its ID and the names of the fields that changed |
| Why | The reason, where the action asked for one |

<Note>
  **Field names leave. Values do not.** An event says a work order's `priority` changed, but not
  from what to what. The values before and after stay in the product, where reading them is checked
  against the person asking. Your SIEM needs to know what happened, and the audit trail keeps the
  detail for whoever investigates it.
</Note>

The **What your SIEM receives** section shows the last day of events, read exactly the way your SIEM
will read them and in the same order. Check it before you connect anything, so you know what you are
getting.

## Your SIEM fetches it

Your SIEM signs in with a **collector key** and asks for everything since the last event it
received. Nothing is missed and nothing is sent twice, even if the SIEM was switched off for a week.

### Issuing a collector key

Click **Issue a collector key**. This creates three things:

* a service account called **SIEM collector**
* a role of the same name, which can follow the audit trail and do nothing else
* a key for that service account

The service account and the role appear on [Access](/setup/access) under those names, so a key made
last year can still be recognised.

<Warning>
  **The key is shown once.** Copy it into your SIEM before you close the dialog. A lost key is
  replaced, not recovered.
</Warning>

**Keys that can follow the trail** lists every key that can read the feed, with the service account
it signs in as, when it last signed in and when it stops working. Keys expire like any other. You
rotate and revoke them on [API keys](/config/api-keys).

### The requests your SIEM makes

The screen shows these with this organisation's own address filled in:

```bash theme={null}
curl -X POST "$CMMS_URL/api/rpc/exchange_api_key" \
  -H 'content-type: application/json' \
  -H 'accept: application/vnd.pgrst.object+json' \
  -d '{"p_key": "cmk_…"}'
# → { "token": "...", "expires_at": "..." }   good for fifteen minutes

curl -X POST "$CMMS_URL/api/rpc/audit_feed" \
  -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"p_after": "<cursor of the last event you stored>", "p_limit": 1000}'
# → [ { "cursor": "...", "occurred_at": "...", "event_type": "...", ... } ]
```

Every event has a `cursor`. Store the cursor of the last event on each page, and send it as
`p_after` with the next request.

| To | Send |
| - | - |
| Start from the beginning of the trail | Leave out `p_after` |
| Start from a date instead | `p_from` with a timestamp, and no `p_after` |
| Carry on | `p_after` with the last cursor you stored |

A page holds up to 1,000 events. When a page comes back empty, you are up to date. Ask again whenever
you like.

A change that is still being saved when your SIEM asks is held back until it is finished, not
skipped. So the feed can run a few seconds behind the screen, but it never has a gap.

## We send it to you

The **Send it to your OpenTelemetry collector** section sends new events every few seconds to an
[OpenTelemetry](https://opentelemetry.io) collector, as OTLP logs over HTTPS. The collector then
forwards them to your SIEM. [Connecting your SIEM](/config/siem-collectors) has a collector
configuration for Splunk, Microsoft Sentinel, Elastic and QRadar.

| Field | Notes |
| - | - |
| **Your collector's address** | The collector's OTLP/HTTP logs address. It must start with `https://`, usually ends `/v1/logs`, and must be reachable from the internet |
| **Header** | The header your collector checks for a credential. `Authorization` by default |
| **Credential** | The header's value, such as `Bearer …`. Nobody can read it back once it is saved. Once one is stored, the field shows its last few characters. Leave it empty to keep the stored credential |
| **Send events to this collector** | Nothing is sent until this is ticked. Untick it to pause |
| **Start from the beginning of the trail rather than from now** | Offered only when you first set this up. Leave it unticked to send only what happens from now on. Tick it to send the whole trail first |

Click **Save**, then **Send a test event**. The test posts a single event to your collector, marked
as a test. The result appears under the form: either **The last test was accepted**, or **The last
test was refused** with the reason your collector gave. Nothing moves on for a test, so you can send
as many as you need.

**Remove the credential** deletes the stored credential, for a collector that does not need one.

Changing the address does not restart the stream. A replacement collector picks up where the old one
stopped.

### Is it keeping up?

The section header shows **Sending**, **Failing** or **Off**. Below the form:

| Figure | Meaning |
| - | - |
| **Last delivered** | When your collector last accepted a page |
| **Up to events from** | The time of the newest event your collector has |
| **Waiting to be sent** | Events recorded since then. It shows **10,000 or more** beyond that |
| **Sent so far** | Every event delivered. If your collector turned down individual events, it also shows how many it refused |

If your collector is down or refuses a delivery, **The last attempt failed, and we will try again**
shows the reason. Nothing is lost while it is down. Events wait, the gap between attempts grows up to
an hour, and delivery carries on from the first event your collector does not have.

If your collector accepts a page but turns down particular events in it, those events are counted
and not sent again. A collector that turns an event down because of its content would turn it down
again.

## Who can use this

| To | You need |
| - | - |
| Open this screen and see the feed | Permission to export the audit trail |
| Issue a collector key | Permission to export the audit trail, and to create roles and API keys |
| See which keys can follow the trail | Permission to view API keys |
| Set up sending to a collector | Permission to configure integrations |

Connecting a SIEM hands it the whole audit trail. So this screen is only for people who may export
that trail themselves. See [roles](/setup/roles).

<Card title="Connecting your SIEM" icon="shield-check" href="/config/siem-collectors">
  A collector configuration for Splunk, Microsoft Sentinel, Elastic and QRadar.
</Card>
