---
title: "Audit Console"
slug: audit-console
section: Governance & Operations
status: stable
description: "Staff-only console for browsing the audit log produced by every state-changing operation in the docs service."
last-updated: 2026-10-07
---

# Audit Console

Staff-only console for browsing the audit log produced by every state-changing operation in the docs service. Backed by GET /api/audit.

## Overview

> **Read-only surface**
>
> This page never writes back to the audit store. It is gated by the same admin-key check used for staff-only mutations elsewhere in this service. Partner-scoped consumers should continue to use /api/partner/host/overview/audit.

## Filters

Query

Entity type

Entity id

User id

Action (exact)

Action prefix

From

To

Apply filters
Reset

Refresh

## Results

Loading… / Showing $–$ of $

| Timestamp | Action | Entity | Actor |
| --- | --- | --- | --- |
| No audit entries match the current filters. | | | |

_(row above is a template, repeated for each data row)_

Previous

offset

Next

## Audit Contract

Every mutating endpoint in this service writes an audit_logs row after the response is sent (failures are swallowed and logged). Action names use a dotted vocabulary scoped by subsystem and entity.

> **Two audit stores — know which one you're querying**
>
> This console's GET /api/audit reads the docs-service (Nexus) audit store, which itself records only the asana.*, mcp.*, partner_organization.* and host_organization.note.* families. The applicant.*, host_organization.account_manager.* and host_user.* families documented below are Globus vocabulary — they are written by the Globus Hub (via its auditMutation middleware) and queried through the Globus endpoints /api/audit-logs and /api/rbac/audit-logs (also served by the alias /api/admin/audit-log, same handler and filters; System Administrators only, others get 403 FORBIDDEN), not through this console. They are cross-referenced here because this is the canonical vocabulary page. The sponsor-side Host Management surface presents those Globus host families as a per-company activity feed with a derived “Last staff action” value — see Host Audit Activity & Last Staff Action . That feed reads the Globus store; do not look for its rows in this console.

> **Device fields on Globus audit rows (user_agent, session_id)**
>
> The Globus audit_logs table carries two nullable device-forensics columns, user_agent (text) and session_id (varchar(128), indexed), added by migration 20270805000000_audit_logs_device_fields for concurrent-session analysis and session-hijack detection. They are populated on the Globus back-office authentication events: auth.login (user_agent + session_id), auth.login_failed, auth.login_mfa_pending and auth.logout (user_agent only — no session id is recorded for those), and auth.password_change (user_agent + session_id). All other audit rows, and every row written before the migration, leave both columns null.

> **Host reservation explanations in the RBAC Audit Log (All activity tab)**
>
> The Globus staff screen that reads these Globus audit rows, RBAC Audit Log, opens on an All activity tab (no category restriction) next to the existing Security Changes and Session Activity tabs. It surfaces the explanation a host captured when it requests more time on a reservation, declines a candidate, or releases or cancels a reservation. These are ordinary audit_logs rows, not a separate notes store. The Reason column shows a two-line preview and the expanded row shows the full text under a label that depends on the action: extension_request.created shows “Reservation extension request explanation” when newValue.requestType is reservation_extension (otherwise “Extension request explanation”), read from the row reason and falling back to newValue.reason; reservation.hold_extended shows “Staff hold extension note”, read from newValue.reason and falling back to the row reason; transition.approve and transition.reject on entity type extension_request show “Extension approval review note” and “Extension rejection review note”, read from newValue.reviewNotes, then newValue.reason, then the row reason. Cancellation-type rows (placement.terminated, placement.cancelled, reservation.released, reservation.cancelled, reservation.deleted and the newly included reservation.declined) show the verb, the reason-code label and an “Explanation / note” block. When newValue carries neither reasonCode nor reasonNote (older portal rows), the screen reads the row reason as “code: note” and treats the part before the first colon as a reason code only if it exactly matches a known catalogue key; any other text is kept whole as the note. The reasonCode filter on the server matches either newValue.reasonCode or, for rows with neither structured key, the part of reason before the first colon. The Cancellations only quick filter covers the six cancellation-type actions above and does not include extension requests. The action dropdown lists each action as “Label (action)”.

- asana.* — Asana project, portfolio, and task mutations.

- mcp.* — MCP coordination heartbeats and sync-status reports.

- partner_organization.* — Host overview edits via the partner portal, with proxiedByUserId recorded when staff are impersonating.

- applicant.* — Staff back-office mutations against an applicant. Includes the assignment family (applicant.assignment.created, .reassigned, .auto_assigned), the workflow-progression family read by the Operational Timeline (applicant.lifecycle.transitioned, applicant.approval.granted, applicant.approval.denied, applicant.document.reviewed), and the Next-Action invocation marker applicant.next_action.invoked (written in addition to the underlying operation's own audit row when a processor clicks a recommendation). The Activity Timeline tab on the Applicant Detail Page is a filtered view over these rows. The applicant History view reads business events from these rows through GET /api/applicants/:id/audit-trail (session, applicant.profile.view, entity scope enforced). It takes category (all, application, compliance, placement, sevis_visa or admin; the legacy channel parameter is still accepted), an optional applicationId that must belong to the applicant (404 otherwise) and cursor pagination. See Assignment History & Audit Vocabulary, Activity Timeline & Last-Touch Signals, Operational Timeline, and Next Action Engine.

- host_organization.* — Sponsor-side mutations against a host org row. Includes the Account Manager family (host_organization.account_manager.assigned, .unassigned, .redistributed, .reverted_to_region) written against entityType: host_organization whenever account_manager_user_id changes (paired with an append-only host_organization_assignment_history row in the same transaction). See Host Account Manager Assignment — Audit Vocabulary.

- host_portal.user.* — Host Portal user lifecycle mutations against entityType: partner_user. Covers host_portal.user.create / .update / .delete plus host_portal.user.reissue_invite written by the per-user Resend Invitation endpoint (the staff-side repair route writes partner.user.reissue_invite; each resend mints a fresh token and re-sends the invitation; 60-second resend cooldown per user, answered with 429 INVITE_RESEND_COOLDOWN). Actor is the host_admin or sponsor-side staff who clicked Resend (never system:unattended). See Host User Invitation — Resend Invitation and Host Governance — Users Tab Resend Visibility.

- host_organization.note.* — Host organization notes running log (entity type host_organization_note). One row per create / edit / pin / unpin / delete; the create payload lands in after, edits (host_organization.note.updated) record the note body in before and the body plus host organization id and actor label in after, pin changes write host_organization.note.pinned / .unpinned rows, and deletes record the prior body in before and deletedAt in after. See Host Camp Overview &rarr; Host Notes.

- reservation.* and extension_request.* — Host reservation lifecycle rows read by the RBAC Audit Log All activity tab. reservation.released, reservation.cancelled, reservation.deleted and reservation.declined are the cancellation-type actions (labels “Reservation released”, “Reservation cancelled”, “Reservation deleted”, “Reservation declined”); extension_request.created (“Extension request submitted”) records a host request for more time; reservation.hold_extended (“Hold extended by staff”) records a staff action that directly extends a hold. Staff decisions on an extension request are written as transition.approve and transition.reject against entity type extension_request. See the RBAC Audit Log callout above for which field supplies each explanation.

See Error Handling — Audit Log for the failure model and the per-row schema.
