Shadow AI settings
Manage API keys, syslog sources, rate limiting, data retention, and risk score calculation.
Overview
The Settings page is where you configure data sources, access controls and operational parameters for Shadow AI detection. It's divided into six sections: API keys, syslog sources, data formats, rate limiting, data retention and risk score calculation. Only Admins can manage API keys, syslog sources and the rate limit.
API keys
API keys authenticate events sent to the REST API, for example from a SIEM webhook, a script or a browser extension. Syslog sources do not use these keys; each source has its own key (see below).
Creating a key
- Click "Create API key"
- Optionally enter a label (e.g., "Zscaler proxy")
- Click "Create"
- Copy the full key immediately, it won't be shown again
Managing keys
The API keys table shows each key's prefix, label, status (Active or Revoked), creation date and last-used date. You can:
- Revoke: Deactivates the key. Integrations using this key will stop working. This cannot be undone. The server you are connected to refuses the key at once; with several backend replicas, the others can accept it for up to 5 minutes.
- Delete: Permanently removes a revoked key from the list. Only revoked keys can be deleted.
Syslog sources
Syslog sources define where network traffic data comes from. Each source has a parser type, a secret key that every log line carries, and an optional allowed sender IP. Only Admins can manage them. The setup steps are in the Integration guide.
A line above the table shows whether the syslog listener is on and where to send logs (port 6514 by default, over TLS or plain TCP). The listener is off until the server operator sets SHADOW_AI_SYSLOG_ENABLED=true. If it is turned on but could not start, the line says so; the server log has the reason. The hosted service does not offer syslog yet.
Parser types
| Parser | Description |
|---|---|
| Zscaler | Reads key=value lines with user, dst, method, uri, action, department and timestamp |
| Netskope | Reads a JSON object with user, url (else domain or site), method, action, department and timestamp |
| Squid proxy | Reads the native Squid access log. A DENIED result code is blocked, anything else is allowed |
| Generic key-value | Reads key=value lines and CEF extensions with suser, dhost, request, requestMethod, act, department and timestamp (or CEF rt, end, start). The default for a new source |
The source table
| Column | Meaning |
|---|---|
| Allowed sender IP | If set, lines from other IPs are dropped. A value that is not an IP address is flagged for you to clear or fix. |
| Parser | The parser type for this source. |
| Key | The first characters of the key, or "No key yet". Use "Generate key" or "Regenerate key" to create one. |
| Last received | When the last event from this source was stored, or "Never". |
| Accepted | Events stored. |
| Dropped | Lines that could not become events. |
| Last drop reason | Why the last line was dropped, in plain words (see the table below). |
| Status | Active or Inactive. |
| Last drop reason | Meaning |
|---|---|
| No user email in the log line | The user field is missing, empty or not an email address. |
| No destination host in the log line | The line has no destination host or URL. |
| Line does not match the parser type | The parser could not read the line, or the line repeats a field such as user, destination, action, method, department or timestamp. |
| Sent from an IP that isn't allowed or is shared with another source | The sender IP is not the source's Allowed sender IP, or two sources share an IP in IP-only mode. |
| Over the hourly event limit | The hourly rate limit was reached, or the listener's queue was full. |
Source keys
When you add a source, the "Your source key" dialog shows the key once, with a copy button and a ready-made vw_source=<key> line. VerifyWise stores only a hash and the first characters of the key, so it cannot show the key again. If you lose it, click "Regenerate key". The old key stops working at once on the server you used and within 60 seconds on any other replica. Removing a source stops processing events from it.
Data formats
This section documents the exact data formats VerifyWise expects when ingesting Shadow AI events, whether through the REST API or via syslog forwarding.
REST API event schema
Send events via POST /api/v1/shadow-ai/events with a JSON request body containing an events array of up to 10,000 events. Authenticate using the X-API-Key header with a valid API key.
{
"events": [
{
"user_email": "alice@company.com",
"destination": "chat.openai.com",
"timestamp": "2026-02-09T14:32:00Z",
"uri_path": "/v1/chat",
"http_method": "POST",
"action": "allowed",
"department": "Engineering",
"job_title": "Senior Engineer",
"manager_email": "bob@company.com"
}
]
}| Field | Required | Description |
|---|---|---|
| user_email | Yes | Email address of the user who made the request. Stored in lowercase, with surrounding spaces removed. Up to 255 characters |
| destination | Yes | Hostname or domain of the AI tool (e.g., chat.openai.com). Stored in lowercase. Up to 512 characters |
| timestamp | Yes | ISO 8601 timestamp of the event. A timestamp that cannot be read fails the whole request |
| uri_path | No | URL path of the request (e.g., /v1/chat). Left out if longer than 2,048 characters |
| http_method | No | HTTP method (GET, POST, etc.). Stored in upper case |
| action | No | "blocked" if the proxy refused the request. Any other value, or none, is stored as "allowed" |
| department | No | Department of the user (e.g., Engineering, Finance). Used in risk scoring and department alerts |
| job_title | No | Job title of the user |
| manager_email | No | Email address of the user's manager. Stored in lowercase |
X-API-Key header with a valid API key created from the API keys section above. Requests without a valid key receive a 401 response. The Integration guide lists every error response.Syslog format examples
Syslog messages arrive over TCP (TLS by default), newline-delimited or octet-counted, up to 64 KiB each. An RFC 3164 or RFC 5424 header is removed when present. Every line must carry the source key as vw_source=<key> at the start or the end of the line, or as a top-level "vw_source" member when the line is a JSON object. A key anywhere else is ignored. VerifyWise removes the key before parsing and never stores it. Below is one example per parser, with the field added.
Zscaler (key=value)
user=alice@company.com dst=chat.openai.com method=POST uri=https://chat.openai.com/v1/chat action=allowed department=Engineering vw_source=YOUR_SOURCE_KEYNetskope (JSON-in-syslog)
{"user":"alice@company.com","url":"https://chat.openai.com/v1/chat","method":"POST","action":"allow","department":"Engineering","timestamp":"2026-02-09T14:32:00Z","vw_source":"YOUR_SOURCE_KEY"}Send the whole object on one line. With newline-delimited framing, a pretty-printed object is split into lines that cannot be read.
Squid (space-delimited)
1707489120.000 200 10.0.0.1 TCP_MISS/200 1024 POST https://chat.openai.com/v1/chat alice@company.com DIRECT/chat.openai.com vw_source=YOUR_SOURCE_KEYGeneric key-value (CEF-style)
suser=alice@company.com dhost=chat.openai.com requestMethod=POST act=allowed vw_source=YOUR_SOURCE_KEYField mapping
Each parser maps its fields to the normalized event schema. The user must be a valid email address; it is stored in lowercase. For the action, the first word of the value decides: block, deny, drop, reset or reject (and forms such as blocked, denied or dropped) become blocked, and allow, permit, pass or accept (and forms such as allowed or permitted) become allowed, so Blocked by policy is blocked. A negated allow such as Not allowed or disallowed is also blocked. Any other value, including words that only begin with one of these such as password_reset, or no action at all, is stored as allowed. The timestamp comes from the payload field when present, then from the syslog header, then from the time VerifyWise received the line. A timestamp without a time zone is read as UTC; a zone it states (an offset, Z, PST, CEST, also in brackets such as (Tokyo JST)) is used. A zone name VerifyWise does not know, such as IST, makes the payload time unreadable, so send an offset or Z if your source logs in another zone. A timestamp more than a year before the time the line was received, or more than a day after it, is ignored. A destination can be a host name, an IPv4 or IPv6 address (bare or in brackets) or a URL, and is stored in lowercase. A destination of -, N/A or none is skipped and the next field (such as the URL) is used. An email address longer than 255 characters or a destination longer than 512 drops the line, and a department is cut to 255 characters.
| Normalized field | Zscaler | Netskope | Squid | Generic KV |
|---|---|---|---|---|
| user_email | user | user | field 8 | suser |
| destination | dst, else host of uri | url (hostname), else domain or site | url (hostname) | dhost, else host of request |
| uri_path | uri (path) | url (path) | url (path) | request (path) |
| http_method | method | method | field 6 | requestMethod |
| action | action | action, else activity | field 4 (result code) | act |
| timestamp | timestamp | timestamp | field 1 (epoch) | timestamp, else CEF rt, end or start |
| department | department | department | — | department |
Rate limiting
Rate limiting caps how many events your organization can ingest per hour, across the REST API and syslog. It protects the system from a misconfigured data source or a traffic spike. Only Admins can change it. The count is kept in memory by each backend process, so with several backend replicas the effective limit is the setting times the number of replicas.
- Set a number to cap hourly ingestion. Set 0 or leave the field empty for no limit (the default).
- The hour starts with the first event counted, not on the clock hour.
- A REST request that would go over the limit is refused whole with a 429 response. Syslog events over the limit are dropped and show "Over the hourly event limit" on the source.
- A change applies to the REST API at once and to syslog within 30 seconds.
Data retention
Data retention settings control how long different types of Shadow AI data are stored before automatic cleanup. Three categories can be configured independently:
| Category | Default | Description |
|---|---|---|
| Raw events | 30 days | Individual event records from ingestion (highest volume) |
| Daily rollups | 365 days | Aggregated daily statistics (lower volume) |
| Alert history | 90 days | Records of triggered alert rules |
Each period can be 1 to 3,650 days. Cleanup runs once a day, so a change applies at the next run. Raw events are removed by the time of the event, not the time it arrived. Admins can change these periods, and so can a custom role that has the organization admin permission. Each change is recorded in the audit ledger.
Risk score calculation
The bottom of the Settings page explains how the risk score is calculated. Each AI tool receives a score from 0 to 100, recalculated nightly using four weighted factors:
| Factor | Weight | Description |
|---|---|---|
| Approval status | 40% | Unapproved tools (not in model inventory or not approved) receive the maximum score. |
| Data & compliance | 25% | Based on training-on-data policy, SOC 2, GDPR, SSO and encryption at rest. |
| Usage volume | 15% | Normalized against the organization average. Higher usage increases the score. |
| Department sensitivity | 20% | Uses the highest sensitivity among departments that used the tool in the last 30 days. Finance, Legal and HR are rated highest (80). Events without a department add nothing here. |