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
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | 1-indexed page number |
| limit | number | 20 | Page size (max 100) |
Response (status 200)
{
"data": [
{
"id": "a34946a8-c5cb-47ff-9d64-3a179c81baeb",
"event": "version_committed",
"actor": { "id": "5fc99de4-ca8d-4a7e-9946-9e77dcb0fa62", "email": "b5v3@example.com" },
"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": "b5v3@example.com" },
"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": "b5v3@example.com" },
"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": "demo@acruxcore.com" },
"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": "demo@acruxcore.com" },
"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": "demo@acruxcore.com" },
"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 and trace settings 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
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | 1-indexed page number |
| limit | number | 20 | Page size (max 100) |
| event | string | — | Comma-separated event names to include; unknown names return 400 |
| actorId | uuid | — | 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": "demo@acruxcore.com" },
"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": "demo@acruxcore.com" },
"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.
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": "demo@acruxcore.com" },
"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 34 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": "demo@acruxcore.com",
"eventCount": 1005
}
]
}