Feature 1: Flexible Notification System

Overview

The current notification system in Semaphore UI is configured exclusively through config.json, supports a limited set of channels (Email, Telegram, Slack, MS Teams), and operates at the global level with minimal per-project customization. This feature redesigns notifications from the ground up to be UI-managed, extensible, and configurable at project and template granularity.

Motivation

  • Users cannot add or configure notification channels without editing config files and restarting the server.
  • There is no way to have multiple instances of the same channel type (e.g., two different Slack webhooks for different teams).
  • Per-project notification routing is limited — Slack and MS Teams are global, only Telegram supports per-project chat IDs.
  • Many popular platforms (Discord, Ntfy, Google Chat, Rocket.Chat, Pushover) have no native support.
  • There is no outbound webhook mechanism for integrating with arbitrary external services.

Detailed Specification

Community

1.1 Extensible Channel Architecture

Goal: Define a common NotificationChannel interface in the backend with per-channel implementations, making it possible to add new channels by implementing a single interface.

Design considerations:

  • Consider adopting a universal notification library such as nikoksr/notify (Go library supporting 50+ services) or integrating with Apprise as a notification gateway to avoid maintaining per-provider code.
  • Each channel implementation must support: sending a message, validating configuration, and reporting delivery status.
  • Channel implementations should be registered via a plugin-like registry so that new channels can be added without modifying core dispatch logic.

Related issues: #2325, #1290

1.2 UI-Managed Notification Configuration

Goal: Move notification setup entirely from config.json into the Web UI.

Requirements:

  • Notification channels are configured per-project in the project settings UI.
  • Support creating multiple instances of the same channel type within a project (e.g., two Slack webhooks — one for #ops, one for #dev).
  • Each channel instance stores its own configuration (URL, token, chat ID, etc.) in the database.
  • Per-template notification options: choose which channel instances fire on success vs. failure.
  • Project-level defaults: define default notification behavior that templates inherit unless overridden.
  • Inspired by Uptime Kuma’s notification management UI — a good reference for the UX pattern.
  • Backward compatibility: continue reading config.json as a fallback for existing deployments, but mark it as deprecated in docs.

Related issues: #3387, #1821, #3588

1.3 Template-Based Message Customization

Goal: Allow users to customize notification message content using templates.

Requirements:

  • Templates are stored as files on disk (not in the database) for easy version control and deployment.
  • Support Go template syntax with access to task context variables: {{ .Task.Name }}, {{ .Task.Status }}, {{ .Task.URL }}, {{ .Project.Name }}, {{ .Template.Name }}, {{ .Task.Duration }}, etc.
  • Provide default templates for each channel that ship with the application.
  • Users override defaults by placing custom template files in a configurable directory.
  • Templates are resolved per-channel (e.g., slack.tmpl, email.tmpl, telegram.tmpl).

1.4 Per-Project Channel Selection

Goal: Allow users to configure which notification channels are active on a per-project basis through project settings.

Requirements:

  • Project settings UI shows all configured channel instances with enable/disable toggles.
  • Channels can be enabled for specific event types: task start, task success, task failure.
  • Templates within the project can override the project-level channel selection.

Related issues: #3588

1.5 Outbound Webhook Events

Goal: Provide a generic webhook mechanism for integrating with arbitrary external services and CI/CD pipelines.

Requirements:

  • Define event types: TASK_START, TASK_SUCCESS, TASK_FAILURE, TASK_CANCEL.
  • Create “Webhook Templates” per project in the UI with:
    • Target URL
    • HTTP method (POST/PUT)
    • Custom headers (for authorization tokens, etc.)
    • HMAC authentication (SHA-256 and SHA-512) with a configurable secret key for payload signing
    • Request body template with access to task context variables
  • Attach webhook templates to task templates (select which webhooks fire for which events).
  • Use case: report build status to Gitea/GitHub commit status APIs, trigger downstream pipelines, send events to unsupported notification platforms.
  • Webhook delivery should be asynchronous with configurable retry on failure (1-3 retries with exponential backoff).

Related issues: #1825, #2594, #3066

1.6 New Notification Channels

Goal: Add native support for commonly requested notification platforms.

Channels to add:

Channel Protocol Notes Issue
Discord Webhook (similar to Slack) High demand, straightforward implementation #2924
Ntfy HTTP POST to ntfy.sh or self-hosted Lightweight, push-notification focused #3383
Google Chat Incoming webhook Mostly needs a different payload template; logic similar to Slack #1148
Rocket.Chat Incoming webhook Same HTTP webhook pattern as Slack #1091
Pushover HTTP API Requires user key + application token #2594

Note: If the outbound webhook feature (1.5) is implemented first, several of these could be documented as webhook configurations rather than native implementations.

1.7 “Fixed” Notifications (Recovery Alerts)

Goal: Notify when a template recovers from failure — i.e., the first successful run after one or more consecutive failures.

Requirements:

  • Track per-template last-known-status in the database.
  • When a template transitions from FAILURE → SUCCESS, send a “fixed” notification in addition to (or instead of) the normal success notification.
  • Add a checkbox on the template notification settings: “Notify on recovery.”
  • Similar to GitLab CI’s on_failure / on_success / on_change notification modes.

Related issues: #3380

1.8 Disable All Notifications per Template

Goal: Add a “Disable All Notifications” option on the task template options page.

Current state: Only “Disable Successful Notifications” checkbox exists. Users developing/debugging scripts have no way to suppress failure notifications without disabling the entire channel.

Requirements:

  • Add a “Disable All Notifications” checkbox on the template edit form.
  • When checked, no notifications of any kind (success, failure, recovery) are sent for tasks launched from this template.
  • Useful during development/debugging to avoid spamming team channels.

Related issues: #3724

1.9 Email on Success

Goal: Include email in the success notification path.

Current state: Email alerts are only sent on task failure. The sendMailAlert() function is not called from the success notification code path, while Telegram, Slack, and MS Teams are.

Fix: Add sendMailAlert() to the success notification handler, gated by the same “Disable Successful Notifications” checkbox.

Related issues: #3503

1.10 Fix Task URL in Notifications

Goal: Ensure all notification channels receive a fully qualified, clickable task URL.

Current state: Multiple channels produce broken or unclickable links:

  • Email: task link lost web_host prefix after an update, rendering links unclickable (#2097).
  • Slack: {{ .Task.URL }} outputs a relative URI path (/project/1/templates/6?t=189) without scheme/host, failing Slack’s URL validation (#2311).
  • MS Teams: task URL is truncated/incomplete (#3292).

Fix: Construct the task URL centrally using web_host configuration, producing a full URL (e.g., https://semaphore.example.com/project/1/templates/6?t=189). All notification templates should use this pre-built URL.

1.11 Fix Email Delivery Issues

Goal: Resolve multiple SMTP-related bugs that prevent email notifications from working.

Issues to fix:

Problem Root Cause Issue
Port 465 (implicit TLS/SMTPS) does not work Semaphore uses STARTTLS, which only works on port 587. Port 465 requires implicit TLS from connection start. #2201
Auth command sent before TLS handshake When TLS is off but SMTP auth is configured, the auth command is sent before STARTTLS #2971
Date header format incorrect Uses UTC string instead of +0000, causing display/sorting issues in mail clients #3542
Office365 port 587 fails STARTTLS negotiation fails — connection reset when email_secure: true, STARTTLS required when email_secure: false #3209
Display name in email_sender breaks sending Using "Name <email>" format in sender field causes errors #2201

1.12 Telegram Thread/Topic Support

Goal: Send Telegram notifications to specific group threads (topics) using message_thread_id.

Requirements:

  • Add a message_thread_id field to the Telegram notification configuration.
  • Configurable per-project (default thread) and per-template (override).
  • Also fix: double quotes in task names cause Telegram API 400 errors due to missing escaping (#3585).

Related issues: #3493, #1456

1.13 Slack Template Improvements

Goal: Expose top-level fields in the Slack notification payload for Workflow Builder compatibility.

Current state: Slack’s Workflow Builder can only access top-level JSON fields. The current payload nests everything under attachments, which Workflow Builder cannot parse.

Fix: Add title, title_link, text, color, author, version as top-level fields alongside the existing attachments array.

Related issues: #2607

1.14 Alert Proxy Support

Goal: Allow configuring an HTTP proxy for outbound notification HTTP calls.

Requirements:

  • Add alert_proxy_endpoint configuration option (in config.json and/or environment variable).
  • All outbound notification HTTP requests (Slack, Telegram, MS Teams, webhooks, etc.) use this proxy.
  • Does not affect other Semaphore HTTP traffic (Git operations, API, etc.).
  • Use case: air-gapped environments where only a proxy server has external network access.

Related issues: #1484


Pro

1.15 Pro-Only Notification Channels

Goal: Gate specific notification channels behind the Pro plan.

Requirements:

  • Certain channels (to be determined — potentially incident management integrations, advanced webhooks with HMAC, or high-volume channels) are only available to Pro subscribers.
  • The UI shows these channels as locked/unavailable on the Community plan with an upgrade prompt.

1.16 Long-Running Task Alerts

Goal: Trigger a notification when a task runs longer than a configurable threshold.

Requirements:

  • Add a “Maximum expected duration” field on the template edit form (in minutes).
  • If a running task exceeds this threshold, fire a notification to configured channels.
  • The notification should include the current runtime and a link to the running task.
  • Use case: detecting stuck tasks, runaway playbooks, or infrastructure issues that cause abnormal execution times.
  • Implementation: a background goroutine that periodically checks running tasks against their thresholds.

Related issues: #1393


Enterprise

1.17 Audit Log for Notifications

Goal: Maintain a searchable, persistent log of all notification delivery attempts.

Requirements:

  • Log entry fields: timestamp, channel type, channel instance, recipient(s), event type, task ID, delivery status (sent/failed/retried), error message (if any).
  • Searchable and filterable in the UI by date range, channel, status, and project.
  • Improve error logging to include recipient context on failures — currently, email send failures do not indicate which recipient failed (#3410).
  • Retention policy: configurable retention period (default 90 days).

Related issues: #3410

1.18 Integration with Incident Management Platforms

Goal: Native integrations with enterprise incident management tools.

Integrations:

  • PagerDuty — create/resolve incidents via Events API v2. Map task failure → trigger, task recovery → resolve.
  • Opsgenie — create/close alerts via Opsgenie API. Support priority mapping based on template configuration.
  • ServiceNow — create incident records via ServiceNow REST API. Map Semaphore task metadata to ServiceNow fields (assignment group, category, priority).

Requirements:

  • Each integration is configured per-project with API keys/tokens stored in the key store.
  • Automatic incident lifecycle: failure creates an incident, recovery resolves it.
  • De-duplication: repeated failures for the same template do not create multiple incidents.

1.19 Role-Based Notification Access Control

Goal: Restrict who can configure notification rules and channels based on organizational roles.

Requirements:

  • Only project admins (or a new “notification admin” role) can create/modify/delete notification channel configurations.
  • Project members can view which channels are configured but cannot modify them.
  • Notification rules (which templates use which channels) can be locked by admins to prevent modification by regular users.

Database Schema Changes

New tables required:

  • notification_channel — stores channel instances (project_id, type, name, config JSON, created_at, updated_at)
  • notification_rule — links channels to templates/events (template_id, channel_id, on_success, on_failure, on_start, on_recovery)
  • notification_log — audit log entries (Enterprise only) (channel_id, task_id, event_type, status, error, recipient, timestamp)

API Endpoints

  • GET/POST /api/project/{id}/notification-channels — list/create channels
  • PUT/DELETE /api/project/{id}/notification-channels/{channel_id} — update/delete channel
  • POST /api/project/{id}/notification-channels/{channel_id}/test — send test notification
  • GET/PUT /api/project/{id}/templates/{id}/notification-rules — get/set template notification rules
  • GET /api/project/{id}/notification-log — query audit log (Enterprise)

İlginizi çekebilecek diğer yazılar