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

# Audit Log

> An append-only record of every execution, denial, and governance change in your organization, with export and retention

Every governance-relevant action in an organization writes one row to an append-only audit log: tool executions, denied calls, catalog changes, membership and team changes, policy changes, API key lifecycle, credential access, data-plane events and exports of the log itself. Rows are never updated. They are deleted only by the retention job you configure.

## What a row contains

| Field | Meaning |
| - | - |
| `action` | What happened, from the vocabulary below |
| `status` | `success`, `denied`, `error`, or `info` |
| `actor_type` and `actor_profile_id` | Who acted: `user` (a dashboard session), `agent` (an API key), `dataplane` (an agent inside your network) or `system` (a workflow step) |
| `api_key_id` | The key the call came through, when it came through a key. The viewer stamps the key's `agent_name` and `agent_type` on the row so it reads as "research-bot", not a uuid |
| `resource_type`, `resource_id` | What the action was about: a tool, service, member, team, grant, api\_key, organization, credential, dispatch or data-plane agent |
| `service_id`, `tool_id`, `tool_name` | What was called or changed |
| `args_hash` | A SHA-256 digest of the call's parameters. The parameters themselves are never stored, so the log can prove which arguments were used without holding the data. |
| `denial_reason` | Why a call was denied, when it was (see [Denial reasons](/organizations/policies#denial-reasons)) |
| `metadata` | Action-specific detail: the keys a settings change touched, the surface a call came from, a data-plane call's duration. Names of things, never parameter values or results |
| `ip`, `request_id` | Where the request came from, and the id to correlate with your own logs |
| `dataplane_agent_id`, `seq` | For events from a data-plane agent: which agent, and its monotonic sequence number |
| `created_at` | When it happened |

## Actions

Owners and admins read every row in the viewer and the export. The third column says who the row is usually for.

| Action | When | Who reads it |
| - | - | - |
| `tool.execute` | Every tool call, on every surface: an API or MCP call, a batch element, a workflow step, a data-plane result. `status` is `success` or `error` | Anyone reviewing what agents did; a SIEM |
| `tool.denied` | A call refused by a key restriction, the organization policy, a spend cap, an organization cap, or a missing credential | Whoever rolls out policies and caps |
| `tool.confirmation_required` | A key with `require_confirmation` was asked to make a destructive call and a confirmation token was issued | Reviewers of agent consent |
| `tool.create`, `tool.update`, `tool.delete`, `tool.deprecate` | A tool added to, changed on, removed from or deprecated on a service the actor manages | Catalog change history |
| `service.create`, `service.update`, `service.delete`, `service.refresh_tools` | A service registered (including through the organization endpoint, with `visibility`, `source` and `tool_count`), edited, deleted, or its tools re-synced | Catalog change history |
| `org.create`, `org.update`, `org.delete` | The organization created, renamed or re-planned, or soft-deleted | Change history |
| `org.settings_update` | A settings change; `metadata.keys` names what changed and `metadata.settings` holds the result | Data-handling and compliance review |
| `member.add`, `member.role_change`, `member.remove` | Membership changes; a removal carries `api_keys_revoked` and `credentials_deleted` | Access reviews |
| `team.create`, `team.delete`, `team.member_add`, `team.member_remove` | Team changes; a deletion carries `grants_removed` | Access reviews |
| `grant.create`, `grant.delete` | A policy statement added or removed, with its subject and effect | Policy history |
| `key.create`, `key.update`, `key.rotate`, `key.revoke` | API key lifecycle, including keys minted by the device flow and revocations by an organization admin | Key hygiene |
| `credential.store`, `credential.read`, `credential.delete` | A credential stored, resolved for a call (`metadata.org_shared` says whether a shared one was used) or removed | Secrets access review |
| `dataplane.enroll_token`, `dataplane.enroll`, `dataplane.revoke` | An enrollment token minted, an agent enrolled, an agent revoked | Data-plane operators |
| `audit.export` | Someone downloaded the log; `metadata` carries the format, the filters, the row count and whether it was truncated | Compliance |

A data-plane `tool.execute` carries `metadata.dataplane: true`, `duration_ms`, `spend_cents`, `payloads_stored` and, for a result that arrived after the dispatch had expired, `late_result: true` with `denial_reason: late_result`. A workflow execution carries `metadata.surface: workflow`, `workflow_id`, `execution_id` and `step`.

## Which surfaces are audited

* **API calls**, single and batch, with the caller's key, ip and request id and a hash of the parameters. The organization is the acting organization of the request (the `danube-org-id` header or the key's organization).
* **MCP** calls, which reach the same API through the MCP server. The row shows the key, and a `danube-org-id` header passed by the MCP client attributes the call to that organization.
* **Hosted workflows**, whose executions do not pass through the API. Their rows are filed under the tool's own organization when it has one, and otherwise under the caller's personal organization.
* **Data-plane results**, reported by the agent inside your network, plus the events the agent streams itself with a sequence number.
* **Governance changes** made in the dashboard or through the API: membership, teams, grants, settings, keys, credentials, catalog writes.

Executions that the organization policy refuses in a workflow are logged in the server log but not written to the audit table, because that path has no request context to attribute.

## Read the log

<Steps>
  <Step title="Open the viewer">
    Go to [Dashboard > Organization > Audit Log](https://danubeai.com/dashboard/organization/audit). Admins and owners only.
  </Step>

  <Step title="Filter">
    Filter by action, status, actor, tool, and a time range. The actor filter offers the organization's members by name, or a profile id typed by hand for an actor who is no longer a member. The list is newest first and paginated.
  </Step>
</Steps>

Or query it directly:

```bash theme={null}
curl "https://api.danubeai.com/v1/organizations/ORG_ID/audit?status=denied&start=2026-09-01T00:00:00Z&page_size=100" \
  -H "danube-api-key: YOUR_API_KEY"
```

| Query parameter | Notes |
| - | - |
| `action` | Exact match, for example `tool.execute` |
| `status` | `success`, `denied`, `error`, or `info` |
| `actor` | Actor profile id |
| `tool_id` | One tool |
| `start`, `end` | ISO-8601 bounds on `created_at`, inclusive |
| `page`, `page_size` | `page_size` is at most 100 |

The response is `{"events": [...], "total": N, "page": 1, "page_size": 50}`.

## Export

`GET /v1/organizations/{org_id}/audit/export` downloads the log as a file, newest first, with the same filters as the viewer. The dashboard's **Export CSV** button downloads the currently filtered view.

```bash theme={null}
curl -o audit.ndjson \
  "https://api.danubeai.com/v1/organizations/ORG_ID/audit/export?format=ndjson&action=tool.execute&start=2026-09-01T00:00:00Z" \
  -H "danube-api-key: YOUR_API_KEY"
```

* `format` is `csv` (default, `text/csv`) or `ndjson` (`application/x-ndjson`, one JSON object per line). The file name is `audit-<org slug>-<date>.<format>`.
* The columns are fixed, in this order, and a new column is only ever added at the end: `created_at, action, status, actor_type, actor_profile_id, api_key_id, ip, request_id, resource_type, resource_id, service_id, tool_id, tool_name, args_hash, denial_reason, metadata`. In CSV the metadata column is JSON; in NDJSON each line carries the same keys in the same order.
* `end` defaults to the moment the export starts, so rows written during a long export never shift its pages.
* **Row cap.** An export stops at 200,000 rows and ends with a marker line whose `action` is `audit.export_truncated` (status `info`, `metadata` with `truncated: true`, `max_rows` and a message). Narrow the date range and export again for the rest. If a read fails part-way the file ends with an `audit.export_failed` marker (status `error`) and the export should be retried. A marker has the shape of a row, so a parser reading by column still reads it; the action names what it is.
* Every export is itself recorded as `audit.export` with the format, the filters, the number of rows and whether it was truncated or failed.

## Retention

By default audit events are kept forever. An owner or admin sets `audit_retention_days` under **Organization > Settings > Retention** or with `PATCH /v1/organizations/{org_id}/settings` (`30` to `3650`; `null` returns to keeping everything). A nightly job at 03:41 UTC deletes the organization's events older than the retention, in batches of 5,000 rows and at most 100,000 rows a night across all organizations, so a backlog the day a short retention is first set drains over a few nights.

The floor is 30 days: the setting refuses a smaller value and the purge clamps to 30 as well, so a value written to the database by hand cannot shorten it either. The corresponding switch for execution history is `execution_retention_days`, described under [Data Plane](/organizations/data-plane#data-handling).

## Guarantees

* **Append-only.** The table refuses updates unconditionally at the database level and refuses deletes from every path except the scheduled retention purge, which opens a transaction-local door that no API connection can use.
* **Never blocks the call.** A failed audit write is logged as a warning and the audited operation still completes. A silent gap is not possible: the failure is visible in server logs.
* **No raw parameters.** Only the `args_hash` digest is stored, and `metadata` carries names of things, never tool inputs or results.
* **Data-plane events are idempotent.** Agents inside your network stream events with a per-agent sequence number, buffer them while offline, and retry. Intermittent connectivity neither loses nor duplicates rows.

## Not yet available

There is no push delivery to a SIEM yet. Schedule the export endpoint with the filters you need, or page through the query endpoint.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.