An append-only audit-event module for Xano. Drop it into a workspace and you have a reusable way to capture "who did what to which resource, when, and from where" — callable from any API, function, or task.
This is a module, not a full app: no frontend, no auth, no UI. Just a table, a reusable function, and a small HTTP surface you can point other code at.
- Table
al_event— immutable audit events with indexes on the fields you'll filter by (actor, action, resource, severity, time). - Function
audit_record— call it from anywhere in your XanoScript to record an event. Handles defaults and validation. - HTTP API
audit-log— record + query from outside.
npm install -g @xano/cli
xano profile:wizard
cd backend
xano workspace:pushThis creates one table, one function, one api group, and four endpoints in your workspace.
POST /api:audit-log/events record an event (actor/action/resource/severity/metadata)
GET /api:audit-log/events list with filters (newest first, paginated)
GET /api:audit-log/events/{id} single event
GET /api:audit-log/stats total + last-hour + last-day + breakdown by severity
curl -X POST https://YOUR-INSTANCE.n7d.xano.io/api:audit-log/events \
-H 'Content-Type: application/json' \
-d '{
"action": "user.login",
"actor_id": "42",
"resource_type": "user",
"resource_id": "42",
"severity": "info",
"source": "web",
"metadata": {"browser":"firefox","mfa":true}
}'ip and user_agent are captured automatically from the request.
GET /api:audit-log/events
?actor_id=42
&action=user.login
&resource_type=ticket
&resource_id=1234
&severity=warning
&source=web
&from=<timestamp>
&to=<timestamp>
&page=1&per_page=25
All filters are optional and additive. Returns a paginated list with totals.
Inside any query/function/task, call the bundled helper:
function.run "audit_record" {
input = {
action : "ticket.assign",
actor_id : $auth.id|to_text,
actor_type : "user",
resource_type : "ticket",
resource_id : $input.ticket_id|to_text,
severity : "info",
source : "api",
metadata : { from: $old_assignee, to: $new_assignee }
}
} as $audital_event
| field | type | notes |
|---|---|---|
id |
int | PK |
created_at |
timestamp | defaults to now |
action |
text | required, e.g. ticket.create, user.login |
actor_id |
text | who performed the action |
actor_type |
text | default "user" — e.g. service, system |
resource_type |
text | e.g. ticket, user, asset |
resource_id |
text | stored as text so it works with int ids, UUIDs, or slugs |
severity |
enum | debug, info (default), warning, error, critical |
source |
text | free-form — api, web, cron, migration |
ip |
text | captured from request |
user_agent |
text | captured from request |
metadata |
json | anything else you want to attach |
Indexed on created_at desc, action, actor_id, resource_type, resource_id, severity.
- Append-only by design. There's no update or delete endpoint. If an event is wrong, record a corrective event.
- Text IDs.
actor_idandresource_idaretextso the module doesn't assume anything about your user table's PK type. Convert with|to_textwhen you call it. - Flexible severity. Defaults to
info. Reserveerror/criticalfor things your on-call wants to know about. - No auth on the module itself. It's meant to be called from trusted code inside Xano. If you're exposing it to the public internet, put an API-group-level middleware in front of it.
MIT.