Audit log

Record API traffic across REST, SOAP, IMAP and pub/sub, with database storage.

The audit log records structured events describing the traffic that flows through your integrations. Every event includes a correlation ID (CID), a timestamp, the name of the object it belongs to, the outcome, the size of the data and the data itself.

Traffic analytics drill-downs and the per-caller usage reports of API versioning both read these events, so an object with its audit log off contributes to neither.

The audit log

The audit log is source-agnostic - the same log, storage and Dashboard views serve everything below. The source is what an event is filed under, and it is also the first filter on every screen.

Message traffic

SourceWhat is recorded
REST channelsEach request received and each response sent
SOAP channelsEach request received and each response sent
REST outgoingEach request sent and each response received
SOAP outgoingEach request sent and each response received
Pub/subEach message published, delivered, or whose delivery failed or expired
IMAPEach message received, marked as seen or deleted, with its attachments
SMTPEach message sent, with its attachments, and each credential failure
File transferEach file transferred - metadata always, the file's bytes only when the connection opts in
SQLEach statement run through an outgoing SQL connection with auditing turned on, with its duration
LLMEach model invocation, with its duration
MCPEach request an AI agent makes - initialize, tools/list, tools/call - with payload sizes only, never payloads themselves
MLLP channelsEach HL7 message received and the ACK sent back
MLLP outgoingEach HL7 message sent and the ACK received
FHIR outgoingEach FHIR request sent and the response received
OdooEach remote call, with its duration
Microsoft cloudEach call a Microsoft 365, Teams, Power Automate or Fabric connection makes - with Microsoft 365, that is every Graph call, mail, OneDrive and SharePoint included

Configuration and access

SourceWhat is recorded
Log accessEach configuration object created, edited or deleted, with a masked before-and-after summary, and each time somebody opened a message body - who, which message, which screen

This is the source to look at when the question is who changed something or who read a payload. Opening a message in the audit log is itself an audited act.

Scheduler

SourceWhat is recorded
SchedulerEach job run - the job, the service, the outcome, how long it took, how late it started and the log lines it produced

Probes

These are events Zato produces about itself rather than about traffic somebody sent:

SourceWhat is recorded
REST checksEach health check ping of an outgoing REST connection
SOAP checksEach health check ping of an outgoing SOAP connection
CertificateEach certificate expiry check, with the days remaining
Microsoft healthEach poll of a Microsoft service's own reported health state
Test transferEach test file transfer - upload, download, compare, delete

Health check events are kept apart from the connection's own traffic and counted separately from it.

Alerts

Alerting has no source of its own. When an alert is raised it is recorded under the source of the thing it is about, as an alert-raised event, and an AI diagnosis of it as alert-diagnosed. An alert about an outgoing REST connection is therefore a rest-outgoing event, sitting in the same timeline as the calls that caused it. See alerting.

Only user-defined objects are recorded. Channels and connections that are internal to the platform, such as the built-in publish/subscribe REST channels, are skipped entirely.

Auditing AI agents? The MCP audit log page describes the MCP-specific schema - event types, the tool and caller columns, and why payloads are never recorded for agent traffic.

Querying from Python

Your own services can query everything the Dashboard shows - self.audit searches events by free text, source, outcome or time window, returns the newest event time of each channel or connection and reads stored payloads, with no SQL and no database configuration:

# Zato
from zato.common.audit_log.api import AuditOutcome

events = self.audit.search(outcome=AuditOutcome.Error, time_from='2026-08-29')

The whole API, with the filters and the shape of what comes back, is on the audit log Python API.

Example: a REST channel

In Dashboard, go to Connections → Channels → REST and click the Audit log link in the channel's row.

One invocation of a channel produces two events:

  • request-received - the incoming request, including its payload
  • response-sent - the outgoing response, including its body

Each event holds the CID shared by the request and its response, the name of the service that handled the request, the outcome (ok or error), the size of the data and a preview of the data itself. Clicking the CID opens the complete message.

The same applies to outgoing REST connections, under Connections → Outgoing → REST, whose events are request-sent and response-received. Pinging a connection - from the Dashboard or through a health check - writes the same request and response pair, but under the connection's health source, so the checks stay apart from the traffic the connection delivers.

Example: an IMAP connection

In Dashboard, go to Connections → Email → IMAP and click the Audit log link in the connection's row.

Working with a mailbox produces these events:

  • message-received - a message was downloaded from the server
  • message-marked-seen - a message was marked as seen
  • message-deleted - a message was deleted

Each event records the CID, the folder the message lives in, the message ID, the outcome, the size and a preview of the message.

Turning the audit log on and off

Each item covered by the audit log has its own Audit log checkbox, right next to the Active one in its create and edit forms. The checkbox is on by default - every new item records events unless you decide otherwise.

The checkbox exists for:

  • REST and SOAP channels
  • Outgoing REST and SOAP connections
  • IMAP and SMTP connections
  • HL7 MLLP channels and outgoing connections
  • Outgoing FHIR connections
  • Publish/subscribe topics
  • MCP gateways

Unchecking it stops new events for that item only - traffic keeps flowing as before and other items keep recording theirs. Checking it again resumes the events. Events recorded earlier remain in the database either way, subject to the usual retention.

Two kinds of items opt in with something richer than a checkbox:

  • Outgoing SQL connections have an audit level instead - off, which is the default, statement, which records each statement's text, statement-params, which adds the statement's parameters, and full, which also keeps the rows that came back. A connection left at off records nothing.
  • File transfer connections always record each transfer's metadata, and their should_store_content field decides whether the file's own bytes are kept with the event too - it is off by default.

In enmasse, the toggle is the is_audit_log_active attribute. For channels, connections and topics it is exported only when it is off - an item without the attribute in YAML has its audit log on. MCP gateways are the exception - their attribute is exported when it is on and a gateway without it in YAML has its audit log off:

channel_rest:
  - name: orders.api
    service: orders.handler
    url_path: /orders
    is_audit_log_active: false

The whole environment

The audit log can be switched off for an entire environment:

export Zato_Audit_Log_Enabled=False

Nothing is recorded anywhere, for any object, and alerting has nothing left to measure. It takes effect without a restart.

Storage and configuration

By default, all events go to an SQLite database file called audit.db, shared by all servers of an environment, and there is nothing to configure.

For production environments with multiple hosts or higher volumes, the audit log can store its events in an external database instead - MySQL, PostgreSQL or Oracle DB. Which database is used comes from environment variables that all the servers and the Dashboard read:

VariableDefaultDescription
Zato_Audit_Log_DB_TypesqliteThe database type - one of sqlite, mysql, postgresql or oracle
Zato_Audit_Log_DB_Host(none)The host the database runs on
Zato_Audit_Log_DB_Port3306, 5432 or 1521The port the database listens on, defaulting per database type
Zato_Audit_Log_DB_Username(none)The username to connect with
Zato_Audit_Log_DB_Password(none)The password to connect with
Zato_Audit_Log_DB_Nameaudit.dbThe database name - the service name with Oracle DB, or the full file path with SQLite
Zato_Audit_Log_DB_SSLoffWhether to encrypt the connections with SSL/TLS - on or off
Zato_Audit_Log_DB_SSL_CA_File(system store)Path to the CA certificate the server certificate is verified against
Zato_Audit_Log_DB_SSL_Cert_File(none)Path to the client certificate, for mutual TLS
Zato_Audit_Log_DB_SSL_Key_File(none)Path to the client private key, for mutual TLS
Zato_Audit_Log_DB_SSL_VerifyonWhether to verify the server certificate and hostname when SSL is on

The schema is created automatically the first time a server connects - there is no migration step. What you do need to create upfront is the database itself and its user, which is described separately for each database:

For instance, this points the audit log at a PostgreSQL server:

export Zato_Audit_Log_DB_Type=postgresql
export Zato_Audit_Log_DB_Host=db.example.com
export Zato_Audit_Log_DB_Port=5432
export Zato_Audit_Log_DB_Username=zato_audit_log
export Zato_Audit_Log_DB_Password=my.secret.password
export Zato_Audit_Log_DB_Name=zato_audit_log

How events are written

By default every event is written to the database as it happens. Events can be batched instead:

VariableDefaultDescription
Zato_Audit_Log_Flush_Max_Size1How many events are buffered before they are written. 1 means no buffering - every event is written as it happens
Zato_Audit_Log_Flush_Max_Wait_Ms500How long a buffered event may wait before it is written regardless of how few there are

Raising the flush size means fewer writes, at the cost of losing the buffered events if the container stops abruptly.

Attachments - the files delivered by IMAP and SMTP messages and by file transfers - have a size cap of their own. An attachment bigger than the cap is still recorded as an event, but its bytes are not stored:

# 10 MB by default
export Zato_Audit_Log_Max_Attachment_Size=52428800

Retention

Events are kept for 30 days by default. Older events are deleted automatically and periodically by the servers themselves, on every database type.

The window is configurable with an environment variable that all the servers read:

export Zato_Audit_Log_Retention_Days=90

Per-source retention

Not all events are worth keeping for the same length of time. Any source can be given a window of its own by appending its name to the variable, upper-cased with hyphens turned into underscores:

export Zato_Audit_Log_Retention_Days_REST_CHANNEL=14
export Zato_Audit_Log_Retention_Days_SQL_OUTGOING=7

Two sets of sources already have a default of their own:

SourceVariable suffixDefault
REST checksREST_OUTGOING_HEALTH7 days
SOAP checksSOAP_OUTGOING_HEALTH7 days

A source with a default of its own keeps it unless you set that source's own variable.

Keeping payloads for less time than events

Message payloads are usually the bulk of the database, and they are the part that is most sensitive. They can be expired ahead of the events that contain them, leaving the record of what happened while removing what was in it:

VariableDefaultDescription
Zato_Audit_Log_Content_Retention_Days0How many days payloads are kept. 0 means payloads live exactly as long as their events
Zato_Audit_Log_Content_Retention_Days_<SOURCE>0The same, for one source, with the suffix built exactly as above

So this keeps a quarter of history while holding payloads for a week:

export Zato_Audit_Log_Retention_Days=90
export Zato_Audit_Log_Content_Retention_Days=7

Payloads of failed events are exempt - an event whose outcome is an error keeps its payload, because that is the one you will want when somebody asks what went wrong.

Archiving before deletion

If a directory is named, everything about to be deleted is written out first as JSON Lines:

export Zato_Audit_Log_Archive_Dir=/opt/zato/audit-archive

Each run produces a file named after the moment it started, for instance audit-archive-20260812-030000.jsonl. Unset, which is the default, nothing is archived and expired rows are simply deleted. Point it at a mounted volume rather than the container filesystem.

Retention windows, payload windows and archive directories can also be kept in configuration as named retention policies, imported and exported with the rest of an environment through enmasse.

Retention does not bound long-term trends - traffic analytics extracts hourly aggregates from the audit log before its rows expire and keeps them indefinitely, so quarter-long trend charts work regardless of how long the raw events are retained.

SSL/TLS

Connections to MySQL, PostgreSQL and Oracle DB can be encrypted with SSL/TLS. Turn it on with Zato_Audit_Log_DB_SSL and point Zato_Audit_Log_DB_SSL_CA_File at the CA certificate that issued your database server's certificate:

export Zato_Audit_Log_DB_Type=mysql
export Zato_Audit_Log_DB_Host=db.example.com
export Zato_Audit_Log_DB_Username=zato_audit_log
export Zato_Audit_Log_DB_Password=my.secret.password
export Zato_Audit_Log_DB_Name=zato_audit_log

export Zato_Audit_Log_DB_SSL=on
export Zato_Audit_Log_DB_SSL_CA_File=/path/to/ca.crt

The server certificate and its hostname are verified by default. If your database uses a certificate whose hostname does not match, e.g. in a development environment, verification can be turned off with Zato_Audit_Log_DB_SSL_Verify=off - the connection remains encrypted but the certificate is not checked.

If the database requires client certificates (mutual TLS), add the certificate and its key:

export Zato_Audit_Log_DB_SSL_Cert_File=/path/to/client.crt
export Zato_Audit_Log_DB_SSL_Key_File=/path/to/client.key

How to enable TLS on the database server itself, including how to require it for all connections, is covered in the per-database setup pages:

The audit log and alerting

Alerting measures the audit log alone. For an object with an Audit log checkbox, switching the checkbox off also stops its alerts, while sources without a checkbox keep recording and keep being measured.

An alert is recorded here as an alert-raised event under the source it concerns, and the notification you receive links back into these screens, filtered to the object that failed. When the event that failed can be resubmitted, that link arrives with the resubmit action already armed.

Learn more