Goal
Allow users to configure a webhook URL that receives JSON payloads on key CodeFRAME events, enabling integration with Slack, Discord, or any HTTP endpoint.
Background
Browser notifications (issue #559) require the tab to be open at least once. Webhooks are the push-based alternative for headless/automated workflows and CI integrations.
Scope
Settings UI (add to Settings page from #554):
- Webhook URL field in a "Notifications" settings tab (or within existing tabs)
- [Test] button that fires a sample payload and shows status code
- Enable/disable toggle without clearing the URL
Backend:
- Store webhook URL in settings (encrypted at rest)
- Fire
POST {webhook_url} with JSON payload on three events:
- batch complete:
{ "event": "batch.completed", "batch_id": "...", "task_count": N, "timestamp": "..." }
- blocker created:
{ "event": "blocker.created", "blocker_id": "...", "task_id": "...", "timestamp": "..." }
- PR merged:
{ "event": "pr.merged", "pr_url": "...", "timestamp": "..." }
- Fire and forget (no retry), log failures to app logger
- 5-second timeout per request
Out of scope
- Webhook signing / HMAC verification (can be added later)
- More than 3 event types in v1
Acceptance criteria
Goal
Allow users to configure a webhook URL that receives JSON payloads on key CodeFRAME events, enabling integration with Slack, Discord, or any HTTP endpoint.
Background
Browser notifications (issue #559) require the tab to be open at least once. Webhooks are the push-based alternative for headless/automated workflows and CI integrations.
Scope
Settings UI (add to Settings page from #554):
Backend:
POST {webhook_url}with JSON payload on three events:{ "event": "batch.completed", "batch_id": "...", "task_count": N, "timestamp": "..." }{ "event": "blocker.created", "blocker_id": "...", "task_id": "...", "timestamp": "..." }{ "event": "pr.merged", "pr_url": "...", "timestamp": "..." }Out of scope
Acceptance criteria
npm testanduv run pytestpass for the new backend code