Alerts
Alert rules notify a webhook or email address. Create them in the admin UI (Alerts), with
POST /api/v1/alert-rules, or with the MCP tool create_alert_rule.
Triggers
| Trigger | Fires when |
|---|---|
new_issue |
An error is seen for the first time |
regression |
A resolved issue gets a new event |
spike |
An issue has at least threshold events (default 50, minimum 10) in the last hour and more than 3× its hourly average over the previous day. Checked every 5 minutes |
A rule without project applies to all projects. A spike rule notifies at most once per issue
every 6 hours.
Create a rule
curl -X POST https://vaka.example.com/api/v1/alert-rules -H "Authorization: Bearer $VAKA_TOKEN" \
-H 'Content-Type: application/json' -d '{
"project": "web",
"name": "New web errors to Slack bridge",
"trigger": "new_issue",
"channel": "webhook",
"target": "https://hooks.example.com/vaka",
"secret": "a-shared-secret"
}'
For email, set "channel": "email" and "target" to one or more comma-separated addresses.
Email needs VAKA_RESEND_API_KEY and VAKA_EMAIL_FROM on the server.
Test a rule with POST /api/v1/alert-rules/{id}/test and see results with
GET /api/v1/alert-rules/{id}/deliveries.
Webhook payload
POST with Content-Type: application/json and User-Agent: Vaka-Webhook/1:
{
"trigger": "new_issue",
"project": {"id": "…", "slug": "web", "name": "Web"},
"issue": {"id": "…", "short_id": "WEB-12", "title": "TypeError: x is undefined", "culprit": "checkout (src/cart.ts:42)", "level": "error", "platform": "javascript", "status": "open", "times_seen": 1, "first_seen": "…", "last_seen": "…", "first_release": "1.4.0", "last_release": "1.4.0"},
"url": "https://vaka.example.com/ui/projects/web/issues/…",
"api_url": "https://vaka.example.com/api/v1/issues/…",
"details": {},
"sent_at": "2026-11-10T08:00:00Z"
}
For spike, details is {"last_hour": 140, "hourly_avg_prev_24h": 12.5}. Non-2xx responses and
timeouts (10 s) are recorded as failed deliveries.
Verifying signatures
When the rule has a secret, the request carries
X-Vaka-Signature: sha256=<hex HMAC-SHA256 of the raw body with the secret>.
import crypto from 'node:crypto';
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-vaka-signature'] ?? ''));