Investigator API¶
The Investigator API provides read-only access to alerts, detections, and raw log data, enabling seamless integration with security platforms such as SIEM, SOAR, and custom platforms. This access allows you to automate case management, synchronize statuses, and enrich third-party data.
Key features¶
The Investigator API includes the following features:
Flexible data retrieval: Use precise, single-request queries for only the data you need.
Secure access: Authenticate securely with API keys managed directly through the Investigator UI.
Handle large datasets: Built-in support for pagination and filtering ensures efficient retrieval of large result sets.
Streamline workflows: Dedicated capabilities for integrating with SOAR and SIEM platforms.
Asynchronous log extraction: Programmatically extract structured log data using a two-step job creation and polling process with the Logscale Query Language (LQL).
Prerequisites¶
To get started with the Investigator API, you will need the following:
- API endpoint and schema: The Investigator API uses a GraphQL schema to structure queries. Use the endpoint that corresponds to your service region:
Americas:
https://api.investigator.corelight.com/graphqlEurope (EU):
https://eu.api.investigator.corelight.com/graphqlMiddle East (ME):
https://me.api.investigator.corelight.com/graphqlAsia Pacific (APAC):
https://apac.api.investigator.corelight.com/graphql
API key authentication: An active API key is required for all requests.
- API key management and policy
Key management privileges: Your account must have Admin privileges to manage (create, rotate, or revoke) API keys in the Investigator UI.
Principle of least privilege: Adhere strictly to the principle of least privilege. When granting permissions, only enable the specific read-only permissions (detections, alerts, or logs) necessary for the consuming application or service to perform its required task.
Key limit: Investigator supports a maximum of 20 active API keys per tenant.
Authentication and API key management¶
This section details the general workflow for generating and using the API key required for all requests to the Investigator API.
API key authentication
To use the Investigator API, every HTTP request must be authenticated.
Requirement: An API key must be included in the request header. See the Request Structure and Guidelines section for the required header format.
Active keys: The system supports a maximum of 20 active API keys per tenant, allowing you to manage access for different services or users.
Purpose of tools: Tools such as
curlor Postman are used only to send authenticated queries to the API, not for key creation or management.
Managing an API key
API key management is performed exclusively within the Investigator UI.
Location: Keys are managed under General Settings | API Keys.
Required privilege: You must have Admin privileges in the Investigator UI to manage keys.
Key lifecycle actions: You can:
Create a new API key to initiate access.
Rotate an existing key to enhance security or replace an exposed key.
Revoke a key that’s no longer needed or may have been compromised.
Create an API key¶
To generate a new API key in the Investigator UI:
Navigate to the gear icon (left navigation) and select General Settings. Then, under the API Keys section, click the + New Key button.
In the Create a new API key modal, enter a descriptive Name for the key.
Select an Expiration Date. The default is 180 days. You can adjust this to a different timeframe (for example, 1 year, 5 years, or a custom date) or choose ‘No Expiration’.
Under Permissions, enable only the necessary read-only permissions (adhering to the principle of least privilege):
Detections: Grants read-only access to detection data.
Alerts: Grants read-only access to alert data.
Logs: Grants read-only access to log data.
Click Create Key.
A success modal displays showing your new API key. You must copy this key and store it in a secure location immediately. Important: The API key is shown only once upon creation. For security reasons, you will not be able to view the key again after you close this modal. If the key is lost, you must revoke it and create a new one.
Rotate an API key¶
Rotate an API key to replace it, typically when the existing key is exposed or due for renewal.
Go to General Settings | API Keys.
Locate the key to replace and click the Rotate icon at the end of its row. A confirmation modal displays.
Select a New Key Expiration. The default (180 days) can be adjusted or set to ‘No Expiration’.
Click Generate New Key.
A success modal displays showing your new API key. Copy this new key and store it in a secure location immediately.
Important: When you rotate a key, the original key is immediately and permanently revoked. This action does not affect any other active API keys. You must update all systems using the original key with the new one immediately to avoid service interruption.
Revoke an API key¶
Revoke a key to permanently disable it, typically when it is no longer in use or has been compromised.
Go to General Settings | API Keys.
Locate the key to revoke and click the trash can icon at the end of its row.
Confirm the action by clicking Revoke Key.
Important
Once revoked, the key is permanently invalid. All requests attempting to use this key will fail.
Quickstart guide¶
This Quickstart is designed to get you up and running quickly by using a simple curl command to verify your API key and endpoint.
Prerequisites¶
To run this verification, ensure you have the following readily available:
A generated API key: Your key must be active and have at least the alerts read-only permission enabled. See Authentication and API Key Management for key creation steps.
A command-line tool: Use a tool such as
curlto send the request.The API endpoint: The regional GraphQL URL corresponding to your service.
Step 1: Prepare the command¶
The sample command below uses two placeholders you must replace before running it:
YOUR_ENDPOINT: Replace this with the full API endpoint URL for your service region (for example, https://api.investigator.corelight.com/graphql).
YOUR_API_KEY: Replace this with the actual API key you generated and stored from the Investigator UI.
Step 2: Run the request¶
This example sends a minimal GraphQL query to fetch the alert_id and score for the 5 most recent alerts, sorted in descending order by their start time.
curl -X POST YOUR_ENDPOINT \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "query { alerts (offset: 0, size: 5, sort: [{sort_by: \"alert_timestamp.start\", sort_dir: \"desc\"}]) { alerts { alert_id score } } }"
}'
Step 3: Review the results¶
Success: If the request is successful, the console will print a JSON response containing an
alertsarray with the requested fields, confirming your successful connection and authentication.- Error: If the request fails, the API returns a standard HTTP status code, which helps you troubleshoot immediately:
401 Unauthorized: Missing or invalid key. Action: Check the Authorization header format and the key value.403 Forbidden: Insufficient permissions. Action: Ensure the key has thealertsread-only permission.400 Bad Request: Malformed syntax (GraphQL query or JSON payload). Action: Check the command’s syntax within the-dflag.
Next steps¶
You have successfully verified your connectivity and authentication with the Investigator API. Now that you’ve confirmed your setup, go to the following Making API requests request section to learn the required technical specifications for all API communications.
Making API requests¶
This section details the required HTTP headers, data format, and guidelines for sending authenticated GraphQL requests to the Investigator API. All API calls are sent as a POST request to your regional GraphQL endpoint.
Required headers¶
All requests must include the following headers for authentication and data format:
Authorization: Bearer YOUR_API_KEYAction: Replace YOUR_API_KEY with the actual key copied from the Investigator UI.
Content-Type: application/jsonFormat: Indicates the request payload is in JSON format.
Sample curl request¶
This example sends a simple query to fetch the alert_id and score for the 10 most recent alerts.
curl -X POST https://api.investigator.corelight.com/graphql \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "query { alerts(offset: 0, size: 10) { alerts { alert_id score } } }"
}'
Request and payload limits¶
The API enforces strict rules on the content and size of each HTTP request:
Operation limit: The Investigator API only supports one GraphQL operation (
queryormutation) per request. Batching multiple operations into a single request is not supported and will result in an error.Batching error: Attempting to include more than one API operation in a single request will result in a
403 Forbiddenresponse with a message (for example, “There should be exactly one API call per invocation”).Pagination/Size limit: If an individual response, even when paginated, is too large, you will receive a
403 Forbiddenerror. When this occurs, you must decrease the requested page size (sizeorpage_sizeparameter) to reduce the data payload and retrieve results.
Rate limits¶
To ensure fair usage and API stability, the Investigator API enforces the following rate limits:
Per minute: 50 requests
Per hour: 250 requests
Per day: 5,000 requests
Note
These limits are global and apply across both public APIs (Alerts and Logs). Only successful requests count against the rate limit.
Time window limits¶
To ensure optimal query performance and stability, the Investigator API enforces the following time window limits:
Logs API: The maximum allowed time window for queries is 48 hours.
Note
If you attempt to query a time window larger than this, you will receive a 400 Bad Request error with a message indicating the time window is invalid or outside of allowable bounds.
When rate limits are exceeded¶
Exceeding these limits will result in a 429 Too Many Requests error. The response will typically include a Retry-After header indicating when you can safely retry your request.
Tracking limits¶
For advanced integration and preventative logic, you can monitor your current rate limit status by inspecting the following response headers returned with every API call.
X-RateLimit-Limit: The maximum number of requests allowed for the current time period.X-RateLimit-Remaining: The number of requests remaining in the current time period.X-RateLimit-Reset: The time (in seconds since epoch) when the current rate limit window resets.
Error handling and troubleshooting¶
The API uses standard HTTP status codes to indicate success or failure. For detailed context, GraphQL will also return a top-level errors object in its response, which should be inspected for diagnostic information.
Common HTTP error codes¶
These codes are common for synchronous queries (alerts, detections, metadata) and represent general request failures:
400 Bad Request: Malformed syntax in the request (for example, GraphQL query or JSON payload).401 Unauthorized: Missing or invalid API key.403 Forbidden: Insufficient permissions on the API key.429 Too Many Requests: Rate limit exceeded.
Logs API specific errors¶
For asynchronous log queries (using createLogJob and pollLogJob), you may receive these specific error messages in the response payload:
Invalid time window (over 48 hours):
{ "message": "Time window is either invalid or outside of allowable bounds." }Invalid time window (start > end):
{ "message": "start_ts cannot be larger than end_ts." }Invalid LQL or disallowed function:
{ "message": "Query supplied is not a valid LogScale/Humio syntax, or query contains disallowed function(s). Check the query and try again." }Job creation failure (Internal):
{ "message": "Could not create query job. Please try again later." }Job not found (expired or invalid ID):
{ "message": "Supplied job_id=(your-job-id) does not exist." }Tenant not enabled:
{ "message": "Tenant is not enabled to call this API." } (Indicates the Logs API feature is not enabled for your tenant. Contact your Corelight representative for access ).Payload too large:
{ "message": "Query payload exceeded the maximum allowable size." }
Resolving the RESPONSE_PAYLOAD_TOO_LARGE error¶
The error RESPONSE_PAYLOAD_TOO_LARGE indicates that the data requested for a single page exceeds the system’s internal limits. This usually occurs when the individual records requested are large.
To resolve this issue, use the following two-step approach:
Decrease the page size
Explicitly set a lower value for the page size parameter used in your query.
Primary queries (alerts, detections, metadata): Use the size parameter. The maximum page size is 100 records.
Log queries (
pollLogJob): Use thepage_sizeparameter. The maximum page size is 750 records.Example: If you are querying logs and receive this error, reduce the
page_sizevalue (for example, from 200 to 100).
Streamline the data requested
Reduce the amount of data contained within each record by requesting only the specific fields you need in your GraphQL query. Minimizing the size of each record reduces the total payload size for that page.
Common tools¶
The following tools are commonly used to send GraphQL requests to the Investigator API:
Command line:
curlGUI-based: Postman (use the GraphQL tab)
GraphQL clients: Altair, Insomnia
API reference¶
The API reference provides detailed information on all available queries, the objects they return, and the data structures they are built from.
Investigator API structure¶
The Investigator API is logically structured into two main components:
Alerts, Detections, and Metadata API: Used for synchronous queries, immediately retrieving structured security data.
Logs API: Used for asynchronous operations, retrieving raw log data via a two-step process involving mutations and queries. See the Logs API reference below.
Supported alert types¶
The system supports multiple alert types, which implement the core alert interface. These include:
SuricataAlert: Alerts from the Suricata network security tool.
MlAlert (Machine Learning): Alerts from supervised machine learning models.
CustomSearchRuleAlert: Alerts generated from user-defined search rules.
YaraAlert: Alerts from Yara for malware detection.
AnomalyAlert: Alerts from unsupervised machine learning for anomaly detection.
NoticeAlert: Alerts from Zeek Notices.
Alerts query¶
The alerts query is a synchronous operation used to retrieve a list of security events. An alert is an interface representing a single security event, which is implemented by specific types like SuricataAlert and NoticeAlert.
Parameters¶
The following parameters are available for filtering, paginating, and sorting alert results:
Parameter |
Type |
Description |
|---|---|---|
|
|
An object containing filters for the query, such as by alert info, entities, timestamp, or score. |
|
|
The number of items to skip for pagination. Defaults to 0. |
|
|
The number of items to return per page. Defaults to 10, with a maximum of 100 records. |
|
|
An array of objects to define sorting. Defaults to timestamp descending. |
Request¶
This example retrieves the first five alerts with a score greater than or equal to 5, filtered by a specific alert name, and sorted by score.
query Alerts($alert_filter: AlertFilterInput, $offset: Int, $size: Int, $sort: [SortParameterInput]) {
alerts(alert_filter: $alert_filter, offset: $offset, size: $size, sort: $sort) {
alerts {
alert_id
alert_info { alert_name alert_type content_id }
alert_timestamp { start end observed ttl }
score
false_positive
}
page_info {
offset
size
total_items
sort { sort_by sort_dir }
}
}
}
Variables¶
This object defines the parameters used in the GraphQL query above, allowing for dynamic filtering, pagination, and sorting of alerts.
{
"alert_filter": {
"score": {
"gte": 5
},
"alert_info_list": [
{
"alert_name": "ETPRO JA3 Hash - Possible Ligolo Server/Golang Binary Response"
}
]
},
"offset": 0,
"size": 5,
"sort": [
{
"sort_by": "score",
"sort_dir": "desc"
}
]
}
Response¶
Example response showing a subset of the alert objects to illustrate structure and key fields.
{
"data": {
"alerts": {
"alerts": [
{
"alert_id": "cff115c9-1354-4995-bbaa-c248ecd36b36",
"alert_info": {
"alert_name": "ETPRO JA3 Hash - Possible Ligolo Server/Golang Binary Response",
"alert_type": "suricata_corelight",
"content_id": "SURI-2850023"
},
"alert_timestamp": {
"start": 1742770313,
"end": 1742770313,
"observed": 1742770313,
"ttl": 1750546313
},
"score": 6,
"false_positive": false
}
],
"page_info": {
"offset": 0,
"size": 5,
"total_items": 9999,
"sort": [
{
"sort_by": "score",
"sort_dir": "desc"
}
]
}
}
}
}
Detections query¶
The detections query is a synchronous operation used to retrieve a list of detection objects, which are higher-level correlated security incidents.
Parameters¶
The following parameters are available for filtering, paginating, and sorting detection results:
Parameter |
Type |
Description |
|---|---|---|
|
|
An object containing filters for the query, such as status, severity, timestamps, assignees, or associated alert info. |
|
|
The number of items to skip for pagination. Defaults to 0. |
|
|
The number of items to return per page. Defaults to 10, with a maximum of 100 records. |
|
|
An array of objects to define sorting. |
Request¶
This example retrieves open detections with a severity of 3 or higher, sorted by detection ID in ascending order.
query Detections($detection_filter: DetectionFilterInput, $offset: Int, $size: Int, $sort: [SortParameterInput]) {
detections(detection_filter: $detection_filter, offset: $offset, size: $size, sort: $sort) {
detections {
detection_id
alert_info { alert_name alert_type content_id }
alert_entity { entity_id entity_name entity_type entity_category }
detection_status
detection_timestamp { start end }
rank { severity }
}
page_info {
offset
size
total_items
sort { sort_by sort_dir }
}
}
}
Variables¶
This object specifies the parameters for the detection query, including filters for severity and status, as well as pagination and sorting options.
{
"detection_filter": {
"severity": {
"gte": 3
},
"detection_statuses": [
"open"
]
},
"offset": 0,
"size": 5,
"sort": [
{
"sort_by": "detection_id",
"sort_dir": "asc"
}
]
}
Response¶
Example response showing a subset of the detection objects to illustrate structure and key fields.
{
"data": {
"detections": {
"detections": [
{
"detection_id": "1ca01fd03b64be883b707b39383f1d80",
"alert_info": {
"alert_name": "SSL::Invalid_Server_Cert",
"alert_type": "notice",
"content_id": "SSL::Invalid_Server_Cert"
},
"alert_entity": {
"entity_id": "IP10.2.128.138",
"entity_name": "10.2.128.138",
"entity_type": "IP",
"entity_category": "source"
},
"detection_status": "open",
"detection_timestamp": {
"start": 1749881040,
"end": 9223372036854776000
},
"rank": {
"severity": 4
}
}
],
"page_info": {
"offset": 0,
"size": 5,
"total_items": 7,
"sort": [
{
"sort_by": "detection_id",
"sort_dir": "asc"
}
]
}
}
}
}
AlertMetadataList query¶
The alertMetadataList query is a synchronous operation that retrieves a list of alert metadata definitions. This metadata describes the nature of different alert types, including default severity, description, and MITRE mappings.
Parameters¶
The following parameters are available for filtering, paginating, and sorting Alert Metadata definitions:
Parameter |
Type |
Description |
|---|---|---|
|
|
An object containing filters for the query, including titles, content IDs, status, category, and severity. |
|
|
The number of items to skip for pagination. Defaults to 0. |
|
|
The number of items to return per page. Defaults to 10, with a maximum of 100 records. |
|
|
An array of objects to define sorting. |
Request¶
The following query example retrieves active AlertMetadataList definitions with a severity of 2 or higher, sorted by the most recently updated.
query AlertMetadataList($alert_metadata_filter: AlertMetadataFilterInput, $offset: Int, $size: Int, $sort: [SortParameterInput]) {
alertMetadataList(alert_metadata_filter: $alert_metadata_filter, offset: $offset, size: $size, sort: $sort) {
alert_metadata_list {
id
title
content_id
severity
active
category
updated_timestamp
}
page_info {
offset
size
total_items
sort { sort_by sort_dir }
}
}
}
Variables¶
This object provides the parameters for the AlertMetadataList query, allowing for filtering by active status and severity, alongside pagination and sorting.
{
"alert_metadata_filter": {
"active_statuses": [
true
],
"severity": {
"gte": 2
}
},
"offset": 0,
"size": 5,
"sort": [
{
"sort_by": "updated_timestamp",
"sort_dir": "desc"
}
]
}
Response¶
Example response showing a subset of the metadata objects to illustrate structure and key fields.
{
"data": {
"alertMetadataList": {
"alert_metadata_list": [
{
"id": "847f532b-ad34-4c1f-b40f-e6c2fc260f35",
"title": "MOVEit Authentication bypass SSH",
"content_id": "847f532b-ad34-4c1f-b40f-e6c2fc260f35",
"severity": 10,
"active": true,
"category": null,
"updated_timestamp": 1749578764
}
],
"page_info": {
"offset": 0,
"size": 5,
"total_items": 51016,
"sort": [
{
"sort_by": "updated_timestamp",
"sort_dir": "desc"
}
]
}
}
}
}
Logs API reference¶
The Logs API allows for asynchronous, programmatic extraction of structured log data. Because log retrieval is asynchronous, you must implement a polling loop using the two-step workflow: first, the createLogJob mutation, and second, the pollLogJob query.
createLogJob mutation¶
The createLogJob mutation initiates an asynchronous query for log data. This process creates a job on the server and immediately returns a unique job_id. You must use this job_id with the pollLogJob query to retrieve the log results.
Parameters¶
The createLogJob mutation accepts the following arguments:
Parameter |
Type |
Description |
|---|---|---|
query |
String! |
The valid Logscale Query Language (LQL) string to execute.
Note: Visualization, Join, and Sort functions are restricted.
|
|
|
The start of the time range as a Unix epoch timestamp in seconds. |
|
|
The end of the time range as a Unix epoch timestamp in seconds. |
|
|
Optional. The maximum number of records to return for the entire job (Max 5,000). |
LQL reference¶
The query parameter accepts strings formatted in the LogScale Query Language (LQL).
Basic filters: You can filter by specific fields (for example,
id.orig_h=10.0.0.1) or use free text.Full documentation: For a complete guide on valid LQL syntax, operators, and functions, refer to the Humio/LogScale Query Language Documentation.
Note
While standard LQL is supported, specific heavy-load functions are restricted in the Investigator API to ensure performance. See LQL Restrictions below.
LQL restrictions¶
While standard LQL is supported, specific heavy-load functions are restricted in the Investigator API to ensure performance:
Category |
Disallowed Functions/Restrictions |
|---|---|
Visualization |
|
Joins & Multi-Dataset |
|
Sorting & Uniqueness |
|
Job and row limits¶
The following operational constraints apply to all log query jobs:
Limit type |
Constraint |
|---|---|
Query Timeframe |
Maximum time window between |
Max Total Results |
Maximum total results ( |
Job Lifetime |
A created job ( |
Request¶
This mutation creates a job to find 10 log records for corelight traffic over a 10-minute span.
mutation CreateLogJob ($query: String!, $start_ts: Int!, $end_ts: Int!) {
createLogJob (query: $query, start_ts: $start_ts, end_ts:
$end_ts, result_size: 10) {
job_id
}
}
Variables¶
{
"query": "#path = known_hosts",
"start_ts": 1742770000,
"end_ts": 1742770600
"result_size": 10
}
Response¶
A successful request returns a job_id.
{
"data": {
"createLogJob": {
"job_id": "vp9j8CDgZmG3Zgib_"
}
}
}
pollLogJob query¶
The pollLogJob polls an active log query job to retrieve results. Because log retrieval is asynchronous, you must implement a polling loop to check the job status and paginate through results.
Parameters¶
The pollLogJob query accepts the following parameters to manage polling and pagination of the asynchronous job:
Parameter |
Type |
Description |
|---|---|---|
|
|
The job ID returned from the createLogJob mutation. |
|
|
Optional. The number of events to return in this specific poll. Default 200, Max 750. |
|
|
Optional. The offset for pagination. Default 0. |
Polling strategy and timeout¶
The timing of your polling loop is critical to ensure timely retrieval of results and prevent job expiration.
Polling interval: A polling interval of 1 to 5 seconds is recommended. This frequency ensures timely data retrieval without exhausting your rate limits.
- Idle timeout: Active jobs have an idle timeout of 90 seconds.
The timer resets every time the job is successfully polled.
If you stop polling for more than 90 seconds, the job will be deleted, and subsequent requests will return a
Job Not Founderror.
Pagination and completion logic¶
Correct pagination and completion requires checking both the job status and the data array. The response returns a done boolean and an events array.
Job completion:
done: trueindicates the server has finished searching the database.Data retrieval check: Even if
doneistrue, there may still be more records to fetch if yourpage_sizelimit was reached in the last call.Pagination logic: For details, see the LogScale/Humio Pagination API documentation.
Request¶
This query polls the job created in the previous example to retrieve the first page of results.
query PollLogJob ($job_id: String!, $page_size: Int, $page_offset: Int) {
pollLogJob (job_id: $job_id, page_size: $page_size, page_offset:
$page_offset) {
done
num_of_events
events
pagination {
page_offset
page_size
}
}
}
Variables¶
{
"job_id": "vp9j8CDgZmG3Zgib_",
"page_size": 10,
"page_offset": 0
}
Response¶
The response includes the job status (done), the number of events in the current page (num_of_events), and the log data (events).
{
"data": {
"pollLogJob": {
"job_id": "vp9j8CDgZmG3Zgib_",
"done": true,
"num_of_events": 3,
"events": [
{
"#path": "known_hosts",
"ts": "2024-06-10T07:30:19.326062Z",
"host_ip": "10.2.128.10",
"_system_name": "LABSensor1",
"@timestamp": 1718005584157
},
{
"#path": "known_hosts",
"ts": "2024-06-10T07:29:35.178588Z",
"host_ip": "10.2.128.138",
"_system_name": "LABSensor1",
"@timestamp": 1718005544156
},
{
"#path": "known_hosts",
"ts": "2024-06-10T07:28:51.031114Z",
"host_ip": "
"_system_name": "LABSensor1",
"@timestamp": 1718005424151
}
],
"pagination": {
"page_offset": 0,
"page_size": 10
}
}
}
}
Response type reference¶
This section uses clear tables to show the fields, types, and descriptions for each API object returned in responses.
Top-level objects¶
This table shows the main data returned by the alerts, detections, and alertMetadataList, createLogJob and pollLogJob.
Object |
Attribute |
Type |
Description |
|---|---|---|---|
Alerts |
|
|
An array of alert objects. |
|
|
Pagination information for the result set. |
|
Detections |
|
|
An array of detection objects. |
|
|
Pagination information for the result set. |
|
AlertMetadataList |
|
An array of alert metadata objects. |
|
|
|
Pagination information for the result set. |
|
JobRequestResult |
|
|
The identifier to be used with pollLogJob. |
Logs |
|
|
The job ID these logs are for. |
|
|
|
|
|
|
The number of |
|
|
|
Pagination metadata for the current set of events. |
|
|
|
An array of log events. The structure of each event is a generic JSON object. |
Alert object¶
This table describes the fields returned for the Alert object.
Attribute |
Type |
Description |
|---|---|---|
|
|
The primary entity (for example, IP, Host) associated with the alert. |
|
|
Unique identifier for the alert. A |
|
|
An object containing the alert name, type, and content ID. |
|
|
An object containing start, end, observed, and TTL timestamps. |
|
A list of entities that were the destination in the alert. |
|
|
|
A list of event identifiers associated with the alert. |
|
|
Indicates if the alert has been marked as a false positive. |
|
|
A list of MITRE ATT&CK tactic IDs associated with the alert. |
|
|
A list of MITRE ATT&CK technique IDs associated with the alert. |
|
|
A list of other entities related to this alert. |
|
|
The calculated severity score of the alert. |
|
|
A list of entities that were the source in the alert. |
Detection object¶
This table describes the fields returned for the Detection object.
Attribute |
Type |
Description |
|---|---|---|
|
|
The primary entity associated with the detection. |
|
|
The primary alert information that defines the detection. |
|
|
Information on which user is assigned to this detection. |
|
|
The Unix timestamp when the detection was first created. |
|
|
Unique identifier for the detection. |
|
|
The workflow status of the detection (for examaple, open, closed). |
|
|
The start and end time of the detection window. |
|
|
The earliest start time among all alerts within the detection. |
|
|
Information related to the escalation of the detection. |
|
|
The latest start time among all alerts within the detection. |
|
|
MITRE ATT&CK tactic and technique information for the detection. |
|
|
A list of MITRE ATT&CK tactic IDs associated with the detection. |
|
|
A list of MITRE ATT&CK technique IDs associated with the detection. |
|
|
An object containing the detection’s severity level. |
|
|
The total number of alerts grouped within this detection. |
|
|
Information about when the detection was last updated and by whom. |
AlertMetadata object¶
This table describes the fields returned for the AlertMetadata object.
Attribute |
Type |
Description |
|---|---|---|
active |
Boolean |
Whether this alert rule is currently active and
generating alerts.
|
|
|
Author of the alert definition. |
|
|
The category of the alert (for example, suricata_corelight). |
|
|
An object containing guidance for alert handling. |
|
|
A unique content identifier for the alert type. |
|
|
CVE reference if applicable. |
|
|
Creation date of the metadata. |
|
|
A detailed description of what the alert signifies. |
|
|
Unique identifier for the metadata entry. |
|
|
MITRE ATT&CK tactic and technique information. |
|
|
A list of reference links. |
|
|
The underlying alert rule definition. |
|
|
The default severity level of the alert, from 1-10. |
|
|
An object containing detailed severity settings. |
|
|
The status of the metadata entry. |
|
|
The human-readable name of the alert rule. |
|
|
User who last updated the metadata. |
|
Common and supporting types¶
This table lists shared types and enums used across multiple responses (for example, AlertEntity, PageInfo, DetectionRank, Pagination).
Object |
Type |
Description |
|
|---|---|---|---|
AlertEntity |
|
|
A unique system ID for the entity. |
entity_name |
String |
The value of the entity (for example,
"192.168.1.100"). |
|
|
|
The type of entity (for example, IP, HOST, DOMAIN). |
|
entity_category |
EntityCategory |
Whether the entity was the source or
destination.
|
|
AlertInfo |
|
|
The display name of the alert. |
alert_type |
String |
The source of the alert (for example,
suricata_corelight). |
|
content_id |
String |
A unique content identifier for the alert
type.
|
|
DetectionRank |
|
|
Indicates if a custom severity is being used. |
|
|
The severity level of the detection. |
|
PageInfo |
|
|
The current offset in the result set. |
size |
Int |
The number of items returned in the current
page.
|
|
total_items |
Int |
The total number of items available for the
query.
|
|
|
|
The sorting parameters applied to the query. |
|
Pagination |
|
|
The offset for the current page. |
|
|
The size limit for the current page. |
API schema¶
The Investigator API schema defines the public APIs, offering a set of GraphQL queries to retrieve alerts, detections, and their associated metadata, as well as mutations and queries to retrieve raw log data. It provides flexible data retrieval through advanced filtering, pagination, and sorting for a wide range of security analysis use cases.
Supported alert types¶
The system supports multiple alert types including:
Suricata alerts: Suricata network security alerts.
ML-based alerts: Alerts generated by supervised machine learning models.
Custom search rule alerts: Alerts from custom search rules.
Yara alerts: Malware detection alerts using Yara rules.
Anomaly alerts: Alerts from unsupervised anomaly detection systems.
Notice alerts: Alerts from Zeek notices.
Root queries¶
This section defines the top-level GraphQL queries available in the Investigator API, serving as the entry points for data retrieval.
"""
Root mutation type containing all available mutations. All mutations require API key authentication.
"""
type Mutation {
"""
Create a query job by passing in query, and time horizon (epoch timestamps in seconds)
Args:
query: Valid LQL query string. (Note: Visualization, Join, and Sort functions are restricted).
start_ts: Start of time range (Unix epoch seconds)
end_ts: End of time range (Unix epoch seconds). Max range is 48 hours.
result_size: Optional limit for total records. Max 5,000. (Applied via tail() if not specified in query).
Returns:
JobRequestResult object containing the new job_id
"""
createLogJob(query: String!, start_ts: Int!, end_ts: Int!, result_size: Int): JobRequestResult @api_key
}
"""
Root query type containing all available queries
All queries require API key authentication
"""
type Query {
"""
Retrieve alerts with optional filtering, pagination, and sorting
Args:
alert_filter: Optional filter criteria for alerts
offset: Number of items to skip (for pagination)
size: Number of items to return (for pagination). Default 10, Max 100.
sort: Array of sort parameters
Returns:
Alerts object containing alert list and pagination info
"""
alerts(alert_filter: AlertFilterInput, offset: Int, size: Int, sort: [SortParameterInput]): Alerts @api_key
"""
Retrieve detections with optional filtering, pagination, and sorting
Args:
detection_filter: Optional filter criteria for detections
offset: Number of items to skip (for pagination)
size: Number of items to return (for pagination). Default 10, Max 100.
sort: Array of sort parameters
Returns:
Detections object containing detection list and pagination info
"""
detections(detection_filter: DetectionFilterInput, offset: Int, size: Int, sort: [SortParameterInput]): Detections @api_key
"""
Retrieve alert metadata with optional filtering, pagination, and sorting
Args:
alert_metadata_filter: Optional filter criteria for alert metadata
offset: Number of items to skip (for pagination)
size: Number of items to return (for pagination). Default 10, Max 100.
sort: Array of sort parameters
Returns:
AlertMetadataList object containing metadata list and pagination info
"""
alertMetadataList(alert_metadata_filter: AlertMetadataFilterInput, offset: Int, size: Int, sort: [SortParameterInput]): AlertMetadataList @api_key
"""
Query for a particular job id, with optional pagination
Args:
job_id: The ID from createLogJob. (Jobs expire after 90 seconds of inactivity).
page_size: Number of items to return (for pagination). Default 200, Max 5,000.
page_offset: Number of items to skip (for pagination)
Returns:
Logs object containing job status and log events
"""
pollLogJob(job_id: String!, page_size: Int, page_offset: Int): Logs @api_key
}
Common use cases and workflows¶
This section provides examples of how the Investigator API can integrate into common security workflows.
Custom triage with dashboards or notifications¶
Goal: Build a custom dashboard or notification system to highlight the most critical threats first.
Target Audience: Detection Engineer, Security Operations Lead.
Workflow:
Regularly query the
detectionsendpoint, sorting the results byrank.severityin descending order ("sort_dir": "desc").In your application, you can apply additional business logic. For example, you could increase the priority of a high-severity detection if the
alert_entityinvolved is tagged as a high-value asset in your environment.Send a high-priority notification (for example, to Slack or PagerDuty) with key details and a link back to the detection in the Investigator UI.
SOAR integration for case management¶
Goal: Automatically create and update cases in a SOAR platform like Splunk SOAR, Palo Alto XSOAR, or Torq.
Target Audience: SOAR Engineer, Automation Specialist.
Workflow:
Periodically poll the
detectionsquery for new items wheredetection_statusisopen. You can use theearliest_start_timestampfilter to avoid re-fetching old data.For each new detection, use its
detection_id,alert_info,alert_entity, andrank.severityto create a new case in your SOAR.Store the mapping between the
detection_idand your SOAR case ID. This allows you to synchronize status updates or add new alerts to existing cases later.
SIEM data enrichment¶
Goal: Add deeper context to events in your SIEM (for example, Splunk, Elastic, Microsoft Sentinel).
Target Audience: Security Analyst, SIEM Engineer.
Workflow:
When a notable event involving a specific IP address or hostname appears in your SIEM, trigger an enrichment script.
The script calls the
alertsquery, using thealert_entity_listfilter to find all other Investigator alerts involving that same entity.The results can be added back to the original SIEM event as comments or appended data, giving your analysts immediate access to related Corelight findings without leaving their primary tool.
Log analysis for triage¶
This scenario demonstrates how to use the Logs API to efficiently gather a small, highly relevant set of logs for immediate context without performing a deep-dive analysis that retrieves thousands of records.
Goal: Confirm a single, suspicious network activity event (for example, DNS query, flow record) within a tight time window.
Target audience: Security Analyst, Incident Responder.
Workflow:
Identify specific event: The analyst identifies a suspicious entity (for example, an IP address) and the precise time the activity occurred, narrowing the search to a time window of just a few minutes.
Create scoped log job: The analyst (or an automated script) triggers the
createLogJobmutation, making specific choices to limit volume:Time window: Must include the required
start_tsandend_tsparameters (the time span cannot be more than 48 hours).LQL query: Use multiple filters (for example,
id.orig_h=10.2.128.138 AND _path=dns) to pinpoint the record type and entity.Result size: Set the optional
result_sizeto a low number (for example, 10 or 25) to ensure the API stops searching after finding only a few matching records.
Initiate quick polling: The API returns a
job_id. The script immediately begins calling thepollLogJobquery using thisjob_id.Complete retrieval: The polling loop runs until the server indicates the job is finished by checking the
donefield (done: true). Since the requestedresult_sizeis small (for example, 10-25), all matching records are typically returned in the initial response and further pagination is generally not required.Immediate use: The highly relevant, small set of logs is available for integration into the incident case for quick context.
End-to-end API quickstart¶
This end-to-end API quickstart guide uses a provided Python script to walk you through making simultaneous API calls to alerts, detections, alert metadata, and the full logs API workflow.
Prerequisites¶
To run this end-to-end example, ensure you have the following readily available:
Python 3.x: Python must be installed on your system.
Python requests library: This is required for sending HTTP/GraphQL requests.
A generated API key: Your key must be active and have Alerts, Detections, and Logs read-only permissions enabled.
The API endpoint: The regional GraphQL URL corresponding to your service.
Step 1: Get your API key¶
Before you begin, you must have an API key. Follow the instructions in the Authentication and API Key Management to create and securely store your key.
Step 2: Save the script¶
The following Python script queries for alerts, detections, and alert metadata. Copy the following code and save it in a file named quickstart.py.
import requests
import json
import time
# ----------------------------------------------------------------------
# CONFIGURATION
# ----------------------------------------------------------------------
GRAPHQL_ENDPOINT = "https://api.investigator.corelight.com/graphql"
# IMPORTANT: Replace "your-api-key" with your actual key from the Investigator UI
API_KEY = "your-api-key"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}
# ----------------------------------------------------------------------
# HELPER FUNCTIONS
# ----------------------------------------------------------------------
def run_query(query, variables=None, query_name=""):
"""Sends a GraphQL query and returns the JSON response."""
print(f"--- Running Query: {query_name} ---")
payload = {"query": query}
if variables:
payload["variables"] = variables
try:
# Basic request with timeout
response = requests.post(GRAPHQL_ENDPOINT, headers=headers, json=payload, timeout=30)
response.raise_for_status()
data = response.json()
# Check for GraphQL errors
if "errors" in data:
print(f"GraphQL Error in {query_name}: {data['errors'][0]['message']}")
return None
# Print success structure
print(json.dumps(data, indent=2))
return data
except requests.exceptions.HTTPError as http_err:
print(f"HTTP error occurred: {http_err}")
print(f"Response Text: {response.text}")
except Exception as err:
print(f"An error occurred: {err}")
finally:
print("-" * (len(query_name) + 20) + "\n")
def retrieve_logs(job_id):
"""
Polls the log job until completion, handling pagination automatically.
"""
print(f"--- Polling Log Job ({job_id}) ---")
poll_query = """
query PollLogJob($job_id: String!, $page_size: Int, $page_offset: Int) {
pollLogJob(job_id: $job_id, page_size: $page_size, page_offset: $page_offset) {
job_id
done
num_of_events
events
pagination {
page_offset
page_size
}
}
}
"""
page_size = 200 # Records per page
page_offset = 0
total_events_fetched = 0
# Polling Loop
while True:
variables = {
"job_id": job_id,
"page_size": page_size,
"page_offset": page_offset
}
try:
response = requests.post(
GRAPHQL_ENDPOINT,
headers=headers,
json={"query": poll_query, "variables": variables},
timeout=30
)
response.raise_for_status()
data = response.json()
if "errors" in data:
print(f"Error polling job: {data['errors'][0]['message']}")
break
result = data.get("data", {}).get("pollLogJob", {})
is_done = result.get("done", False)
events = result.get("events", [])
# Process Events
if events:
count = len(events)
total_events_fetched += count
print(f" > Retrieved {count} events (Total: {total_events_fetched})")
# Example: Print the first event of this batch
if page_offset == 0:
print(" > Sample Event:")
print(json.dumps(events[0], indent=2))
# Increment offset for next page
page_offset += count
# EXIT CONDITION: Job is done AND we have drained the event queue
if is_done and not events:
print(f"--- Log Query Complete. Total Events: {total_events_fetched} ---\n")
break
# WAIT: Respect rate limits (sleep 2 seconds between polls)
time.sleep(2)
except Exception as e:
print(f"Exception during polling: {e}")
break
# ----------------------------------------------------------------------
# 1. QUERY: ALERTS
# ----------------------------------------------------------------------
alerts_query = """
query Alerts($alert_filter: AlertFilterInput, $offset: Int, $size: Int, $sort: [SortParameterInput]) {
alerts(alert_filter: $alert_filter, offset: $offset, size: $size, sort: $sort) {
alerts {
alert_id
alert_info { alert_name alert_type content_id }
alert_timestamp { start end observed ttl }
score
false_positive
}
page_info {
offset
size
total_items
}
}
}
"""
alerts_variables = {
"alert_filter": {
"score": {"gte": 5},
# Example filter: "alert_info_list": [{"alert_name": "Suspicious Login"}]
},
"offset": 0,
"size": 5,
"sort": [{"sort_by": "score", "sort_dir": "desc"}]
}
run_query(alerts_query, alerts_variables, query_name="Alerts")
# ----------------------------------------------------------------------
# 2. QUERY: DETECTIONS
# ----------------------------------------------------------------------
detections_query = """
query Detections($detection_filter: DetectionFilterInput, $offset: Int, $size: Int, $sort: [SortParameterInput]) {
detections(detection_filter: $detection_filter, offset: $offset, size: $size, sort: $sort) {
detections {
detection_id
alert_info { alert_name alert_type content_id }
alert_entity { entity_id entity_name entity_type entity_category }
detection_status
detection_timestamp { start end }
rank { severity }
}
page_info {
offset
size
total_items
}
}
}
"""
detections_variables = {
"detection_filter": {
"severity": {"gte": 3},
"detection_statuses": ["open"]
},
"offset": 0,
"size": 5,
"sort": [{"sort_by": "detection_id", "sort_dir": "asc"}]
}
run_query(detections_query, detections_variables, query_name="Detections")
# ----------------------------------------------------------------------
# 3. QUERY: ALERT METADATA
# ----------------------------------------------------------------------
metadata_query = """
query AlertMetadataList($alert_metadata_filter: AlertMetadataFilterInput, $offset: Int, $size: Int, $sort: [SortParameterInput]) {
alertMetadataList(alert_metadata_filter: $alert_metadata_filter, offset: $offset, size: $size, sort: $sort) {
alert_metadata_list {
id
title
severity
active
category
updated_timestamp
}
page_info {
offset
size
total_items
}
}
}
"""
metadata_variables = {
"alert_metadata_filter": {
"active_statuses": [True],
"severity": {"gte": 2}
},
"offset": 0,
"size": 5,
"sort": [{"sort_by": "updated_timestamp", "sort_dir": "desc"}]
}
run_query(metadata_query, metadata_variables, query_name="Alert Metadata")
# ----------------------------------------------------------------------
# 4. MUTATION: CREATE LOG JOB
# ----------------------------------------------------------------------
# Calculate time window: 10 minutes of data from 1 hour ago
end_time = int(time.time()) - 3600
start_time = end_time - 600
create_job_query = """
mutation CreateLogJob($query: String!, $start_ts: Int!, $end_ts: Int!, $result_size: Int) {
createLogJob(query: $query, start_ts: $start_ts, end_ts: $end_ts, result_size: $result_size) {
job_id
}
}
"""
# Example Query: Corelight traffic on port 443
create_job_vars = {
"query": "#path = corelight | id.resp_p = 443",
"start_ts": start_time,
"end_ts": end_time,
"result_size": 50
}
print("--- Creating Log Job ---")
job_response = run_query(create_job_query, create_job_vars, query_name="Create Log Job")
# ----------------------------------------------------------------------
# 5. QUERY: POLL LOG JOB
# ----------------------------------------------------------------------
# Extract the job_id from the previous response to start polling
if job_response:
job_id = job_response.get("data", {}).get("createLogJob", {}).get("job_id")
if job_id:
retrieve_logs(job_id)
else:
print("Failed to retrieve job_id. Skipping polling.")
Step 3: Configure your API key¶
Open quickstart.py in an editor and replace the placeholder your-api-key with the actual key you generated.
Step 4: Run the script¶
Open your terminal, navigate to the directory where you saved the file, and run:
python quickstart.py
Step 5: Review the results¶
The script will print five distinct output blocks to your console, confirming successful connectivity and authentication across all API operations: Alerts, Detections, Alert Metadata, and the full Logs API workflow. You can now adapt this script for your own use cases.