Skip to main content

Webhook Payloads

This page documents the payload schemas for each webhook event type. For webhook configuration, HMAC verification, and retry policies, see Webhooks.


Common envelope

Every webhook delivery wraps the event-specific payload in a common envelope:

{
"id": "evt_abc123def456",
"type": "execution.completed",
"timestamp": "2025-03-10T19:00:05Z",
"team_id": "team_xyz",
"data": { ... }
}
FieldTypeDescription
idstringUnique event ID — use for deduplication
typestringEvent type (see table below)
timestampstringISO 8601 timestamp of when the event occurred
team_idstringThe team that owns the resource
dataobjectEvent-specific payload (documented per event below)

Event types

Event typeTrigger
execution.startedAn Execution begins running
execution.completedAn Execution finishes successfully
execution.failedAn Execution fails
schedule.activatedA Schedule is activated (set to Active)
schedule.pausedA Schedule is paused (set to Inactive)
budget.threshold_breachedA budget threshold is crossed
budget.exceededSpend exceeds the budget amount
violation.createdA new Guard violation is detected
violation.resolvedA Guard violation is resolved
scan.completedA Guard scan finishes
finding.createdA new scan finding is detected
anomaly.detectedA cost anomaly is detected
connection.health_changedA Connection health status changes
access_request.submittedAn access request is submitted
access_request.approvedAn access request is approved
access_request.deniedAn access request is denied

Execution events

execution.started

{
"id": "evt_001",
"type": "execution.started",
"timestamp": "2025-03-10T19:00:00Z",
"team_id": "team_xyz",
"data": {
"execution_id": "exec_abc123",
"target_id": "target_def456",
"target_name": "Dev EC2 Instances",
"action": "stop",
"trigger": "schedule",
"schedule_id": "sched_ghi789",
"schedule_name": "Dev Weekday Stop",
"resource_count": 12,
"connection_id": "conn_jkl012"
}
}

execution.completed

{
"data": {
"execution_id": "exec_abc123",
"target_id": "target_def456",
"target_name": "Dev EC2 Instances",
"action": "stop",
"trigger": "schedule",
"schedule_id": "sched_ghi789",
"resource_count": 12,
"resources_actioned": 10,
"resources_already_in_state": 2,
"resources_failed": 0,
"duration_seconds": 45,
"estimated_savings_usd": 28.50
}
}

execution.failed

{
"data": {
"execution_id": "exec_abc123",
"target_id": "target_def456",
"target_name": "Dev EC2 Instances",
"action": "stop",
"trigger": "manual",
"triggered_by": "user_abc",
"error_code": "iam_permission_denied",
"error_message": "Unable to assume IAM role FrugallyAccessRole in account 123456789012.",
"resource_count": 12,
"resources_actioned": 0,
"resources_failed": 12
}
}

Schedule events

schedule.activated

{
"data": {
"schedule_id": "sched_ghi789",
"name": "Dev Weekday Stop",
"activated_by": "user_abc",
"next_run_at": "2025-03-11T19:00:00Z",
"timezone": "Europe/London"
}
}

schedule.paused

{
"data": {
"schedule_id": "sched_ghi789",
"name": "Dev Weekday Stop",
"paused_by": "user_abc"
}
}

Budget events

budget.threshold_breached

{
"data": {
"budget_id": "budget_def456",
"project_id": "proj_abc123",
"project_name": "Production Infrastructure",
"budget_name": "Monthly EC2 spend",
"amount": 5000.00,
"current_spend": 4005.00,
"percentage_used": 80.1,
"threshold_percentage": 80,
"currency": "USD",
"period": "monthly"
}
}

budget.exceeded

{
"data": {
"budget_id": "budget_def456",
"project_id": "proj_abc123",
"project_name": "Production Infrastructure",
"budget_name": "Monthly EC2 spend",
"amount": 5000.00,
"current_spend": 5120.00,
"overspend": 120.00,
"percentage_used": 102.4,
"currency": "USD",
"period": "monthly"
}
}

Violation events

violation.created

{
"data": {
"violation_id": "viol_ghi789",
"project_id": "proj_abc123",
"project_name": "Production Infrastructure",
"type": "budget_threshold_breach",
"severity": "warning",
"message": "Monthly EC2 spend has reached 80% of the $5,000 budget.",
"budget_id": "budget_def456"
}
}

violation.resolved

{
"data": {
"violation_id": "viol_ghi789",
"project_id": "proj_abc123",
"resolved_by": "user_abc",
"resolution_note": "Budget increased to $7,000 for Q2."
}
}

Scan events

scan.completed

{
"data": {
"scan_id": "scan_mno345",
"project_id": "proj_abc123",
"project_name": "Production Infrastructure",
"status": "completed",
"findings_count": 5,
"findings_by_severity": {
"critical": 0,
"high": 2,
"medium": 2,
"low": 1
},
"duration_seconds": 30
}
}

finding.created

{
"data": {
"finding_id": "find_jkl012",
"scan_id": "scan_mno345",
"project_id": "proj_abc123",
"resource_id": "i-0abc123def456",
"resource_type": "ec2:instance",
"severity": "high",
"title": "Untagged EC2 instance running in production",
"recommendation": "Add the required tags: Environment, Owner, CostCentre."
}
}

Anomaly events

anomaly.detected

{
"data": {
"anomaly_id": "anom_pqr678",
"connection_id": "conn_jkl012",
"service": "Amazon EC2",
"metric": "daily_cost",
"expected_value": 150.00,
"actual_value": 420.00,
"deviation_percentage": 180.0,
"severity": "critical",
"period_start": "2025-03-10",
"period_end": "2025-03-10"
}
}

Connection events

connection.health_changed

{
"data": {
"connection_id": "conn_jkl012",
"account_id": "123456789012",
"previous_status": "connected",
"new_status": "degraded",
"reason": "Cost Explorer verification failed — ce:GetCostAndUsage permission denied."
}
}

Access request events

access_request.submitted

{
"data": {
"request_id": "req_stu901",
"requested_by": "user_ghi",
"requested_by_email": "charlie@example.com",
"resource_type": "project",
"resource_id": "proj_abc123",
"resource_name": "Production Infrastructure",
"role_requested": "contributor",
"reason": "Need to review Q2 budget compliance."
}
}

access_request.approved / access_request.denied

{
"data": {
"request_id": "req_stu901",
"decided_by": "user_abc",
"decision": "approved",
"note": "Approved for Q2."
}
}

Verifying webhook signatures

Each webhook delivery includes an X-Frugally-Signature header containing an HMAC-SHA256 signature. Verify it against the raw request body using your webhook signing secret:

import hmac
import hashlib

def verify_signature(payload_body, signature_header, secret):
expected = hmac.new(
secret.encode('utf-8'),
payload_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature_header)

For full details, see Webhooks — HMAC signing.