# Use the audit record

Read and search your project's audit record, send its entries to your log tools, and check that no entry was changed or removed.

Every sign-in, grant, approval and denial in a project is written to one audit record, for people and AI agents alike. Read it in the AuthFI console, or send it to the log tools you already use.

Use this guide to find out who did what, to keep a copy of the record in your own systems, and to check that the record was not altered.

## Before you begin

You need:

- Access to **Security > Audit** in the console.
- To send entries elsewhere: access to **Integrations > Connections**, and the details and the credential your destination needs. They are listed in [Send entries to your log tools](#send-entries-to-your-log-tools).

## Read the record

In the console, open **Security > Audit**. The page lists the entries, newest first.

| Column        | Shows                                 |
| ------------- | ------------------------------------- |
| What happened | The action                            |
| Who           | The actor                             |
| On what       | The resource                          |
| Refused at    | For a denial, the gate that denied it |
| Attested      | Whether the chain covers the entry    |
| When          | When it was recorded                  |

Entries come in pages. To see older entries, choose **Load earlier events**.

### What an entry contains

Each entry records one event: who did something, what they did, and what they did it to.

```json
{
  "id": "0d6c1b9e-...",
  "actor": "agent:agt_7c21...",
  "action": "agent.authorize.denied",
  "resource": "mcp:payments.refund",
  "recorded_at": "2026-10-10T09:14:07Z",
  "metadata": {
    "decision": "deny",
    "reason": "constraint:max",
    "request_id": "req_81f...",
    "country": "IN"
  }
}
```

| Field         | Meaning                                                       |
| ------------- | ------------------------------------------------------------- |
| `id`          | The entry's identifier                                        |
| `actor`       | Who did it: `user:`, `agent:` or `app:` and an ID             |
| `action`      | What was done, as a dotted name                               |
| `resource`    | What it was done to: a kind and an ID                         |
| `recorded_at` | When, in UTC                                                  |
| `metadata`    | The details: the decision and reason, the session, where from |

`metadata` often carries these keys:

| Key                            | Meaning                                                         |
| ------------------------------ | --------------------------------------------------------------- |
| `decision`, `reason`           | Allowed or denied, and why                                      |
| `session_id`, `request_id`     | The session and the request this belongs to                     |
| `ip_prefix`, `country`, `city` | Where it came from. The address is shortened, not stored whole. |
| `device_hash`                  | A fingerprint of the device, not the device's details           |

## Search the record

On **Security > Audit**, narrow the record in two ways.

**Choose a lens.** The tiles at the top of the page each show one part of the record: **Events**, **People & admin**, **AI response** and **Datapath**. Choose a tile to see only that part.

**Filter by actor or resource.** Two fields filter the entries:

| Field   | Enter                                            | Finds                                 |
| ------- | ------------------------------------------------ | ------------------------------------- |
| Who     | The exact actor, for example `user:ada@acme.com` | Everything one person or agent did    |
| On what | The exact resource, as the record names it       | Everything that happened to one thing |

Filters match exactly. A partial name matches nothing.

To clear the filters, choose **Show the whole record**.

## Count denials

In the console, open **Security > Signals**. Choose **Last 24 hours**, **Last 7 days** or **Last 30 days**.

The page counts the denials in that time. The table groups them by the reason given and the gate that denied the request, in the columns **Cause**, **Refused at** and **Refusals**.

## How long entries are kept

The audit record is kept for 15 days on the Team plan and 30 days on the Professional plan. On the Enterprise plan you set the period yourself.

To keep entries longer, send them to a log tool as they are written. Entries you export are yours to keep for as long as you need.

## Send entries to your log tools

A log destination is a connection in your project.

<Steps>

### Choose the destination

In the console, open **Integrations > Connections** and choose **Connect something**. Choose your log tool. The log destinations are grouped under the heading **Where the audit tape goes**.

### Enter the settings

On the **Settings** step, enter a **Name** for the connection. The **Connections** list uses this name. Then enter the details for your destination.

| Destination          | You provide                                                     |
| -------------------- | --------------------------------------------------------------- |
| Any HTTPS endpoint   | A URL and a token                                               |
| Splunk               | The HEC URL, a token and an index                               |
| Grafana Loki         | The URL, a username and a token                                 |
| Amazon S3            | A bucket, region, prefix and an access key                      |
| Google Cloud Storage | A bucket, prefix and a service account key                      |
| BigQuery             | A project, dataset, table and a service account key             |
| Microsoft Sentinel   | The ingestion endpoint, rule ID, stream and an app registration |

A credential you enter is sealed when you save, and is never shown again.

### Review and connect

Choose **Review**. Nothing is saved at this point. Check the values, then choose **Connect**.

The **Done** step confirms that the connection is saved. Choose **All connections** to see it in the list.

</Steps>

Risk findings can also be sent to PagerDuty, AWS Security Hub and Google Security Command Center. Add them the same way, under **Where security findings go** and **Who gets paged**.

### What a destination receives

Entries are sent in batches, about once a minute. An HTTPS endpoint receives them as newline-delimited JSON. Each line sent is one entry:

```json
{
  "id": "0d6c1b9e-...",
  "tenant_id": "...",
  "actor": "user:ada@acme.com",
  "action": "login.succeeded",
  "resource": "app:grafana",
  "recorded_at": "2026-10-10T09:14:07Z",
  "metadata": {}
}
```

<Note title="Delivery is at least once">
  After a failure, a batch is sent again. An entry can arrive twice and is never lost. Use `id` to drop duplicates.
</Note>

## Check that it works

Sign in to an application in the project, then look in your destination. Within about a minute, a batch arrives that contains a `login.succeeded` entry with your own account as `actor`.

The same entry appears on **Security > Audit**. Enter your own account in **Who**, for example `user:ada@acme.com`, to find it.

## Check that the record was not altered

Entries are chained. Each entry carries a hash computed from the entry before it and from its own contents. An entry cannot be changed, removed or inserted without breaking every hash after it.

The console checks the chain each time you open **Security > Audit**, and shows the result in a banner at the top of the page.

| Banner title                     | Meaning                                                                        |
| -------------------------------- | ------------------------------------------------------------------------------ |
| **Record intact**                | Every entry checked links to the one before it                                 |
| **This record has been altered** | The chain breaks. The banner names the first entry that does not fit, and why. |
| **Could not verify the chain**   | The check did not complete on this load. Choose **Check again**.               |

**Record intact** also reports how many entries were checked, and how many exist in all.

The check looks for four things:

- A gap in the numbering
- A link that does not match
- A missing entry
- An entry whose contents no longer produce its hash

## Troubleshooting

| What you see                                    | Cause                                     | Fix                                   |
| ----------------------------------------------- | ----------------------------------------- | ------------------------------------- |
| The same entry arrives twice at a destination   | A batch was sent again after a failure    | Drop duplicates by `id`               |
| A filter returns nothing for a value you expect | Filters match exactly                     | Enter the whole actor or resource     |
| The banner **This record has been altered**     | An entry was changed, removed or inserted | Start from the entry the banner names |
| The banner **Could not verify the chain**       | The check did not complete on this load   | Choose **Check again**                |

## Next steps

<Cards>
  <Card href="/extend/webhooks" title="Receive webhooks">
    Have your project call your server when a person, group or session changes.
  </Card>
  <Card href="/security/keys" title="Keys and secrets">
    How the credential for a log destination is stored.
  </Card>
  <Card href="/agents/limits" title="Limit what an agent may do">
    The decisions and reasons that appear in audit entries for agents.
  </Card>
  <Card href="/concepts/access" title="Roles, groups and grants">
    How the decisions in the record are made.
  </Card>
</Cards>
