Webhooks
Receive real-time HTTP POST notifications when events occur in StackSpend — such as a new anomaly being detected or a budget threshold being crossed.
How webhooks work
When an event occurs in StackSpend, it sends an HTTP POST request to each registered endpoint with a JSON body describing the event. Your server must respond with a 2xx status code within 10 seconds to acknowledge delivery.
Failed deliveries (non-2xx or timeout) are retried up to 5 times with exponential back-off over 24 hours.
Supported events
| Event type | Description | Status |
|---|---|---|
anomaly.created | A new spend anomaly has been detected for a connected provider. | Available |
budget.threshold_crossed | A provider has crossed a configured budget alert threshold. | Coming soon |
Creating a webhook endpoint
Open the Webhooks tab
Go to Settings → API → Webhooks and click Add endpoint.
Enter your URL
Paste the HTTPS URL of your server endpoint. StackSpend will POST events to this URL.
Copy your signing secret
After saving, a signing secret is shown once. Copy it immediately and store it securely — it will not be displayed again. If you lose it, rotate the secret from the endpoint's settings.
Request format
Every webhook delivery is an HTTP POST with a JSON body and the following headers:
| Header | Value |
|---|---|
X-StackSpend-Event | The event type, e.g. anomaly.created |
X-StackSpend-Event-Id | A unique UUID for this delivery. Use this to deduplicate retries. |
X-StackSpend-Timestamp | Unix timestamp (seconds) of when the event was sent. |
X-StackSpend-Signature | sha256=<hex> — HMAC-SHA256 signature for verifying authenticity. |
Verifying signatures
Always verify the signature before processing a webhook. This confirms the request came from StackSpend and has not been tampered with.
- Read the raw request body as a string (do not parse it yet).
- Build the signed string:
{timestamp}.{raw_body}, wheretimestampis the value fromX-StackSpend-Timestamp. - Compute HMAC-SHA256 of that string using your signing secret as the key.
- Hex-encode the result and prepend
sha256=. - Compare to the
X-StackSpend-Signatureheader using a constant-time comparison. - Reject the request if the signatures do not match, or if the timestamp is more than 5 minutes old.
Example payload — anomaly.created
{
"event": "anomaly.created",
"event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": 1234567890,
"data": {
"id": "9f8e7d6c-5b4a-3210-fedc-ba9876543210",
"provider_type": "aws",
"service": "Amazon EC2",
"severity": "high",
"deviation_percent": 82.4,
"actual_cost": 450.00,
"expected_cost": 247.00,
"detected_date": "2025-01-15"
}
}API management
Webhook endpoints can also be managed programmatically via the public API. All endpoints require a valid API key.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/public/webhooks/endpoints | List all webhook endpoints. |
| POST | /api/v1/public/webhooks/endpoints | Create a new webhook endpoint. |
| PATCH | /api/v1/public/webhooks/endpoints/{id} | Update an endpoint (URL, enabled state). |
| DELETE | /api/v1/public/webhooks/endpoints/{id} | Delete a webhook endpoint. |
| POST | /api/v1/public/webhooks/endpoints/{id}/test | Send a synthetic test event to the endpoint. |
