Audit log
The audit log records security-relevant changes to your Zammad system: who changed what, and when. The audit log is read-only.
Elenca
Required permission: admin.audit_log
GET-Request sent: /api/v1/audit_logs
The endpoint supports pagination. The default page size is 500. Entries are returned ordered by id (ascending). Pass ?sort_by=id and ?order_by=DESC to return the newest entries first.
Details
json
[
{
"id": 12,
"user_id": 10,
"user_fullname": "Hannah Taylor",
"action_type": "create",
"auditable_type": "ChecklistTemplate",
"auditable_id": 1,
"auditable_name": "Onboarding",
"value_from": {},
"value_to": {
"name": "Onboarding",
"active": true,
"sorted_item_names": []
},
"source_ip": "Rails runner",
"preferences": {},
"created_at": "2026-08-19T06:30:43.527Z",
"updated_at": "2026-08-19T06:30:43.527Z"
},
{
"id": 41,
"user_id": 3,
"user_fullname": "Lauren Brooks",
"action_type": "create",
"auditable_type": "Macro",
"auditable_id": 2,
"auditable_name": "RMA",
"value_from": {},
"value_to": {
"name": "RMA",
"active": true,
"perform": {
"ticket.tags": {
"value": "RMA",
"operator": "add"
},
"ticket.group_id": {
"value": "8"
},
"notification.email": {
"body": "<div>Dear #{ticket.customer.firstname} #{ticket.customer.lastname},</div><div><br></div><div>your request got forwarded to our logistics department. You'll receive instructions about how to return your product and the replacement process.</div><div><br></div><div>Best regards,</div><div>#{ticket.owner.firstname} #{ticket.owner.lastname}</div>",
"sign": "no",
"subject": "Update regarding your inquiry",
"internal": "false",
"recipient": [
"ticket_customer"
],
"encryption": "no",
"include_attachments": "false"
}
},
"ux_flow_next_up": "none"
},
"source_ip": "172.20.0.1",
"preferences": {},
"created_at": "2026-08-19T06:43:49.908Z",
"updated_at": "2026-08-19T06:43:49.908Z"
},
{
"id": 33,
"user_id": 3,
"user_fullname": "Lauren Brooks",
"action_type": "update",
"auditable_type": "Setting",
"auditable_id": 270,
"auditable_name": "pgp_integration",
"value_from": {
"area": "Integration::Switch",
"name": "pgp_integration",
"title": "PGP integration",
"options": {
"form": [
{
"tag": "boolean",
"name": "pgp_integration",
"null": true,
"display": "",
"options": {
"true": "yes",
"false": "no"
}
}
]
},
"frontend": true,
"description": "Defines if PGP encryption is enabled or not.",
"state_current": {
"value": false
},
"state_initial": {
"value": false
}
},
"value_to": {
"area": "Integration::Switch",
"name": "pgp_integration",
"title": "PGP integration",
"options": {
"form": [
{
"tag": "boolean",
"name": "pgp_integration",
"null": true,
"display": "",
"options": {
"true": "yes",
"false": "no"
}
}
]
},
"frontend": true,
"description": "Defines if PGP encryption is enabled or not.",
"state_current": {
"value": true
},
"state_initial": {
"value": false
}
},
"source_ip": "Rails runner",
"preferences": {
"changed_attributes": [
"state_current"
]
},
"created_at": "2026-08-19T06:30:53.279Z",
"updated_at": "2026-08-19T06:30:53.279Z"
}
]Mostra
Required permission: admin.audit_log
GET-Request sent: /api/v1/audit_logs/{id}
Details
json
{
"id": 41,
"user_id": 3,
"user_fullname": "Lauren Brooks",
"action_type": "create",
"auditable_type": "Macro",
"auditable_id": 2,
"auditable_name": "RMA",
"value_from": {},
"value_to": {
"name": "RMA",
"active": true,
"perform": {
"ticket.tags": {
"value": "RMA",
"operator": "add"
},
"ticket.group_id": {
"value": "8"
},
"notification.email": {
"body": "<div>Dear #{ticket.customer.firstname} #{ticket.customer.lastname},</div><div><br></div><div>your request got forwarded to our logistics department. You'll receive instructions about how to return your product and the replacement process.</div><div><br></div><div>Best regards,</div><div>#{ticket.owner.firstname} #{ticket.owner.lastname}</div>",
"sign": "no",
"subject": "Update regarding your inquiry",
"internal": "false",
"recipient": [
"ticket_customer"
],
"encryption": "no",
"include_attachments": "false"
}
},
"ux_flow_next_up": "none"
},
"source_ip": "172.20.0.1",
"preferences": {},
"created_at": "2026-08-19T06:43:49.908Z",
"updated_at": "2026-08-19T06:43:49.908Z"
}Cerca
Required permission: admin.audit_log
The search endpoint accepts the Zammad search-backend query syntax. The simplest case is a literal substring on a single indexed field such as auditable_name, auditable_type or user_fullname:
GET-Request sent: /api/v1/audit_logs/search?query={search-string}
Details
json
[
{
"id": 41,
"user_id": 3,
"user_fullname": "Lauren Brooks",
"action_type": "create",
"auditable_type": "Macro",
"auditable_id": 2,
"auditable_name": "RMA",
"value_from": {},
"value_to": {
"name": "RMA",
"active": true,
"perform": {
"ticket.tags": {
"value": "RMA",
"operator": "add"
},
"ticket.group_id": {
"value": "8"
},
"notification.email": {
"body": "<div>Dear #{ticket.customer.firstname} #{ticket.customer.lastname},</div><div><br></div><div>your request got forwarded to our logistics department. You'll receive instructions about how to return your product and the replacement process.</div><div><br></div><div>Best regards,</div><div>#{ticket.owner.firstname} #{ticket.owner.lastname}</div>",
"sign": "no",
"subject": "Update regarding your inquiry",
"internal": "false",
"recipient": [
"ticket_customer"
],
"encryption": "no",
"include_attachments": "false"
}
},
"ux_flow_next_up": "none"
},
"source_ip": "172.20.0.1",
"preferences": {},
"created_at": "2026-08-19T06:43:49.908Z",
"updated_at": "2026-08-19T06:43:49.908Z"
},
{
"id": 12,
"user_id": 10,
"user_fullname": "Hannah Taylor",
"action_type": "create",
"auditable_type": "ChecklistTemplate",
"auditable_id": 1,
"auditable_name": "Onboarding",
"value_from": {},
"value_to": {
"name": "Onboarding",
"active": true,
"sorted_item_names": []
},
"source_ip": "Rails runner",
"preferences": {},
"created_at": "2026-08-19T06:30:43.527Z",
"updated_at": "2026-08-19T06:30:43.527Z"
}
]To filter on a specific attribute rather than substring-match the whole record, prefix the attribute name. You can even use the logical AND connector to narrow down the results:
GET-Request sent: /api/v1/audit_logs/search?query=auditable_type:Macro AND user_id:3
WARNING
Search matches are case-sensitive and search only the indexed attribute fields (auditable_name, auditable_type, user_fullname and so on). The value_from and value_to payloads are not searchable.
TIP
By default the response is a bare JSON array of matching entries. Pass with_total_count=true on the URL (or with_total_count: true in the body of a POST request) to wrap the response in an object that also contains the total_count. Send a POST request when the query is too long or complex for a URL.
Field reference
id : Integer primary key of the audit log entry.
user_id- ID of the user that triggered the change.
nullwhen the entry was written by a background job without a current user. user_fullname- Full name of the user at the time the entry was written. Stored separately so it remains readable after the user account is removed.
action_type- Type of the recorded change. One of:
create(a record was added),update(an existing record was modified),destroy(a record was removed),switch_to(a user took over another user's session via View from user's perspective),switch_back_to(the original session was resumed).
auditable_id : ID of the record that was changed.
auditable_type- Class name of the record that was changed (e.g.
Macro,Setting,KnowledgeBase,ChecklistTemplate,Job). auditable_name- Display name of the changed record at the time the entry was written. Stored separately so it remains readable after the record itself is gone.
value_from- Object (JSON) holding the previous state of the audited attributes. Empty (
{}) oncreateentries. value_to- Object (JSON) holding the new state of the audited attributes. Empty (
{}) ondestroyentries. source_ip- IP address that issued the underlying request.
Rails consoleorRails runneris stored when the entry was written from a maintenance script. preferences- Object (JSON) holding additional per-entry metadata. For
updateentries this contains achanged_attributesarray listing the attributes that actually changed. created_at- Timestamp at which the entry was written. Audit log entries are append-only.
updated_at- Same as
created_atfor audit log entries. Audit log entries are append-only.
Lifecycle
A scheduled task removes entries older than 12 months every day. Use AuditLog.cleanup in the Rails console to trigger a cleanup manually.