Alerts, Deliveries & Webhooks
Test alerts, inspect delivery attempts, retry failures, and verify webhook signatures
When a monitor detects a change, it delivers an alert to the channels you configured (alertWebhookUrl and/or alertEmails). Every attempt is logged as a delivery.
Webhook payload
Anakin POSTs this JSON body to your alertWebhookUrl on each detected change:
{
"type": "monitor.change",
"monitorId": "b1e7c2a4-…",
"url": "https://example.com/product/123",
"watchMode": "specific_data",
"changeId": "c_9a1…",
"changedAt": "2026-01-01T12:00:00Z",
"changedFields": ["price"],
"summary": "The price dropped from $19.99 to $14.99.",
"diff": { "before": { "price": 19.99 }, "after": { "price": 14.99 } }
}Each request carries these headers:
| Header | Description |
|---|---|
X-Anakin-Signature | sha256= + hex HMAC-SHA256 of the raw request body, keyed with the monitor's alertWebhookSecret. |
X-Anakin-Timestamp | Unix seconds when the request was signed. |
X-Anakin-Delivery-Id | The delivery ID (for idempotency / retry correlation). |
Verify the signature by recomputing the HMAC over the exact raw body and comparing (constant-time) to X-Anakin-Signature:
import hmac, hashlib
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header)For the full webhook integration guide (retries, delivery semantics), see Website Monitoring Webhooks.
https://api.anakin.io/v1/monitors/{id}/test-alertSend a sample alert to the monitor's configured channels so you can verify your endpoint and signature handling before a real change fires.
Response
200 OK{ "success": true, "status": 200 }status is the HTTP status your webhook returned (or the email send result). Errors: 400 invalid_request if no alert channel is configured; 502 if delivery failed (the body includes status and a message).
https://api.anakin.io/v1/monitors/{id}/deliveriesList recent alert-delivery attempts, newest first (up to 200).
Response
200 OK{
"deliveries": [
{
"id": "d_5f2…",
"monitorId": "b1e7c2a4-…",
"changeId": "c_9a1…",
"event": "change",
"channel": "webhook",
"url": "https://your-app.com/hooks/anakin",
"status": "success",
"httpStatus": 200,
"attempts": 1,
"createdAt": "2026-01-01T12:00:01Z"
}
]
}| Field | Type | Description |
|---|---|---|
event | string | change or test. |
channel | string | webhook or email. |
url | string | Destination — the webhook URL or the comma-separated recipients. |
status | string | pending, success, failed, or exhausted (retries used up). |
httpStatus | number | null | HTTP status returned by the webhook. |
attempts | number | Delivery attempts so far. |
error | string | null | Failure reason, when failed. |
nextAttemptAt | string | null | When the next retry is scheduled (while pending). |
https://api.anakin.io/v1/monitors/{id}/deliveries/{deliveryId}/retryRe-queue a failed or exhausted delivery for another attempt.
Response
200 OK{ "success": true }Returns 404 not_found if the delivery doesn't belong to this monitor.
Code Examples
# Fire a test alert
curl -X POST https://api.anakin.io/v1/monitors/b1e7c2a4-.../test-alert -H "X-API-Key: your_api_key"
# Inspect deliveries
curl https://api.anakin.io/v1/monitors/b1e7c2a4-.../deliveries -H "X-API-Key: your_api_key"
# Retry one
curl -X POST https://api.anakin.io/v1/monitors/b1e7c2a4-.../deliveries/d_5f2.../retry -H "X-API-Key: your_api_key"import requests
headers = {"X-API-Key": "your_api_key"}
mid = "b1e7c2a4-..."
print(requests.post(f"https://api.anakin.io/v1/monitors/{mid}/test-alert", headers=headers).json())
deliveries = requests.get(f"https://api.anakin.io/v1/monitors/{mid}/deliveries", headers=headers).json()["deliveries"]
for d in deliveries:
if d["status"] in ("failed", "exhausted"):
requests.post(f"https://api.anakin.io/v1/monitors/{mid}/deliveries/{d['id']}/retry", headers=headers)