Skip to main content

Graylog Threshold Alerts

Overview

Not all detections are triggered by a single event. Some security-relevant scenarios — such as excessive login failures, brute-force attempts, or repeated policy violations — only become meaningful when a count of events exceeds a threshold within a given time window. Graylog handles these through threshold-based Event Definitions, which are fundamentally different from standard (single-event) alerts:
Alert typeTriggerExample
StandardA single matching eventWazuh rule fires for malware detection
ThresholdCount of matching events ≥ N in a time window10+ failed logins in 5 minutes
Because threshold alerts aggregate multiple events, there is no single underlying event _id to reference. CoPilot therefore exposes a dedicated webhook endpoint (/create/threshold) that accepts the aggregated alert metadata and converts it into an Incident Management alert.

When to use threshold alerts

Threshold alerts are the right choice when you need to detect:
  • Brute-force / credential stuffing — e.g., ≥ 10 failed authentication attempts within 5 minutes for the same user or source IP.
  • Excessive privilege escalation attempts — e.g., ≥ 5 sudo failures on a single host in 10 minutes.
  • Anomalous volume — e.g., ≥ 100 Office 365 FileDeleted events from the same user in 15 minutes.
  • Repeated policy violations — e.g., ≥ 3 DLP policy hits from the same endpoint in 1 hour.
  • Scan / enumeration detection — e.g., ≥ 50 connection attempts to different ports on the same destination in 1 minute.
Rule of thumb: If the detection logic includes words like “more than,” “at least,” or “within X minutes,” you likely need a threshold alert.

Step-by-step configuration

1. Create the Event Definition in Graylog

  1. Navigate to Alerts → Event Definitions in the Graylog web UI.
  2. Click Create Event Definition.
  3. Fill in the Title and Description — this title will become the alert name inside CoPilot.

Condition Configuration

  1. Under Condition Type, select Filter & Aggregation.
  2. Define your search query (filter) to match the relevant log events (e.g., source:office365 AND event_type:login_failure).
  3. Set the Search within time window (e.g., 5 minutes).
  4. Set the Execute search every interval (e.g., 1 minute).
  5. Under Aggregation, configure:
    • Group By — the field(s) to group on (e.g., source_ip, username, agent_name).
    • Condition — e.g., count() >= 10.
⚠️ Make sure the aggregation condition accurately reflects the threshold you want. Test with Preview before saving.

2. Define the required Custom Fields

CoPilot’s /create/threshold endpoint requires four custom fields to be set on the Event Definition. These fields map the Graylog event into a CoPilot alert. Navigate to the Fields tab of the Event Definition and add the following:
Field nameTypeRequiredDescription
CUSTOMER_CODEstringThe customer code this alert belongs to (must match a customer in CoPilot).
SOURCEstringThe source/integration name (e.g., office365, wazuh, bitwarden). Also drives which OpenSearch index CoPilot searches to resolve the underlying events — see The SOURCE field and index resolution.
ALERT_DESCRIPTIONstringA human-readable description of what was detected.
ASSET_NAMEstringThe name of the affected asset (hostname, username, IP — whatever is most relevant).
For each field, you can either:
  • Set a static value (e.g., CUSTOMER_CODE = ACME) if this event definition is customer-specific, or
  • Use a template that references Graylog event fields (e.g., ASSET_NAME = ${source.agent_name}).
Example field configuration:
🚨 IMPORTANT: Do NOT add a field called COPILOT_ALERT_ID with a value of NONE. This will break the auto-alert creation functionality for standard (non-threshold) alerts.

3. Create the HTTP Notification

  1. Navigate to the Notifications tab of your Event Definition (or go to Alerts → Notifications → Create Notification).
  2. Select HTTP Notification as the notification type.
🚨 IMPORTANT: Use the standard HTTP Notification type — do NOT use the Custom HTTP Notification type. The custom type sends a different payload format that CoPilot cannot parse.
  1. Configure the notification:
SettingValue
TitleGive it a descriptive name (e.g., CoPilot Threshold Webhook)
URLhttp://<your-copilot-url>:5000/api/incidents/alerts/create/threshold
  1. Save the notification.

4. Attach the Notification to the Event Definition

  1. Go back to your Event Definition → Notifications tab.
  2. Click Add Notification and select the HTTP Notification you just created.
  3. Save the Event Definition.

5. Verify the Graylog API header

CoPilot validates inbound Graylog webhook requests using a shared header. Make sure your CoPilot deployment has the GRAYLOG_API_HEADER_VALUE environment variable set, and that the Graylog HTTP Notification sends the matching header. Check with your CoPilot administrator that the Graylog header verification is correctly configured. This is typically set during the initial CoPilot ↔ Graylog integration setup.

The SOURCE field and index resolution

The SOURCE custom field does more than label the alert — CoPilot uses it to find the underlying events behind the threshold, so it can populate the alert’s asset/title and build the event timeline.

Why CoPilot needs a mapping

A Graylog threshold/aggregation event does not include the source index pattern in the payload it sends. CoPilot receives the replay query (Lucene) and the group-by fields, but not the OpenSearch index the original messages live in — Graylog keeps that internal. To go back and pull the contributing events, CoPilot has to be told which index pattern a given SOURCE maps to.
This is not the same as the ingest-side Incident Sources mapping. Those describe per-source field names; this maps a SOURCE value to the physical index a threshold replay query should target.

How a SOURCE is resolved

CoPilot resolves the index pattern for a SOURCE in this priority order:
  1. .env mapping (THRESHOLD_SOURCE_INDEX_MAPPING) — explicit, operator-defined mappings. Highest priority.
  2. Built-in defaultswazuhwazuh-* and office365office365-*, shipped out of the box.
  3. Convention fallback — for any other SOURCE, CoPilot derives <source>-* with a timestamp time field. So a SOURCE of bitwarden automatically resolves to the bitwarden-* index with no configuration required, as long as your index follows the <source>-* naming convention.
In most cases, you don’t need to configure anything — set the SOURCE field to match your index prefix and the convention fallback handles it.

Configuring custom sources (.env)

Use the THRESHOLD_SOURCE_INDEX_MAPPING environment variable when your index name does not follow the <source>-* convention, or when the time field isn’t timestamp. It is a JSON object mapping each SOURCE to either an index-pattern string or a [index_pattern, time_field] pair:
Entries here merge over and override the built-in defaults (so you can, for example, repoint wazuh at a custom index). Malformed JSON or malformed entries are logged and ignored rather than breaking the threshold flow.
VariableDefaultPurpose
THRESHOLD_SOURCE_INDEX_MAPPING(empty)JSON object of SOURCE → index pattern (or [pattern, time_field]). Merges over the built-in defaults.
THRESHOLD_SOURCE_INDEX_FALLBACK_ENABLEDtrueWhen true, an unmapped SOURCE falls back to <source>-* / timestamp. Set false for strict behavior (no guessing — an unmapped source resolves to no underlying events).
After changing these variables, restart the CoPilot backend so the new configuration is picked up.

How it works end-to-end

  1. Graylog evaluates the aggregation query on schedule (e.g., every minute).
  2. When the threshold condition is met, Graylog fires the Event Definition.
  3. The HTTP Notification sends a POST request to CoPilot’s /create/threshold endpoint with the event payload (including your custom fields).
  4. CoPilot validates the Graylog header, extracts the required fields (CUSTOMER_CODE, SOURCE, ALERT_DESCRIPTION, ASSET_NAME), and creates a new alert in Incident Management.
  5. The alert appears in the CoPilot Incident Management → Alerts view and can be triaged, assigned to a case, or trigger downstream automation (e.g., via Shuffle).

Differences from standard alerts

Standard alertsThreshold alerts
TriggerSingle event matchAggregation count over time window
CoPilot ingestionAuto-collected from gl-events-* indicesSent via webhook to /create/threshold
Graylog notification typeNot required (CoPilot polls)HTTP Notification (standard) required
Custom fieldsCOPILOT_ALERT_ID: NONE added by defaultCUSTOMER_CODE, SOURCE, ALERT_DESCRIPTION, ASSET_NAME
Underlying eventLinks back to a specific indexed eventNo single event — aggregated context only

Troubleshooting

SymptomLikely causeFix
Alert not appearing in CoPilotNotification not attached to Event DefinitionAttach the HTTP Notification in the Event Definition’s Notifications tab
403 Forbidden from CoPilotGraylog header mismatchVerify GRAYLOG_API_HEADER_VALUE matches between CoPilot and Graylog
422 Unprocessable EntityMissing required custom fieldsEnsure all four fields (CUSTOMER_CODE, SOURCE, ALERT_DESCRIPTION, ASSET_NAME) are defined
Alert created but missing contextTemplate variable not resolvingCheck that your field templates reference valid event fields (use Graylog’s Preview)
Alert created but no timeline / underlying eventsSOURCE doesn’t resolve to a valid indexSet the SOURCE field to match your index prefix (<source>-*), or map it explicitly via THRESHOLD_SOURCE_INDEX_MAPPING — see The SOURCE field and index resolution
Log warning: No index mapping configured for threshold alert source '<x>'Convention fallback disabled and source not mappedAdd <x> to THRESHOLD_SOURCE_INDEX_MAPPING, or set THRESHOLD_SOURCE_INDEX_FALLBACK_ENABLED=true
Used Custom HTTP Notification typeWrong notification typeDelete and recreate using the standard HTTP Notification type