Skip to main content

Audit API

GET /api/v1/prompts/:id/audit​

Returns a paginated, newest-first list of audit events for a specific prompt. Each event includes the actor (user who triggered it) and a metadata payload describing what changed.

Requires auth (session or Bearer key). The prompt must belong to the authenticated user's team.

curl -X GET "$ACRUXCORE_BASE_URL/prompts/77afb6c5-8146-4fde-96e3-493ef3751374/audit" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"

Query parameters​

ParamTypeDefaultDescription
pagenumber11-indexed page number
limitnumber20Page size (max 100)

Response (status 200)​

{
"data": [
{
"id": "a34946a8-c5cb-47ff-9d64-3a179c81baeb",
"event": "version_committed",
"actor": { "id": "5fc99de4-ca8d-4a7e-9946-9e77dcb0fa62", "email": "[email protected]" },
"metadata": { "versionNumber": 2 },
"createdAt": "2026-06-26T23:06:48.883Z"
},
{
"id": "2615b9bd-d1f0-4336-9d91-e39ebbc59e4e",
"event": "version_committed",
"actor": { "id": "5fc99de4-ca8d-4a7e-9946-9e77dcb0fa62", "email": "[email protected]" },
"metadata": { "versionNumber": 1 },
"createdAt": "2026-06-26T23:06:48.872Z"
},
{
"id": "8c86bf20-078d-48cf-a068-3a38a4f8364e",
"event": "prompt_created",
"actor": { "id": "5fc99de4-ca8d-4a7e-9946-9e77dcb0fa62", "email": "[email protected]" },
"metadata": { "name": "greet" },
"createdAt": "2026-06-26T23:06:48.844Z"
}
],
"total": 3,
"page": 1,
"limit": 20
}

Paginated example​

curl -X GET "$ACRUXCORE_BASE_URL/prompts/77afb6c5-8146-4fde-96e3-493ef3751374/audit?page=1&limit=2" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"
{
"data": [ ... 2 items ... ],
"total": 3,
"page": 1,
"limit": 2
}

Error responses​

Response (status 404) — prompt not found or belongs to another team:

{ "error": { "code": "NOT_FOUND", "message": "Prompt not found." } }

GET /api/v1/tools/:id/audit​

Returns a paginated, newest-first list of audit events for a specific tool: version commits from POST /api/v1/tools/:id/sync (tool_version_committed, tool_version_superseded), and any prompt tool binding set or removed against this tool (prompt_tool_route_set, prompt_tool_route_removed) — a binding event surfaces here even though its promptId points at the prompt that owns the binding, since the same JSON metadata.toolId filter that scopes prompt-side audit by prompt scopes this endpoint by tool.

Requires auth (session or Bearer key). The tool must belong to the authenticated user's team. Same page/limit query parameters as the prompt audit endpoint above.

curl -X GET "$ACRUXCORE_BASE_URL/tools/0d84d6c3-dc86-4db2-aee3-776bdd35ea34/audit?limit=5" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"

Response (status 200)​

{
"data": [
{
"id": "aba700f7-581b-48b6-b806-9519c8fddd13",
"event": "prompt_tool_route_removed",
"actor": { "id": "18c76b52-ee0d-4001-be8e-c29976488fbb", "email": "[email protected]" },
"metadata": {
"toolId": "0d84d6c3-dc86-4db2-aee3-776bdd35ea34",
"promptAlias": "production",
"fromToolAlias": "staging"
},
"createdAt": "2026-08-19T20:41:15.890Z",
"promptId": "bb19245b-408d-4b40-8782-9b2042c97929"
},
{
"id": "f178dcdd-75cc-4bfd-90e0-57895b833de4",
"event": "prompt_tool_route_set",
"actor": { "id": "18c76b52-ee0d-4001-be8e-c29976488fbb", "email": "[email protected]" },
"metadata": {
"toolId": "0d84d6c3-dc86-4db2-aee3-776bdd35ea34",
"toolName": "get_weather",
"promptAlias": "production",
"toToolAlias": "staging",
"fromToolAlias": null
},
"createdAt": "2026-08-19T20:40:56.933Z",
"promptId": "bb19245b-408d-4b40-8782-9b2042c97929"
},
{
"id": "dc1f967e-a439-4f19-aa79-9d8e7fcd71bb",
"event": "tool_version_committed",
"actor": { "id": "18c76b52-ee0d-4001-be8e-c29976488fbb", "email": "[email protected]" },
"metadata": { "via": "sync", "source": "code", "toolId": "0d84d6c3-dc86-4db2-aee3-776bdd35ea34", "versionNumber": 3 },
"createdAt": "2026-08-12T17:49:16.854Z",
"promptId": null
}
],
"total": 5,
"page": 1,
"limit": 5
}

tool_version_committed metadata's via: "sync" / source: "code" marks a version created by a POST /api/v1/tools/:id/sync call detecting changed code — this is the signal an admin uses to notice a tool changed underneath an alias that auto-moved. tool_version_superseded (not shown above) fires alongside it when that sync also moved an alias forward, with supersededVersionNumber/newVersionNumber/ supersededSource metadata.

Error responses​

Response (status 404) — tool not found or belongs to another team:

{ "error": { "code": "NOT_FOUND", "message": "Tool not found." } }

GET /api/v1/teams/:id/audit​

Returns a paginated, newest-first list of every audit event in the team — not just the ones tied to a prompt or a tool. This is the endpoint behind the dashboard's Audit trail screen, and the one to call for a compliance question: API keys generated and revoked, members added and removed, roles changed, invites sent and accepted, provider credentials, virtual keys, budgets, gateway models, secrets, trace settings, datasets and evaluation rules all appear here and nowhere else.

Session authentication only, owner or admin. A Bearer API key is rejected with a 401: the trail records what people did, and a key belongs to a service rather than a person. Editors and viewers are authenticated but get a 403.

curl -X GET "$ACRUXCORE_BASE_URL/teams/8d3ceb9b-39d0-463b-8b91-aa2ef20ac9ba/audit?limit=2" \
-H "Cookie: $ACRUXCORE_SESSION_COOKIE"

Query parameters​

ParamTypeDefaultDescription
pagenumber11-indexed page number
limitnumber20Page size (max 100)
eventstring—Comma-separated event names to include; unknown names return 400
actorIduuid—Restrict to one person's events

event and actorId are AND-ed, and both narrow total as well as the rows — so total is always the size of the filtered set, safe to paginate against.

Response (status 200)​

{
"data": [
{
"id": "3f84fa30-a1de-4ceb-a01c-aa33f0b1445b",
"event": "api_key_revoked",
"actor": { "id": "18c76b52-ee0d-4001-be8e-c29976488fbb", "email": "[email protected]" },
"metadata": { "apiKeyId": "1df65dba-c2d2-490d-a41f-8331a2f5990a" },
"createdAt": "2026-09-08T11:45:31.890Z",
"promptId": null,
"target": null
},
{
"id": "7cf02538-efaa-4940-a954-3eb2477dc3b2",
"event": "api_key_generated",
"actor": { "id": "18c76b52-ee0d-4001-be8e-c29976488fbb", "email": "[email protected]" },
"metadata": { "name": "video-capture", "apiKeyId": "1df65dba-c2d2-490d-a41f-8331a2f5990a" },
"createdAt": "2026-09-08T11:45:29.696Z",
"promptId": null,
"target": null
}
],
"total": 1005,
"page": 1,
"limit": 2
}

promptId is set on a prompt-scoped event and null otherwise. target is the person the event was performed on — filled in for member_role_updated and member_removed, where the stored metadata holds only a user id — and null everywhere else. It is resolved from the user record rather than from current membership, so a member who has since been removed still shows their address.

Evaluation events​

Deleting a dataset destroys the material every past run was measured against, and creating or enabling an evaluation rule commits the team to one judge call per sampled span from then on. Both leave a row here.

A dataset created, a rule created, switched off and deleted, then the dataset deleted:

curl -X GET "$ACRUXCORE_BASE_URL/teams/1f4afe71-3b03-423f-8cca-bf1f2c51f062/audit?event=dataset_created,dataset_deleted,eval_rule_created,eval_rule_updated,eval_rule_deleted&limit=5" \
-H "Cookie: $ACRUXCORE_SESSION_COOKIE"

Response (status 200)​

{
"data": [
{
"id": "4911b0a3-287e-4727-8822-dd71227c6ef3",
"event": "dataset_deleted",
"actor": { "id": "517a0e90-472f-49cd-898b-610804558c24", "email": "[email protected]" },
"metadata": { "name": "regression set", "datasetId": "66bfa175-725d-4f72-82e5-2a0d06c54705" },
"createdAt": "2026-09-16T06:46:58.203Z",
"promptId": null,
"target": null
},
{
"id": "9fec9b1e-534a-4ddf-8e86-3bddffb0dd79",
"event": "eval_rule_updated",
"actor": { "id": "517a0e90-472f-49cd-898b-610804558c24", "email": "[email protected]" },
"metadata": {
"name": "quality gate",
"ruleId": "6c29328e-97ba-49c3-9141-20015d102c73",
"changed": ["enabled"],
"enabled": false
},
"createdAt": "2026-09-16T06:46:58.172Z",
"promptId": null,
"target": null
}
],
"total": 5,
"page": 1,
"limit": 5
}

A rule's metadata names the rule and lists the fields the request changed. It carries enabled in full because that is the field that starts and stops the spend; it never carries criteria or a custom judge prompt, which are free text and can run to thousands of characters. A dataset's metadata records the name as it was at the time, because a deleted dataset is filtered out of every list afterwards and this row is the only place left that says what the id referred to.

Starting an experiment run or an optimize run is not recorded. Both spend money, but a run is bounded — it ends, and its cost already shows in Gateway usage and in the run's own record — so recording every start would push the rest of the trail off the first page.

Filtering by event​

curl -X GET "$ACRUXCORE_BASE_URL/teams/8d3ceb9b-39d0-463b-8b91-aa2ef20ac9ba/audit?event=api_key_generated,api_key_revoked&limit=1" \
-H "Cookie: $ACRUXCORE_SESSION_COOKIE"
{
"data": [
{
"id": "3f84fa30-a1de-4ceb-a01c-aa33f0b1445b",
"event": "api_key_revoked",
"actor": { "id": "18c76b52-ee0d-4001-be8e-c29976488fbb", "email": "[email protected]" },
"metadata": { "apiKeyId": "1df65dba-c2d2-490d-a41f-8331a2f5990a" },
"createdAt": "2026-09-08T11:45:31.890Z",
"promptId": null,
"target": null
}
],
"total": 126,
"page": 1,
"limit": 1
}

Error responses​

Response (status 400) — an event name that is not one of the 39 recorded events. The message lists every accepted value:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid enum value. Expected 'prompt_created' | 'prompt_renamed' | ... | 'prompt_tool_route_removed', received 'nope'"
}
}

Response (status 403) — the caller is an editor or a viewer:

{ "error": { "code": "FORBIDDEN", "message": "Insufficient role for this action." } }

Response (status 401) — no session cookie, or a Bearer API key instead of one:

{ "error": { "code": "UNAUTHORIZED", "message": "Authentication required." } }

GET /api/v1/teams/:id/audit/actors​

Returns everyone who has written at least one audit event for the team, with their event count — the option list for a "who did this" filter. Unpaginated: one row per person, ascending by email. Same owner/admin, session-only gate as the trail itself.

Built from the trail rather than from current membership, so a member who has since been removed is still listed. That is deliberate: their events remain in the record, and they are exactly who an access review asks about.

curl -X GET "$ACRUXCORE_BASE_URL/teams/8d3ceb9b-39d0-463b-8b91-aa2ef20ac9ba/audit/actors" \
-H "Cookie: $ACRUXCORE_SESSION_COOKIE"

Response (status 200)​

{
"data": [
{
"id": "18c76b52-ee0d-4001-be8e-c29976488fbb",
"email": "[email protected]",
"eventCount": 1005
}
]
}