---
title: SIEM export and audit tools
description: SIEM export settings, credential reports, access scans and
  findings, and audit retention policies. Configuration only for now.
url: https://hexelstudio.com/docs/identity/siem-export
---

<Warning>
Nothing is sent to a SIEM yet. SIEM configurations are stored and returned, but no audit event is delivered to any destination, and `last_sync_at` and `last_error` stay empty. The other tools on this page are also storage only: a credential report stores the data you send, a scan stays `RUNNING` and finds nothing, no findings are produced, and retention policies are never applied, so no audit record is deleted or archived because of them. To read your organization's audit log, use [Audit logs](https://hexelstudio.com/docs/identity/audit-logs). This API may change.
</Warning>

## SIEM configurations

A SIEM configuration describes where your organization's audit events should be exported. You can create, list, change and delete configurations. They have no effect yet.

| Field | Type | Description |
| --- | --- | --- |
| `name` | string | Name of the configuration. Unique in your organization. |
| `provider` | string | The SIEM product, in your own terms, for example `splunk`. Not checked. |
| `config` | object | Settings for the destination. Stored and returned exactly as sent. |
| `is_enabled` | boolean | Default `true`. |

The response adds `id` (`sc_...`), `organization_id`, `last_sync_at`, `last_error`, `created_at` and `updated_at`.

<Warning>
`config` is returned in full to everyone with `iam:audit:siem`. Do not put tokens, passwords or other secrets in it.
</Warning>

```bash
curl -X POST https://api.hexelstudio.com/v1/audit/siem \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "security-team",
    "provider": "splunk",
    "config": {"endpoint": "https://splunk.example.com:8088", "index": "hexel"}
  }'
```

```json
{
  "id": "sc_...",
  "organization_id": "<org-id>",
  "name": "security-team",
  "provider": "splunk",
  "is_enabled": true,
  "config": {"endpoint": "https://splunk.example.com:8088", "index": "hexel"},
  "created_at": "2026-10-20T09:00:00Z",
  "updated_at": "2026-10-20T09:00:00Z"
}
```

| Call | Permission | Response |
| --- | --- | --- |
| `POST /v1/audit/siem` | `iam:audit:siem` | `201` with the configuration |
| `GET /v1/audit/siem` | `iam:audit:siem` | Array of your organization's configurations |
| `PATCH /v1/audit/siem/{siem_id}` | `iam:audit:siem` | `{"updated": true}`, or `404` |
| `DELETE /v1/audit/siem/{siem_id}` | `iam:audit:siem` | `204`, or `404` |

`PATCH` changes `name`, `provider`, `config` and `is_enabled`. It never changes `id`, `organization_id`, the timestamps, `last_sync_at` or `last_error`.

## Credential reports

A credential report is a JSON document stored with your organization. Hexel Studio does not generate the content: `report_data` is stored exactly as you send it.

```bash
curl -X POST https://api.hexelstudio.com/v1/audit/reports \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"report_data": {"period": "2026-Q3", "keys_rotated": 12}}'
```

The response is `201 Created` with `id` (`cr_...`), `organization_id`, `report_data`, `generated_by_user_id` (you) and `generated_at`.

| Call | Permission | Response |
| --- | --- | --- |
| `POST /v1/audit/reports` | `iam:audit:write` | `201` with the report |
| `GET /v1/audit/reports` | `iam:audit:read` | Array of your organization's reports, newest first |

## Access scans and findings

A scan records a request to analyze your organization's access. No analysis runs: a scan is created with `status` `RUNNING` and `findings_count` `0` and stays that way, and no findings are produced.

| Field | Type | Description |
| --- | --- | --- |
| `scan_type` | string | `UNUSED_ACCESS`, `OVERPRIVILEGED`, `EXTERNAL_ACCESS` or `POLICY_VALIDATION`. |

```bash
curl -X POST https://api.hexelstudio.com/v1/audit/scans \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scan_type": "UNUSED_ACCESS"}'
```

```json
{
  "id": "aas_...",
  "organization_id": "<org-id>",
  "scan_type": "UNUSED_ACCESS",
  "status": "RUNNING",
  "triggered_by_user_id": "<user-id>",
  "started_at": "2026-10-20T09:00:00Z",
  "findings_count": 0
}
```

| Call | Permission | Response |
| --- | --- | --- |
| `POST /v1/audit/scans` | `iam:audit:write` | `201` with the scan |
| `GET /v1/audit/scans` | `iam:audit:read` | Array of your organization's scans, newest first |
| `GET /v1/audit/findings` | `iam:audit:read` | Array of your organization's findings, filtered by `?status=`. Empty, because nothing produces findings. |
| `PATCH /v1/audit/findings/{finding_id}/resolve` | `iam:audit:write` | `204`, or `404` |

## Retention policies

A retention policy says how long a kind of audit record should be kept. Retention policies are stored only: nothing deletes or archives records based on them.

<Note>
Listing retention policies needs `iam:audit:read`; creating, changing and deleting them needs `iam:audit:write`. Without it the routes return `403` with `{"error": "forbidden"}`.
</Note>

| Field | Type | Description |
| --- | --- | --- |
| `resource_type` | string | **Required.** The kind of record, for example `audit_logs`. One policy per kind in your organization. |
| `retention_days` | integer | **Required.** At least `30`. |
| `archive_enabled` | boolean | Default `false`. |
| `archive_target` | string | Optional. |

```bash
curl -X POST https://api.hexelstudio.com/v1/audit/retention \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"resource_type": "audit_logs", "retention_days": 365}'
```

```json
{
  "id": "drp_...",
  "organization_id": "<org-id>",
  "resource_type": "audit_logs",
  "retention_days": 365,
  "archive_enabled": false,
  "is_enabled": true,
  "created_by_user_id": "<user-id>",
  "created_at": "2026-10-20T09:00:00Z",
  "updated_at": "2026-10-20T09:00:00Z"
}
```

| Call | Response |
| --- | --- |
| `POST /v1/audit/retention` | `201` with the policy. `400` if a required field is missing or `retention_days` is below 30. `409` if the kind already has a policy. |
| `GET /v1/audit/retention` | `{"retention_policies": [...]}` with your organization's policies |
| `PATCH /v1/audit/retention/{policy_id}` | `{"message": "updated"}`, or `404`. Takes `retention_days`, `archive_enabled`, `archive_target` and `is_enabled`. A `retention_days` below 30 is ignored. |
| `DELETE /v1/audit/retention/{policy_id}` | `{"message": "deleted"}` with status `200`, or `404` |
