Skip to content

Audit logging

Audit logging records each tool call on an Epicor instance: who called which tool, when, the outcome, how long it took and how much data came back. It is off by default and available on the Enterprise plan. Ask Cutova to enable it for your organization.

The record never holds tool inputs or results. A record holds the Epicor user, the tool name and type, the outcome, the Epicor status code on a failure, the duration, the result size and record count, a correlation id, and the time. Tool inputs and results go only to your own SIEM, and only at the Advanced and Full levels (see below); Cutova never stores them.

Open the instance and select the Audit logging tab. You see it once logging is enabled for your organization, and you need an Admin role.

  • Logging level. Choose Basic to start recording. Off records nothing. Levels above your organization’s limit are shown but cannot be chosen. The record is the same at every level. What goes to your SIEM grows with the level:

    • Basic: the record’s fields only.
    • Advanced: also the tool input (the arguments of each call).
    • Full: also the tool result.

    Inputs and results can contain Epicor business data. Cutova does not filter them for sensitive data: it sends them as the tool received and returned them and cuts each to 32 KB (see what Cutova removes). You are responsible for making sure that the tools on the instance do not return information you do not want in your SIEM. Cutova sends events only to your SIEM receiver and keeps no copy. Choosing Advanced or Full asks you to confirm, and every administrator of your organization gets an email when an instance starts or stops sending them.

  • Keep records for (days). From 0 to 30. Records older than this are deleted by a cleanup that runs about every six hours, so 0 means a record lasts until the next cleanup.

  • Strict audit. When on, a tool call is refused if its record cannot be written. Leave it off unless an unrecorded call is worse for you than a refused one. It has no effect while the level is Off.

Click Save. Each change is written to your organization’s audit log.

The Audit logging tab starts with a status line (logging level, retention, strict audit, SIEM) and three panels: Logging settings, SIEM destination and Activity. Open a panel to change it.

  1. Open SIEM destination and click Add destination.
  2. Pick your Service: SolarWinds (also Papertrail and Loggly), Sumo Logic, Better Stack, Datadog, New Relic, or Other syslog receiver for Splunk, Sentinel, QRadar, rsyslog and similar. Cutova fills in the port and the message format the service expects.
  3. Enter a Name and the receiver’s Host. Change the Port if your service gives another.
  4. Paste the service’s token or key. Paste only the token: Cutova adds the wrapper the service expects (for SolarWinds, [TOKEN@41058]).
  5. Click Test connection. Cutova connects, checks the receiver’s certificate and sends one cutova.audit.test event. Confirm that your SIEM received it.
  6. Click Save.

Advanced holds the TLS server name, a private CA certificate (when a public certificate authority does not sign the receiver’s certificate) and the source label, and the framing for Other syslog receiver. Custom as the service shows the raw structured-data, message-prefix and framing fields for a collector not in the list.

Your firewall must accept connections from Cutova. The panel shows the address Cutova sends from. The receiver must have a public address: Cutova refuses private, loopback and link-local addresses, also when a DNS name resolves to one.

Once saved and enabled, the destination receives one event for each tool call on the instance, within a few seconds, while the logging level is Basic or above. It also receives an event for each change to these settings, and a gap event when Cutova loses events. The panel shows the delivery status:

  • Sending. Events go out. If the last attempt failed, the panel shows why at once.
  • Failing. Cutova could not send for more than 5 minutes. The organization’s admins get one email (at most one an hour for each destination), and another when sending works again. Cutova keeps trying.
  • Disabled. Nothing is sent.
  • Not sending. The instance’s logging level is Off, so nothing is sent.

The sent and dropped counts start again when the Cutova server restarts.

Most hosted collectors need a token in every message. The table shows what Cutova sends for each service; with Custom, enter these values yourself. Cutova stores tokens encrypted and never shows them again. If you change the host, port or TLS server name, enter the token again: Cutova does not send a saved token to a different receiver.

Collector Host and port Framing Token field
SolarWinds Observability, Papertrail, Loggly syslog.collector.<region>.cloud.solarwinds.com:6514 (from your setup page) Octet counting Structured data: [TOKEN@41058]
Sumo Logic (Cloud Syslog source) syslog.collection.<deployment>.sumologic.com:6514 Octet counting Structured data: [TOKEN] (the token already ends in @41123)
Better Stack the ingesting host from your source, port 6514 Octet counting Structured data: [logtail@11993 source_token="TOKEN"]
Datadog intake.logs.datadoghq.com:10516 (US1; other sites differ) Newline Message prefix: your API key
New Relic newrelic.syslog.nr-data.net:6514 (US) Newline Message prefix: your license key
Splunk, Microsoft Sentinel, QRadar, Graylog, Elastic, rsyslog, syslog-ng your receiver Octet counting. If events arrive joined together, set Framing to Newline under Advanced. None

The vendor formats in this table come from each vendor’s own setup guide. If yours differs, follow the vendor’s guide; Test connection shows whether Cutova can reach the receiver, and your collector’s search shows whether it accepted the event.

Each event is one RFC 5424 syslog message over TLS:

<110>1 2026-10-11T14:03:22.418Z cutova cutova - mcp.tool_call [TOKEN@41058] {"schema":"cutova.mcp.v1",...}
  • PRI is 110 (facility log audit, severity informational).
  • HOSTNAME is cutova. APP-NAME is the source label, or cutova.
  • MSGID is the event type. STRUCTURED-DATA is your collector token, or -.
  • MSG is the event as one line of JSON. Every event has schema (cutova.mcp.v1), id, type, ts, tenantId and instanceId.

Delivery is best effort. Syslog has no acknowledgement, so an event can be lost when the receiver drops a connection, and a retried batch can send an event twice. Use id to drop duplicates. When Cutova cannot reach the receiver, events wait in memory for at most 5 minutes and are then dropped. Cutova does not store them. Known losses are reported as gap events.

One for each tool call. It never contains the credentials Cutova uses (your users’ sign-in tokens, the Epicor credentials Cutova connects with). At Basic it contains no tool inputs or results; Advanced and Full add them (below), and Cutova does not filter them for sensitive data.

{
"schema": "cutova.mcp.v1",
"id": "8f4c2e1a-…",
"type": "mcp.tool_call",
"ts": "2026-10-11T14:03:22.418Z",
"level": "BASIC",
"tenantId": "…",
"instanceId": "…",
"instanceName": "EpicorLive",
"principalId": "…",
"epicorUser": "jgomez",
"company": "EPIC06",
"plant": "MfgSys",
"correlationId": "…",
"surface": "jsonrpc",
"tool": { "id": "…", "name": "SalesOrder_List", "type": "BAQ" },
"outcome": "ok",
"epicorStatus": null,
"durationMs": 412,
"result": { "bytes": 18234, "recordCount": 57 }
}
  • ts is when the call started.
  • company and plant are the ones the call ran in. plant is empty when Epicor’s default was used.
  • surface is jsonrpc (an AI client), rest_mcp_invoke or rest_tool_invoke.
  • outcome is ok, tool_error, not_found, denied, disabled or exception.
  • principalId is null on the rest_tool_invoke route.
  • epicorStatus is the status code Epicor returned with an error (tool_error), else null. A successful call has null. result.bytes and result.recordCount are null when the call returned no result.
  • result.bytes on the jsonrpc surface is the size of the whole response the AI client got: the formatted text, the structured copy and the company footer. On the REST routes it is the size of the result data. So at Full, result.bytes on jsonrpc is larger than output.bytes, which counts the result data only.
  • When the call did not resolve to a tool (not_found, or refused before the lookup, where tool.type is unknown), tool.id and tool.name are null and requestedName holds the name the caller asked for, limited to letters, digits and . _ : - (null when nothing is left).
  • correlationId links related events. It is the caller’s X-Correlation-ID when that is made of letters, digits and . _ : - (at most 128), otherwise one Cutova makes. It proves nothing about the caller.

At Advanced the event also has input, and at Full also output when the call ran and returned a result to the caller (a refused or not-found call has none):

"input": { "args": { "$filter": "CustNum eq 12", "$top": 10 }, "bytes": 34, "truncated": false },
"output": { "data": { "value": [ ... ] }, "bytes": 18234, "truncated": false }
  • level is the level the call ran at: BASIC, ADVANCED or FULL.
  • input.args is the arguments as the tool received them. On the REST routes it is { "params": ..., "input": ... } as sent.
  • output.data is, for an Epicor tool (BAQ, Function, business object), the data Epicor returned, before Cutova formats it for the AI client; on a failure, the error (code, reason, detail). For other tools it is the structured result when the tool has one, else the result text.
  • bytes is the size of the input or result as JSON, before anything is removed. It is null when the value is over 64 KB: Cutova reads only the first 64 KB of a value, so the full size is not known. The result.bytes field above still gives a size for a large Epicor result.
  • Over 32 KB, args or data is replaced by preview, the first 32 KB of the JSON as text, and truncated is true.
  • input.args is what the caller sent, before Cutova resolves file references.
  • Cutova reads the arguments only from a request of 256 KB or less (after decompression). For a larger request, input.preview says the input was not read, and bytes is null.

What Cutova removes from inputs and results

Section titled “What Cutova removes from inputs and results”

Cutova removes only what it puts into an input or result itself, and the network addresses of its own side of a failed call:

Found Sent as
The requestState token Cutova issues when a tool asks the user a question "[credential]"
The signature of an upload or download link Cutova signs (/uploads/… or /downloads/…, then ?exp=…&sig=…). A sig parameter on any other link is left as is. sig=[credential]
A private network address in an error result "[address]"

Nothing else is removed or changed. Cutova’s own credentials (your users’ sign-in tokens, the Epicor credentials Cutova connects with) are never part of a tool’s input or result, so they are never in an event. A secret that your data or the tool arguments hold is sent as is: Cutova does not guess which of your fields are secret.

Sent when someone changes what this instance sends: action, actor, from and to. It never contains a token.

action When from and to actor
instance_config An admin saves the logging settings level, strictAudit, trailRetentionDays (from is null the first time) the admin
entitlement Cutova changes your organization’s limit (to every enabled destination) the level Cutova
destination_saved An admin saves the destination name, url, enabled, framing, TLS server name, source label, and whether a CA and tokens are set (from is null when it is new) the admin
destination_removed An admin removes the destination as above; to is null the admin

The admin is shown by email, or by user id when the account has no email. Only enabled destinations receive these events. When an admin changes the receiver (host, port, TLS server name, CA, framing or token), disables it or removes it, the old receiver gets this event first, once and only if it was enabled, so a change of destination is visible in the SIEM that stops receiving events.

Sent at the start of the next batch after Cutova lost events: reason, droppedCount, firstDroppedTs, lastDroppedTs.

  • dropped: events waited longer than 5 minutes, or the memory limit was reached. The times are the first and last lost event.
  • process_restart: the Cutova server restarted, for example for an update. Events waiting at that moment are lost; the count and the times are unknown (null). Each destination that existed before the restart gets one with its first batch, also when nothing was lost.

A gap event keeps its id when Cutova sends it again after a failed try, unless more events were dropped meanwhile: it then goes out with a new id and the new count.

The Activity section lists the newest 1,000 calls on the instance. Search by tool, Epicor user, outcome or entry point, and page through the results. Times are in your browser’s time zone.

Export CSV downloads the calls that match your search, with times in UTC. The file opens directly in Excel and Google Sheets.

An outcome of pending appears only with strict audit on: the call was admitted and its result has not been written yet, because it is still running or the result write failed.

If logging is turned off for your organization

Section titled “If logging is turned off for your organization”

Nothing new is recorded and the settings are hidden. The calls already recorded stay readable on the tab until they are older than the instance’s retention, then the cleanup deletes them.