Webhooks Endpoint
The Webhooks API allows you to receive real-time notifications when events occur in CICosts.
Webhook Events
| Event | Description |
|---|---|
workflow.completed | A workflow run has completed |
alert.triggered | An alert has been triggered |
alert.resolved | An alert has been resolved |
cost.threshold | A cost threshold has been reached |
export.ready | An export file is ready for download |
List Webhooks
Get all configured webhooks.
GET /v1/webhooks
Example Response
{
"data": {
"webhooks": [
{
"id": "wh_abc123",
"url": "https://example.com/webhooks/cicosts",
"events": ["workflow.completed", "alert.triggered"],
"status": "active",
"secret_prefix": "whsec_abc...",
"last_delivery_at": "2024-01-15T11:30:00Z",
"last_delivery_status": "success",
"created_at": "2023-12-01T00:00:00Z"
}
]
}
}
Create Webhook
Create a new webhook endpoint.
POST /v1/webhooks
Request Body
{
"url": "https://example.com/webhooks/cicosts",
"events": ["workflow.completed", "alert.triggered"],
"secret": "optional_shared_secret"
}
Example Response
{
"data": {
"id": "wh_def456",
"url": "https://example.com/webhooks/cicosts",
"events": ["workflow.completed", "alert.triggered"],
"status": "active",
"secret": "whsec_xxxxxxxxxxxxxxxxxxx",
"created_at": "2024-01-15T12:00:00Z"
}
}
Secret shown once
The webhook secret is only returned when creating the webhook. Store it securely.
Update Webhook
Update a webhook configuration.
PUT /v1/webhooks/:id
Request Body
{
"url": "https://example.com/webhooks/new-endpoint",
"events": ["alert.triggered", "alert.resolved"],
"status": "paused"
}
Delete Webhook
Delete a webhook.
DELETE /v1/webhooks/:id
Webhook Payloads
workflow.completed
Sent when a workflow run completes.
{
"event": "workflow.completed",
"timestamp": "2024-01-15T11:30:00Z",
"data": {
"run": {
"id": "run_xyz789",
"workflow_id": "wf_abc123",
"workflow_name": "ci.yml",
"repository": "api-service",
"organization": "my-company",
"status": "success",
"duration_minutes": 12.5,
"cost": 3.45,
"started_at": "2024-01-15T11:15:00Z",
"completed_at": "2024-01-15T11:27:30Z"
}
}
}
alert.triggered
Sent when an alert is triggered.
{
"event": "alert.triggered",
"timestamp": "2024-01-15T08:00:00Z",
"data": {
"alert": {
"id": "alert_abc123",
"name": "Monthly Budget Alert",
"type": "threshold"
},
"trigger": {
"current_value": 2015.67,
"threshold_value": 2000,
"metric": "monthly_cost"
}
}
}
alert.resolved
Sent when an alert condition is no longer met.
{
"event": "alert.resolved",
"timestamp": "2024-01-16T08:00:00Z",
"data": {
"alert": {
"id": "alert_abc123",
"name": "Monthly Budget Alert"
},
"resolution": {
"triggered_at": "2024-01-15T08:00:00Z",
"resolved_at": "2024-01-16T08:00:00Z",
"duration_hours": 24
}
}
}
cost.threshold
Sent when a cost threshold is reached (50%, 75%, 90%, 100% of budget).
{
"event": "cost.threshold",
"timestamp": "2024-01-15T10:00:00Z",
"data": {
"threshold": {
"percentage": 75,
"budget": 2000,
"current_spend": 1500
},
"scope": {
"type": "organization",
"org": "my-company"
}
}
}
Verifying Webhooks
Webhook requests include a signature header for verification.
Signature Header
X-CICosts-Signature: sha256=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Verification (Node.js)
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return `sha256=${expectedSignature}` === signature;
}
// Express middleware
app.post('/webhooks/cicosts', (req, res) => {
const signature = req.headers['x-cicosts-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
console.log('Received:', req.body.event);
res.status(200).send('OK');
});
Verification (Python)
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return f"sha256={expected}" == signature
Webhook Delivery
Retry Policy
Failed deliveries are retried:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failures, the webhook is marked as failing.
Response Requirements
Your endpoint must:
- Respond within 30 seconds
- Return a 2xx status code
- Handle duplicate deliveries (use event IDs)
Debugging
View delivery history:
GET /v1/webhooks/:id/deliveries
{
"data": {
"deliveries": [
{
"id": "del_abc123",
"event": "workflow.completed",
"status": "success",
"response_code": 200,
"response_time_ms": 234,
"delivered_at": "2024-01-15T11:30:00Z"
}
]
}
}
See also: Integrations →