Alerts & Errors
The Panther API supports the following alerting operations:
- Listing your alerts and errors with optional filters
- Fetching the details of a particular alert
- Updating the status of one or more alerts
- Adding a comment to an alert
- Assigning and un-assigning a user to one or more alerts, by providing:
- A user's ID
- A user's email address
You can invoke Panther's API by using your Console's API Playground, or the GraphQL-over-HTTP API. Learn more about these methods on Panther API.
See the sections below for GraphQL queries, mutations, and end-to-end workflow examples around core alert and error operations.
Below are some of the most common GraphQL alert and error operations in Panther. These examples demonstrate the documents you have to send using a GraphQL client (or
curl
) to make a call to Panther's GraphQL API. First page of all alerts
Subsequent page of alerts
Filtered page of alerts
# `FirstPageOfAllAlerts` is a nickname for the operation
query FirstPageOfAllAlerts {
alerts(input: {
createdAtAfter: "2022-01-01T00:00:00.000Z",
createdAtBefore: "2023-01-01T00:00:00.000Z"
}) {
edges {
node { # you can ask for more alert-related fields here
id
title
severity
status
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
# `AnotherPageOfAllAlerts` is a nickname for the operation
query AnotherPageOfAllAlerts {
alerts(input: {
createdAtAfter: "2022-01-01T00:00:00.000Z",
createdAtBefore: "2023-01-01T00:00:00.000Z",
cursor: "{\"creationTime\":{\"B\":null,\"BOOL\":null,\"BS\":null,\"L\":null,\"M\":null,\"N\":null,\"NS\":null,\"NULL\":null,\"S\":\"2022-08-11T21:16:12Z\",\"SS\":null},\"id\":{\"B\":null,\"BOOL\":null,\"BS\":null,\"L\":null,\"M\":null,\"N\":null,\"NS\":null,\"NULL\":null,\"S\":\"6472742357f3488aeb3f48a9c5da9262\",\"SS\":null},\"alertMonthPartition\":{\"B\":null,\"BOOL\":null,\"BS\":null,\"L\":null,\"M\":null,\"N\":null,\"NS\":null,\"NULL\":null,\"S\":\"202208\",\"SS\":null}}"
}) {
edges {
node { # you can ask for more alert-related fields here
id
title
severity
status
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
# `FilteredPageOfAlerts` is a nickname for the operation
query FilteredPageOfAlerts {
alerts(input: {
createdAtAfter: "2022-01-01T00:00:00.000Z",
createdAtBefore: "2023-01-01T00:00:00.000Z",
severities: [LOW],
statuses: [OPEN]
}) {
edges {
node { # you can ask for more alert-related fields here
id
title
severity
status
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
query AlertDetails {
alert(id: "FAKE_ALERT_ID") {
id
title
severity
status
}
}
query AlertDetails {
alert(id: "FAKE_ALERT_ID") {
id
title
severity
origin {
... on Detection {
id
name
}
... on SystemError {
relatedComponent
type
}
}
}
}
mutation UpdateAlertStatus {
updateAlertStatusById(
input: {
ids: ["FAKE_ALERT_ID_1","FAKE_ALERT_ID_2"],
status: CLOSED # notice how this isn't a string when hardcoded (it's an `enum`)
}
) {
alerts {
id
status
}
}
}
mutation CreateAlertComment {
createAlertComment(
input: {
alertId: "FAKE_ALERT_ID",
body: "<p>This is HTML</p>",
format: HTML # can also be PLAIN_TEXT if you want HTML parsing disabled
}
) {
comment {
id
}
}
mutation UpdateAlertsAssigneeById {
updateAlertsAssigneeById(
input: {
assigneeId: "FAKE_USER_ID",
ids: ["FAKE_ALERT_ID_1","FAKE_ALERT_ID_2"]
}
) {
alerts {
assignee {
email
givenName
familyName
id
}
}
}
}
mutation UpdateAlertsAssigneeByEmail {
updateAlertsAssigneeByEmail(
input: {
assigneeEmail: "[email protected]",
ids: ["FAKE_ALERT_ID_1","FAKE_ALERT_ID_2"]
}
) {
alerts {
assignee {
email
givenName
familyName
id
}
}
}
}
NodeJS
Python
// npm install graphql graphql-request
import { GraphQLClient, gql } from 'graphql-request';
const client = new GraphQLClient(
'YOUR_PANTHER_API_URL',
{ headers: { 'X-API-Key': 'YOUR_API_KEY' }
});
// `FindAlerts` is a nickname for the query. You can fully omit it.
const findAlerts = gql`
query FindAlerts($input: AlertsInput!) {
alerts(input: $input) {
edges {
node {
id
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
`;
// `UpdateAlerts` is a nickname for the query. You can fully omit it.
const updateAlerts = gql`
mutation UpdateAlerts($input: UpdateAlertStatusByIdInput!) {
updateAlertStatusById(input: $input) {
alerts {
id
}
}
}
`;
(async () => {
try {
// an accumulator that holds all alerts that we fetch all pages
let allAlerts = [];
// a helper to know when to exit the loop
let hasMore = true;
// the pagination cursor
let cursor = null;
// Keep fetching pages until there are no more left
do {
const queryData = await client.request(findAlerts, {
input: {
severities: ["LOW"],
createdAtBefore: "2022-03-01T00:00:00.000Z",
createdAtAfter: "2022-02-01T00:00:00.000Z",
cursor
}
});
allAlerts = [
...allAlerts,
...queryData.alerts.edges.map((edge) => edge.node)
];
hasMore = queryData.alerts.pageInfo.hasNextPage;
cursor = queryData.alerts.pageInfo.endCursor;
} while (hasMore);
const mutationData = await client.request(updateAlerts, {
input: {
ids: allAlerts.map((alert) => alert.id),
status: "RESOLVED"
}
});
console.log(
`Resolved ${mutationData.updateAlertStatusById.alerts.length} alert(s)!`
);
} catch (err) {
console.error(err.response);
}
})();
# pip install gql aiohttp
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport
transport = AIOHTTPTransport(
url="YOUR_PANTHER_API_URL",
headers={"X-API-Key": "YOUR_API_KEY"}
)
client = Client(transport=transport, fetch_schema_from_transport=True)
# `FindAlerts` is a nickname for the query. You can fully omit it.
find_alerts = gql(
"""
query FindAlerts($input: AlertsInput!) {
alerts(input: $input) {
edges {
node {
id
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
"""
)
# `UpdateAlerts` is a nickname for the query. You can fully omit it.
update_alerts = gql(
"""
mutation UpdateAlerts($input: UpdateAlertStatusByIdInput!) {
updateAlertStatusById(input: $input) {
alerts {
id
}
}
}
"""
)
# an accumulator that holds all alerts that we fetch all pages
all_alerts = []
# a helper to know when to exit the loop
has_more = True
# the pagination cursor
cursor = None
# Keep fetching pages until there are no more left
while has_more:
query_data = client.execute(
find_alerts,
variable_values={
"input": {
"severities": ["LOW"],
"createdAtBefore": "2022-03-01T00:00:00.000Z",
"createdAtAfter": "2022-02-01T00:00:00.000Z",
"cursor": cursor
}
}
)
all_alerts.extend([edge["node"] for edge in query_data["alerts"]["edges"]])
has_more = query_data["alerts"]["pageInfo"]["hasNextPage"]
cursor = query_data["alerts"]["pageInfo"]["endCursor"]
mutation_data = client.execute(
update_alerts,
variable_values={
"input": {
"ids": [alert["id"] for alert in all_alerts],
"status": "RESOLVED"
}
}
)
print(f'Resolved {len(mutation_data["updateAlertStatusById"]["alerts"])} alert(s)!')
Last modified 13d ago