Audit log
Purpose and who can read it
Super AdminHotel ManagerSecurityManagers and Security see the trail; nobody edits it.
The audit log answers “who changed this and when?”. Every write to the system leaves a row: bookings, payments, guest edits, room status, staff records, settings, role changes, sign-ins, successful, failed and denied. Rows are written by the server as part of the action itself, so they cannot be skipped by the browser.
Reading it is limited to the admin tier: Super Admin, Hotel Manager, or any role whose matrix has been raised to Settings = Full, plus Security, whose job is access logs. Anyone else gets a 403 and does not see the sidebar entry. Nothing on the page writes; the only action is Export CSV.
Screen anatomy
Four KPI tiles, a filter bar with date range and extra filters, the event table and a detail drawer.
- 1Page head“Every staff action across the hotel, with who / what / when / from where.”
- 2Filter bar“Search actor, action, code, IP…” plus Person (name · role), Role, Module and Action dropdowns.
- 3Date range and More filtersFrom and To date pickers (hotel-local days). More filters reveals HTTP method (POST / PATCH / PUT / DELETE) and Outcome (Succeeded / Denied / Error).
- 4Event rowsWhen · Person (avatar, name, role) · Action · Record (module badge and record id) · Changed (a one-line summary of the diff) · Outcome (OK, Denied or Error badge, with the status code when it failed) · From (IP). On phones the rows become cards.
- 6Pager and column titlesFifty rows a page (25, 50 or 100), numbered pages (“Showing 1–50 of N”), and the page, sort and filters live in the address bar. Tap a column title to sort (When starts newest first); the Person, Action, Record and Outcome titles carry filter popovers with each value and its count. A Cards view is one click away.
- 7KPI tilesEvents today (with the hotel date), Last 7 days, People active today, Denied this week. Today and Last 7 days are date presets that set the range; Denied this week sets Outcome = Denied for the week.
- 8Export CSVDownloads audit-log-YYYY-MM-DD.csv (named for today's hotel date) with the current filters applied (up to 5,000 rows, newest first).
Click a row for the drawer. At the top is the actor card: name, role, email, the browser and device the request came from, the IP address and the outcome badge. Below it: Action (raw name such as booking.checkedin), Module, Record (a link straight to the record where one exists), Outcome with the HTTP status, When, IP address, Request (method and path) and Browser (the full user agent). What changed is a table with one row per field: the old value in red, the new value in green. Raw record shows the stored document, and “No extra detail was stored for this event.” appears when there is none. Related events on the same record are listed at the bottom; click one to jump to it.
What is recorded
Named domain events with diffs, plus a generic row for every other write, sign-ins and denials.
| Field | Type | Notes |
|---|---|---|
| Actor | The user id plus a snapshot of the name and role at the time. If the account is later renamed or deleted the row still reads correctly; rows without a snapshot fall back to the current account, or “Unknown”. | |
| Action | A dotted name: booking.checkedin, payment.refunded, settings.updated, auth.login.failed, access.denied, night_audit.closed… Screen labels translate the common ones (“Checked in”, “Refunded payment”, “Access denied”). | |
| Module and Record | The entity type (booking, invoice, user, hotel, sequence…) and the record's code or id, BK-1042, INV-…, an email for user events, a slug for settings. | |
| What changed | Before → after per field for edits (bookings, rooms, guests, staff, accounts, settings, roles, housekeeping, maintenance, payments). Creates and older rows show the record that was sent; a night audit stores its snapshot. Secrets are masked: a logo change stores “(image updated)”, a PIN change “(updated)”, hashes read “(changed)”. | |
| Request metadata | IP (first address in X-Forwarded-For), browser user agent (240 chars), HTTP method and path (200 chars), and the status code. | |
| Outcome | Derived from the status code: below 400 → OK; 401 or 403 → Denied; any other 4xx/5xx → Error. | |
| When | Server time, displayed in the hotel's timezone to the second. |
- Named events are written inside the same transaction as the change, so a booking cannot be checked in without its row.
- Generic rows: any other POST, PATCH, PUT or DELETE that did not write a named event gets
http.post-style rows: Module is the first path segment, Record is any code or id in the path, and the diff holds the path and status. GET requests, health checks, sign-in endpoints, notifications, the audit log itself, demo sandboxes and search are deliberately not logged as actions. - Sign-ins and denials: successful and failed email or PIN sign-ins, sign-outs and every request refused by the permission guard are recorded with the same IP and browser details.
Outcomes
Every row is OK, Denied or Error, derived from the HTTP status, not stored.
- Request handled → OK (< 400)
- Request handled → Denied (401 / 403)
- Request handled → Error (other 4xx / 5xx)
Denied is the security view: a role trying a screen or action it does not have, or a wrong password. Error is a business refusal, a check-in on the wrong day, an overbooked room, a validation failure, which is useful when a colleague says “it would not let me”.
Tracing a change
Search the record code, narrow by person or action, open the drawer, read the diff.
- 1Type the record code in the search boxBK-1042, INV-2210, a guest code, an email, an IP address or part of an action name. The search matches action, record id, module, actor name, request path and IP.
- 2Narrow with the dropdowns or the column filtersPerson lists every account with its role; Role matches the role snapshot on the row (or the account's current role); Module and Action list the values that exist in the log. The Person, Action, Record and Outcome column titles open the same filters as popovers.
- 3Set the datesFrom / To are hotel-local calendar days: From starts at 00:00 hotel time, To includes the whole day. The Events today and Last 7 days tiles are presets that fill them for you.
- 4Open the rowThe drawer shows the actor card (name, role, email, IP, browser and device), the outcome, the request line, a link straight to the record, What changed with each field's old value in red and new value in green, and Related events on the same record so you can walk its history. Raw record opens the stored document for anything older.
- 5Export if you need a recordExport CSV honours every filter and writes at, actor, role, action, entity_type, entity_id, outcome, method, path, ip and diff (as JSON text).
Permissions and retention
Admin tier plus Security may read; nobody may edit or delete; rows are kept indefinitely.
| Role | Settings |
|---|---|
| Super Admin | Full |
| Hotel Manager | Edit |
| Security | None |
| HR Manager | View |
- A Hotel Manager reads the log even at Settings = Edit, because the role is admin tier by name. A custom role becomes admin tier the moment its matrix is set to Settings = Full.
- Security always reads it, whatever the rest of its matrix says.
- There is no write API: rows are appended by the server and never edited. Nothing in the system prunes them, so the trail is complete for the life of the hotel; use Export CSV for off-site copies.
Tips
Wildcards, denied spikes and the hotel clock.
Errors you may see
One server refusal and one client-side export failure.
The audit log is limited to Super Admin, Hotel Manager and SecurityCould not export the audit log- Admin tier
- Super Admin, Hotel Manager, or any role with Settings = Full, the roles that can manage users, roles and the PIN.
- Actor snapshot
- The name and role copied onto the row when it was written, so history survives renames and deletions.
- Diff
- The JSON stored with an event describing what was sent or what changed.