Skip to main content

Alerts API

Create alert rules to get notified when traffic anomalies occur.

Overview

The Alerts API allows you to:

  • Create rules for traffic anomaly detection
  • Configure notifications (email, Slack, webhook)
  • View alert history and statistics
  • Acknowledge and manage triggered alerts

Base path: /alerts

Scopes and API keys

Read endpoints (GET /alerts/rules, /alerts/history, /alerts/stats) require the read scope. Endpoints that create, update, delete, test, or acknowledge alerts require the write scope. API keys are read-only, so those write operations must be performed with a user session that holds the write scope — an API key alone cannot create or modify alert rules.


Alert Rules

List Alert Rules

GET /alerts/rules?account_id={account_id}

Query Parameters:

ParameterTypeDescription
account_idstringRequired. Account ID
include_inactivebooleanInclude disabled rules

Response:

{
"rules": [
{
"id": 1,
"account_id": "acme",
"name": "Traffic Drop Alert",
"description": "Alert when traffic drops significantly",
"alert_type": "traffic_drop",
"metric": "entrances",
"condition_type": "percentage_change",
"threshold_value": 25,
"threshold_direction": "decrease",
"baseline_period": "7d",
"comparison_window": "1h",
"custom_condition": null,
"notify_email": true,
"notify_slack": false,
"notify_webhook": true,
"email_recipients": ["alerts@company.com"],
"slack_webhook_url": null,
"custom_webhook_url": "https://hooks.example.com/abc",
"cooldown_minutes": 60,
"last_triggered_at": "2025-01-05T10:30:00Z",
"is_active": true,
"trigger_count": 4,
"created_by": 12,
"created_at": "2024-12-01T10:00:00Z",
"updated_at": null
}
],
"total": 1
}

Create Alert Rule

POST /alerts/rules?account_id={account_id}

Request Body:

{
"name": "Conversions Drop",
"description": "Alert when conversions drop more than 30%",
"alert_type": "conversion_change",
"metric": "conversions",
"condition_type": "percentage_change",
"threshold_value": 30,
"threshold_direction": "decrease",
"baseline_period": "7d",
"comparison_window": "1h",
"notify_email": true,
"notify_slack": false,
"notify_webhook": false,
"email_recipients": ["alerts@company.com"],
"cooldown_minutes": 60
}
FieldTypeRequiredDefaultDescription
namestringYesRule name (1-255 chars)
descriptionstringNonullRule description (max 1000 chars)
alert_typeenumNotraffic_spikeType of alert
metricenumNopageviewsMetric to monitor
condition_typeenumNopercentage_changeHow to detect the anomaly
threshold_valuenumberNo50.0Threshold value, e.g. 50 for 50% change (0–10000)
threshold_directionenumNobothincrease, decrease, or both
baseline_periodenumNo7dPeriod for baseline calculation
comparison_windowenumNo1hWindow for the current value
custom_conditionobjectNonullCustom condition config
notify_emailbooleanNotrueSend email notifications
notify_slackbooleanNofalseSend Slack notifications
notify_webhookbooleanNofalseSend webhook notifications
email_recipientsstring[]No[]Email addresses (max 10)
slack_webhook_urlstringNonullSlack webhook URL (max 500 chars)
custom_webhook_urlstringNonullCustom webhook URL (max 500 chars)
cooldown_minutesintNo60Minutes between alerts (5–1440)

Alert Types

TypeDescription
traffic_spikeTraffic spike
traffic_dropTraffic drop
conversion_changeConversion change
error_rateError rate
source_changeSource change
goal_reachedGoal reached
customCustom

Available Metrics

MetricDescription
pageviewsTotal pageviews
sessionsSessions
visitorsUnique visitors
entrancesSession starts
bounce_rateBounce rate percentage
conversionsConversion count
conversion_rateConversion rate
customCustom metric

Condition Types

ConditionDescription
percentage_changePercentage change vs baseline
absolute_thresholdAbsolute threshold value
std_deviationStandard deviation from baseline
rate_of_changeRate of change

Threshold Directions

DirectionDescription
increaseAlert on increases only
decreaseAlert on decreases only
bothAlert on either direction

Baseline Periods

PeriodDescription
24hLast 24 hours
7dLast 7 days
30dLast 30 days
90dLast 90 days

Comparison Windows

WindowDescription
15mLast 15 minutes
1hLast hour
6hLast 6 hours
24hLast 24 hours

Response (201 Created):

{
"id": 2,
"account_id": "acme",
"name": "Conversions Drop",
"description": "Alert when conversions drop more than 30%",
"alert_type": "conversion_change",
"metric": "conversions",
"condition_type": "percentage_change",
"threshold_value": 30,
"threshold_direction": "decrease",
"baseline_period": "7d",
"comparison_window": "1h",
"custom_condition": null,
"notify_email": true,
"notify_slack": false,
"notify_webhook": false,
"email_recipients": ["alerts@company.com"],
"slack_webhook_url": null,
"custom_webhook_url": null,
"cooldown_minutes": 60,
"last_triggered_at": null,
"is_active": true,
"trigger_count": 0,
"created_by": 12,
"created_at": "2025-01-10T14:30:00Z",
"updated_at": null
}

Get Alert Rule

GET /alerts/rules/{rule_id}?account_id={account_id}

Update Alert Rule

PATCH /alerts/rules/{rule_id}?account_id={account_id}

Request Body:

{
"name": "Updated Name",
"threshold_value": 40,
"is_active": false
}

All fields are optional. Only provided fields are updated. In addition to the creation fields, the update accepts is_active (boolean) to enable or disable the rule.


Delete Alert Rule

DELETE /alerts/rules/{rule_id}?account_id={account_id}

Returns 204 No Content on success.


Test Alert

Test Alert Rule

POST /alerts/rules/{rule_id}/test?account_id={account_id}

Test a rule without triggering actual notifications.

Request Body (optional):

{
"send_notification": true
}

Set send_notification: true to send a real test notification.

Response:

{
"success": true,
"current_value": 650,
"baseline_value": 1000,
"would_trigger": true,
"change_percentage": -35.0,
"notification_sent": false,
"message": "Rule would trigger: entrances decreased 35% vs baseline"
}

Alert History

Get Alert History

GET /alerts/history?account_id={account_id}

Query Parameters:

ParameterTypeDescription
account_idstringRequired. Account ID
rule_idintFilter by specific rule
statusstringFilter by status
limitintMax results (default: 50, range 1–200)
offsetintPagination offset

Alert Statuses

StatusDescription
activeAlert fired, awaiting acknowledgment
acknowledgedUser acknowledged the alert
resolvedAlert condition no longer active
false_positiveAlert was a false positive

Response:

{
"alerts": [
{
"id": 123,
"rule_id": 1,
"rule_name": "Traffic Drop Alert",
"account_id": "acme",
"alert_type": "traffic_drop",
"metric": "entrances",
"current_value": 650,
"baseline_value": 1000,
"change_percentage": -35.0,
"triggered_at": "2025-01-10T10:30:00Z",
"period_start": "2025-01-10T09:30:00Z",
"period_end": "2025-01-10T10:30:00Z",
"context": null,
"notification_sent": true,
"notification_channels": ["email"],
"notification_error": null,
"acknowledged_at": null,
"acknowledged_by": null,
"notes": null,
"status": "active"
}
],
"total": 45
}

Acknowledge Alert

PATCH /alerts/history/{alert_id}?account_id={account_id}

Request Body:

{
"status": "acknowledged",
"notes": "Investigating the traffic drop"
}
FieldTypeDefaultDescription
statusenumacknowledgedacknowledged, resolved, or false_positive
notesstringnullOptional notes about the action taken (max 1000 chars)

Response: the full alert history entry with updated status, for example:

{
"id": 123,
"rule_id": 1,
"rule_name": "Traffic Drop Alert",
"account_id": "acme",
"status": "acknowledged",
"acknowledged_at": "2025-01-10T14:30:00Z",
"acknowledged_by": 12,
"notes": "Investigating the traffic drop"
}

Statistics

Get Alert Statistics

GET /alerts/stats?account_id={account_id}

Response:

{
"active_rules": 4,
"total_triggers_24h": 3,
"total_triggers_7d": 12,
"total_triggers_30d": 45,
"unacknowledged_alerts": 3,
"most_triggered_rule": "Traffic Drop Alert",
"last_trigger": "2025-01-10T10:30:00Z"
}
FieldTypeDescription
active_rulesintNumber of active rules
total_triggers_24hintTriggers in the last 24 hours
total_triggers_7dintTriggers in the last 7 days
total_triggers_30dintTriggers in the last 30 days
unacknowledged_alertsintAlerts still awaiting acknowledgment
most_triggered_rulestringName of the most-triggered rule
last_triggerdatetimeTimestamp of the most recent trigger

Code Examples

Python - Create and Monitor Alerts

import requests

API_KEY = "sm_your_api_key"
BASE_URL = "https://my.sealmetrics.com/api/v1"
ACCOUNT_ID = "my-account"

def create_alert_rule(name: str, metric: str, threshold: int) -> dict:
"""Create a new alert rule."""
response = requests.post(
f"{BASE_URL}/alerts/rules",
headers={"X-API-Key": API_KEY},
params={"account_id": ACCOUNT_ID},
json={
"name": name,
"metric": metric,
"condition_type": "percentage_change",
"threshold_value": threshold,
"threshold_direction": "decrease",
"baseline_period": "7d",
"comparison_window": "1h",
"notify_email": True,
"email_recipients": ["alerts@company.com"]
}
)
response.raise_for_status()
return response.json()

def get_active_alerts() -> list:
"""Get all active (unacknowledged) alerts."""
response = requests.get(
f"{BASE_URL}/alerts/history",
headers={"X-API-Key": API_KEY},
params={
"account_id": ACCOUNT_ID,
"status": "active"
}
)
response.raise_for_status()
return response.json()["alerts"]

def acknowledge_alert(alert_id: int, notes: str):
"""Acknowledge an alert."""
response = requests.patch(
f"{BASE_URL}/alerts/history/{alert_id}",
headers={"X-API-Key": API_KEY},
params={"account_id": ACCOUNT_ID},
json={
"status": "acknowledged",
"notes": notes
}
)
response.raise_for_status()

# Usage
rule = create_alert_rule("Traffic Alert", "entrances", 25)
print(f"Created rule: {rule['id']}")

alerts = get_active_alerts()
for alert in alerts:
print(f"Alert: {alert['rule_name']} - {alert['change_percentage']}%")

JavaScript - React Hook for Alerts

import { useState, useEffect } from 'react';

function useAlerts(accountId) {
const [alerts, setAlerts] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
async function fetchAlerts() {
const response = await fetch(
`${BASE_URL}/alerts/history?account_id=${accountId}&status=active`,
{ headers: { 'X-API-Key': API_KEY } }
);
const data = await response.json();
setAlerts(data.alerts);
setLoading(false);
}

fetchAlerts();
const interval = setInterval(fetchAlerts, 60000); // Poll every minute

return () => clearInterval(interval);
}, [accountId]);

const acknowledgeAlert = async (alertId, notes) => {
await fetch(
`${BASE_URL}/alerts/history/${alertId}?account_id=${accountId}`,
{
method: 'PATCH',
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ status: 'acknowledged', notes })
}
);
// Refresh alerts
setAlerts(alerts.filter(a => a.id !== alertId));
};

return { alerts, loading, acknowledgeAlert };
}

Best Practices

1. Start with Conservative Thresholds

Begin with higher thresholds to avoid alert fatigue:

{
"threshold_value": 40
}

Adjust lower as you understand normal traffic patterns.

2. Use Cooldowns to Avoid Alert Fatigue

Set a cooldown_minutes window so a noisy rule doesn't fire repeatedly:

{
"name": "Paid Traffic Drop",
"cooldown_minutes": 120
}

3. Combine with Slack and Webhooks

Integrate with Slack or other tools by enabling the relevant notification flags and providing the webhook URLs:

{
"notify_slack": true,
"slack_webhook_url": "https://hooks.slack.com/services/...",
"notify_webhook": true,
"custom_webhook_url": "https://hooks.example.com/alerts"
}

4. Review and Resolve Alerts

Don't let alerts pile up:

# Auto-resolve old active alerts
old_alerts = get_alerts(status="active", older_than_days=7)
for alert in old_alerts:
acknowledge_alert(alert["id"], "Auto-resolved after 7 days")