Skip to main content

Overview

Borg UI integrates with Apprise to send notifications to 100+ services including Email, Slack, Discord, Telegram, Pushover, Microsoft Teams, and many more.

Supported Services

Apprise supports a wide range of notification services:
  • Slack - Team collaboration
  • Discord - Gaming and community chat
  • Telegram - Secure messaging
  • Microsoft Teams - Enterprise collaboration
  • Mattermost - Self-hosted chat
  • Rocket.Chat - Open source chat
See the Apprise Wiki for a complete list of supported services and URL formats.

Creating Notification Settings

1

Navigate to Notifications

Go to Settings > Notifications in the Borg UI interface.
2

Add Notification Service

Create a new notification configuration:
POST /api/notifications
{
  "name": "Production Slack",
  "service_url": "slack://TokenA/TokenB/TokenC",
  "enabled": true,
  "title_prefix": "[PROD]",
  "include_job_name_in_title": true,
  "notify_on_backup_start": false,
  "notify_on_backup_success": true,
  "notify_on_backup_failure": true,
  "notify_on_restore_success": false,
  "notify_on_restore_failure": true,
  "notify_on_check_success": false,
  "notify_on_check_failure": true,
  "monitor_all_repositories": true,
  "repository_ids": null
}
# From notifications.py:22-38
class NotificationSettingsCreate(BaseModel):
    name: str = Field(..., min_length=1, max_length=255)
    service_url: str = Field(..., description="Apprise service URL")
    enabled: bool = Field(default=True)
    title_prefix: Optional[str] = Field(default=None, max_length=100)
    include_job_name_in_title: bool = Field(default=False)
    notify_on_backup_start: bool = Field(default=False)
    notify_on_backup_success: bool = Field(default=False)
    notify_on_backup_failure: bool = Field(default=True)
    notify_on_restore_success: bool = Field(default=False)
    notify_on_restore_failure: bool = Field(default=True)
    notify_on_check_success: bool = Field(default=False)
    notify_on_check_failure: bool = Field(default=True)
    monitor_all_repositories: bool = Field(default=True)
    repository_ids: Optional[List[int]] = Field(default=None)
3

Test Notification

Verify the service URL before saving:
POST /api/notifications/test
{
  "service_url": "slack://TokenA/TokenB/TokenC"
}
# From notifications.py:218-225
@router.post("/test")
async def test_notification(
    request: TestNotificationRequest,
    current_user: User = Depends(get_current_user)
):
    result = await notification_service.test_notification(request.service_url)
    return result

Service URL Formats

Each notification service has a specific URL format:
Slack Webhook Integration
slack://TokenA/TokenB/TokenC
slack://TokenA/TokenB/TokenC/#channel
slacks://TokenA/TokenB/TokenC  # SSL
Setup:
  1. Go to Slack App Settings
  2. Create Incoming Webhook
  3. Copy the webhook tokens
  4. Format: https://hooks.slack.com/services/TokenA/TokenB/TokenC
  5. Use tokens in URL: slack://TokenA/TokenB/TokenC
Discord Webhook
discord://WebhookID/WebhookToken
Setup:
  1. Go to Server Settings > Integrations > Webhooks
  2. Create webhook
  3. Copy webhook URL: https://discord.com/api/webhooks/ID/Token
  4. Format as: discord://ID/Token
Telegram Bot
tgram://BotToken/ChatID
Setup:
  1. Create bot with @BotFather
  2. Get bot token
  3. Get chat ID (send message to bot, check updates)
  4. Format: tgram://BotToken/ChatID
Generic SMTPGmail Example:
Gmail requires an App Password, not your regular password. Enable 2FA and generate an App Password in Google Account settings.
Pushover Notifications
pover://UserKey@AppToken
pover://UserKey@AppToken/Device
Setup:
  1. Sign up at pushover.net
  2. Get user key from dashboard
  3. Create application to get app token
  4. Format: pover://UserKey@AppToken
Teams Webhook
msteams://TokenA/TokenB/TokenC
Setup:
  1. Go to Teams channel
  2. Add Incoming Webhook connector
  3. Copy webhook URL
  4. Extract tokens from URL
Custom JSON Webhooks
json://webhook.example.com/path
jsons://webhook.example.com/path  # SSL
Use Case:
# From notifications.py:25
service_url: str = Field(..., description="Use json:// or jsons:// for JSON webhooks")
Borg UI sends:
{
  "title": "[PROD] Backup Failed",
  "body": "Backup for repository 'production' failed: Connection timeout",
  "type": "failure"
}

Event Types

Configure which events trigger notifications:
# From notifications.py:28-36
notify_on_backup_start: bool = Field(default=False)
notify_on_backup_success: bool = Field(default=False)
notify_on_backup_failure: bool = Field(default=True)
notify_on_restore_success: bool = Field(default=False)
notify_on_restore_failure: bool = Field(default=True)
notify_on_check_success: bool = Field(default=False)
notify_on_check_failure: bool = Field(default=True)
notify_on_schedule_failure: bool = Field(default=True)
  • backup_start: Backup operation begins
  • backup_success: Backup completes successfully
  • backup_failure: Backup fails or is cancelled

Repository Filtering

Monitor specific repositories or all repositories:
# From notifications.py:37-38
monitor_all_repositories: bool = Field(default=True)
repository_ids: Optional[List[int]] = Field(default=None)
Monitor everything
{
  "monitor_all_repositories": true,
  "repository_ids": null
}
Receives notifications for all backup operations across all repositories.
Use Case Example:
[
  {
    "name": "Critical Systems Slack",
    "service_url": "slack://TokenA/TokenB/TokenC/#critical",
    "monitor_all_repositories": false,
    "repository_ids": [1, 2],  // Production repos only
    "notify_on_backup_failure": true
  },
  {
    "name": "All Events Email",
    "service_url": "mailto://admin:[email protected][email protected]",
    "monitor_all_repositories": true,
    "notify_on_backup_success": true,
    "notify_on_backup_failure": true
  }
]

Title Customization

Customize notification titles for easy identification:
# From notifications.py:26-27
title_prefix: Optional[str] = Field(default=None, max_length=100)
include_job_name_in_title: bool = Field(default=False)
Examples:
# Without customization
"Backup Failed"

# With title_prefix="[PROD]"
"[PROD] Backup Failed"

# With title_prefix="[PROD]" and include_job_name_in_title=true
"[PROD] Daily Backup: Backup Failed"

Managing Notifications

List All Notifications

GET /api/notifications
# From notifications.py:100-107
@router.get("", response_model=List[NotificationSettingsResponse])
async def list_notification_settings(
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    settings = db.query(NotificationSettings).all()
    return settings
Response:
[
  {
    "id": 1,
    "name": "Production Slack",
    "service_url": "slack://TokenA/TokenB/TokenC",
    "enabled": true,
    "title_prefix": "[PROD]",
    "include_job_name_in_title": true,
    "notify_on_backup_failure": true,
    "monitor_all_repositories": true,
    "repositories": [],
    "created_at": "2026-02-01T10:00:00Z",
    "updated_at": "2026-02-28T10:00:00Z",
    "last_used_at": "2026-02-28T10:30:00Z"
  }
]

Update Notification Settings

PUT /api/notifications/{setting_id}
# From notifications.py:157-194
@router.put("/{setting_id}", response_model=NotificationSettingsResponse)
async def update_notification_setting(
    setting_id: int,
    setting_data: NotificationSettingsUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    setting = db.query(NotificationSettings).filter(
        NotificationSettings.id == setting_id
    ).first()
    
    # Update fields
    update_data = setting_data.model_dump(exclude_unset=True)
    repository_ids = update_data.pop('repository_ids', None)
    
    for key, value in update_data.items():
        setattr(setting, key, value)
    
    # Update repository associations
    if repository_ids is not None:
        if not setting.monitor_all_repositories and repository_ids:
            repositories = db.query(Repository).filter(Repository.id.in_(repository_ids)).all()
            setting.repositories = repositories

Delete Notification Settings

DELETE /api/notifications/{setting_id}
# From notifications.py:197-216
@router.delete("/{setting_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_notification_setting(
    setting_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    setting = db.query(NotificationSettings).filter(
        NotificationSettings.id == setting_id
    ).first()
    
    if not setting:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Notification setting not found"
        )
    
    db.delete(setting)
    db.commit()

Notification Data Model

# From notifications.py:67-92
class NotificationSettingsResponse(BaseModel):
    id: int
    name: str
    service_url: str
    enabled: bool
    title_prefix: Optional[str]
    include_job_name_in_title: bool
    notify_on_backup_start: bool
    notify_on_backup_success: bool
    notify_on_backup_failure: bool
    notify_on_restore_success: bool
    notify_on_restore_failure: bool
    notify_on_check_success: bool
    notify_on_check_failure: bool
    notify_on_schedule_failure: bool
    monitor_all_repositories: bool
    repositories: List[RepositoryInfo]
    created_at: datetime
    updated_at: datetime
    last_used_at: Optional[datetime]

Notification Best Practices

  • Test First: Always test service URLs before saving
  • Failure Alerts: Enable failure notifications for critical monitoring
  • Success Spam: Avoid success notifications unless needed (reduces noise)
  • Title Prefixes: Use environment tags like [PROD], [DEV], [STAGING]
  • Multiple Services: Configure different services for different severity levels
  • Repository Filtering: Use selective monitoring for large deployments
  • Quiet Hours: Some services support quiet hours in their configuration

Common Integration Examples

Production Alert Stack

[
  {
    "name": "Critical Failures (PagerDuty)",
    "service_url": "pagerduty://IntegrationKey@ApiKey",
    "notify_on_backup_failure": true,
    "notify_on_check_failure": true,
    "monitor_all_repositories": false,
    "repository_ids": [1, 2, 3]  // Production only
  },
  {
    "name": "Team Slack",
    "service_url": "slack://TokenA/TokenB/TokenC/#backups",
    "title_prefix": "[PROD]",
    "notify_on_backup_success": true,
    "notify_on_backup_failure": true,
    "monitor_all_repositories": true
  },
  {
    "name": "Admin Email",
    "service_url": "mailto://alerts:[email protected][email protected]",
    "notify_on_backup_failure": true,
    "notify_on_schedule_failure": true,
    "monitor_all_repositories": true
  }
]

Home Lab Setup

[
  {
    "name": "Discord Alerts",
    "service_url": "discord://WebhookID/WebhookToken",
    "notify_on_backup_failure": true,
    "notify_on_check_failure": true
  },
  {
    "name": "Pushover Mobile",
    "service_url": "pover://UserKey@AppToken",
    "notify_on_backup_failure": true
  }
]

Build docs developers (and LLMs) love