Audit log
The audit log answers “who did what, to what, when, from where, and with what outcome.” It is the one record in hlix that carries a guarantee: every entry is written, no entry can be edited or deleted, and each Workspace’s entries are hash-chained, so a tampered or missing entry is detectable.
Prerequisites
Section titled “Prerequisites”- The
owneroradminrole in the Workspace. Every audit route refusesmemberandclientalike — see Roles & permissions. - An API key and your Workspace ID from Developer tools → API keys, or the Audit Log screen in the dashboard.
Three records, one promise
Section titled “Three records, one promise”hlix keeps three separate records, and the difference between them is the point.
| Record | Answers | Delivery |
|---|---|---|
| Audit log | Who did what, to what, when, from where, with what outcome | Guaranteed and immutable |
| Events | What is happening right now | Best-effort |
| Agent runs and logs | Why a run cost or failed what it did | Best-effort |
What gets recorded
Section titled “What gets recorded”Every action has a stable resource.verb name — project.created, secret.config.updated, task.executed — and each name maps to one of seven categories you can filter on.
| Category | What lands here |
|---|---|
create |
New projects, tasks, cycles, members, collaborators, customer accounts, invoices, secrets, environments, agents, packs, skills, memories, deployments, integrations, and a deliverable approval being requested |
update |
Changes to any of the above, plus settings, billing, provider keys, model and harness preferences, DNA, and every deliverable transition — publishing, unpublishing, approval and changes-requested are all recorded as updates |
delete |
Removal of any of the above |
execute |
Work being run: task and cycle execution, dispatch, deploys, approvals resolved, policy decisions, sandbox actions, integration runs |
auth |
Sign-in, sign-out, API key created, API key revoked, passkey added |
read |
The deliberately narrow set of reads worth recording — billing and secret access |
export |
Data leaving the Workspace: invoice exports and data exports |
Reads are not logged in general. Only billing and secret access are recorded, because there the read itself is the sensitive act.
Each entry carries the actor (a person, an agent, or the system, with the acting user attributed where an agent acted on someone’s behalf), the resource and its parent, the change itself, the source and calling IP and user agent, the outcome and severity, an optional reason, and the correlation ids below.
Secrets never reach an entry intact
Section titled “Secrets never reach an entry intact”Before an entry is written, its change and metadata are redacted:
- Any field whose name looks like a credential — anything matching secret, token, password, API key, authorization, cookie or credential — becomes
[redacted]. - Any value that looks like a credential is replaced by shape, not by name: provider key prefixes, JSON web tokens, bearer tokens, connection strings carrying a password, private-key blocks, and labelled key/value pairs.
- Long strings are truncated.
Redaction is a safety net, not a reason to put credentials in a payload: an encoded, split or unfamiliar value may not match.
Reading it
Section titled “Reading it”curl -sS "$HLIX_BASE_URL/v1/api/audit-log?category=delete&limit=25" \ -H "x-api-key: $HLIX_API_KEY" \ -H "X-Organization-Id: $HLIX_WORKSPACE_ID"Expected result: {"items": [...], "nextCursor": "…"} — entries newest-first, and a cursor that is null on the last page.
Filter on actorType, actorId, action, category, resourceType, resourceId, outcome, severity, source, requestId, traceId, and a from/to time range. limit defaults to 50 and is capped at 200.
Paging is keyset, not offset: pass the previous page’s nextCursor back verbatim. The cursor is opaque — never construct one.
A project-scoped listing is available at /v1/api/audit-log/projects/{projectId}, and it is still owner/admin only. Narrowing what you can read does not widen who may read it.
One entry, with its correlated trace
Section titled “One entry, with its correlated trace”curl -sS "$HLIX_BASE_URL/v1/api/audit-log/$ENTRY_ID" \ -H "x-api-key: $HLIX_API_KEY" \ -H "X-Organization-Id: $HLIX_WORKSPACE_ID"Expected result: the entry plus a correlated object holding the events, agent runs and logs that share its trace.
Three correlation ids make that join possible. requestId identifies one API call. traceId identifies one unit of work across all three records. spanId identifies a step within it. An entry with no trace comes back with an empty correlated object rather than an error.
Verifying integrity
Section titled “Verifying integrity”Two independent mechanisms sit under the guarantee.
Append-only storage. Entries cannot be updated or deleted by hlix or anyone else. Expiring old entries at the end of the retention period never rewrites current ones.
A per-Workspace hash chain. Each entry is hashed together with the hash of the previous entry in that Workspace. Removing an entry breaks the chain; altering one changes its hash. Check it:
curl -sS "$HLIX_BASE_URL/v1/api/audit-log/verify" \ -H "x-api-key: $HLIX_API_KEY" \ -H "X-Organization-Id: $HLIX_WORKSPACE_ID"Expected result on an intact chain:
{"ok": true, "checked": 1842, "total": 1842, "firstBadId": null}Compliance-critical changes commit with their record
Section titled “Compliance-critical changes commit with their record”For the subset of mutations where the record matters as much as the change — provider key changes, secret configuration, environment configuration, and archiving, unarchiving or deleting a project — the change and its audit entry are saved together, atomically. The record cannot exist without the change, or the change without the record.
For every other action the entry is written as part of handling the request, but a failure to write it does not fail the request.
Retention
Section titled “Retention”| Record | Kept for |
|---|---|
| Audit log | 24 months |
| Events (the activity feed) | 6 months |
| Execution logs | 30 days |
| Agent runs | Not pruned — kept for the life of the Workspace |
Expired entries are removed daily. Retention periods are the same for every Workspace.
If the audit log does not answer what you expect
Section titled “If the audit log does not answer what you expect”{"error": "admin only"}with403. Every audit route needsowneroradmin. Amembergets this, and so does a client collaborator. There is no narrower view to fall back to.{"error": "Audit entry not found"}with404. The id does not exist in this Workspace. Confirm theX-Organization-Idheader matches the Workspace the entry came from — an entry from another Workspace is not-found, not forbidden.{"ok": false, ...}with afirstBadId. An entry does not hash to its recorded value. Capture the response, stop writing to that Workspace, and escalate — this is the condition the chain exists to surface.okistruebutcheckedis less thantotal. The walk ran out of chain before it ran out of entries. Treat it exactly as the case above.- A filtered query returned everything. An
actionthe server does not know is ignored rather than rejected. Check the spelling against the API reference. - A change you made is not in the log. Check the category first — general reads are not recorded by design, and only billing and secret access are. If it was a write, look for it under the resource’s own action name rather than the screen you made it from.
- An entry has no correlated events or logs. Those two records are best-effort and are pruned sooner (6 months and 30 days). An older audit entry outliving its correlated records is expected.
- An agent appears as the actor and you want the person behind it. Read the attributed user on the entry; an agent acting on someone’s behalf records both.
Where to go next
Section titled “Where to go next”| If you want to… | Read |
|---|---|
| See the exact request and response shapes | Audit log API |
| Understand who may read this | Roles & permissions |
| See the gates that produce these records | Review & QA gates |
| Read it in the dashboard | The dashboard |