Documentation
Bridge SDK
Notification Channels

Notification Channels

Push agent events to Discord, Telegram, and WhatsApp. The notification system uses a pluggable adapter architecture — each platform is a separate adapter behind a unified dispatcher.

Architecture

Agent Events (agent_end, tool errors, quality gates)


  NotificationDispatcher
  ├── fan-out to all registered adapters
  ├── retry with exponential backoff
  ├── rate limiting (global + per-channel)
  └── dead letter queue

    ┌────┼────┐
    ▼    ▼    ▼
Discord Telegram WhatsApp

Quick Start

import { createNotificationExtension } from '@mariozechner/pi-coding-agent/extensions/nity-notifications';
 
const notifications = await createNotificationExtension({
  enabled: true,
  defaultPriority: 'normal',
  channels: {
    discord: {
      enabled: true,
      mode: 'webhook',
      webhookUrl: 'ENV:DISCORD_WEBHOOK_URL',
    },
  },
});
 
// Send a notification
await notifications.sendText('Build completed successfully');
 
// Send with priority and target channels
await notifications.sendText(
  'Quality gate failed',
  'critical',
  ['discord:build-alerts']
);
 
// Shutdown
await notifications.shutdown();

Supported Platforms

PlatformModeBidirectionalPriority
DiscordWebhookNo (outbound only)Immediate
TelegramBot API (long-polling)Yes (replies + buttons)Immediate
WhatsAppCloud API (Meta)Yes (webhook)P2 — requires Meta verification

Discord

Webhook-only — no bot needed. Single HTTP POST per notification with embed support and priority color coding.

channels: {
  discord: {
    enabled: true,
    mode: 'webhook',
    webhookUrl: 'https://discord.com/api/webhooks/123/abc',
    botName: 'Agent',         // optional
    avatarUrl: 'https://...',  // optional
  },
}

Telegram

Bot API with long-polling (no HTTP server needed). Supports inline keyboard buttons for interactive notifications and bidirectional message handling.

channels: {
  telegram: {
    enabled: true,
    mode: 'long-polling',
    botToken: 'ENV:TELEGRAM_BOT_TOKEN',
    defaultChatId: 'ENV:TELEGRAM_CHAT_ID',
    parseMode: 'MarkdownV2',
  },
}

Telegram adapter automatically handles MarkdownV2 escaping and supports inline keyboard buttons from ActionButton definitions.

WhatsApp

Cloud API (Meta Business Platform). Supports text, media, interactive buttons, and template messages.

channels: {
  whatsapp: {
    enabled: true,
    mode: 'cloud-api',
    phoneNumberId: 'ENV:WHATSAPP_PHONE_ID',
    phoneNumber: 'ENV:WHATSAPP_PHONE',
    accessToken: 'ENV:WHATSAPP_TOKEN',
    appSecret: 'ENV:WHATSAPP_SECRET',
    webhookVerifyToken: 'ENV:WHATSAPP_VERIFY_TOKEN',
    defaultRecipient: 'ENV:WHATSAPP_RECIPIENT',
  },
}
⚠️

WhatsApp requires Meta Business verification. Use Discord or Telegram for immediate setup.

Event Mapping

Agent lifecycle events automatically map to notifications:

Agent EventPriorityContent
agent_endnormalTask completed summary
tool_execution_end (error)highTool name + error message
quality_gate (fail)criticalGate details + blocking issues
session_shutdownlowSession ended

Configuration

# Discord
export DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..."
 
# Telegram
export TELEGRAM_BOT_TOKEN="123456:ABC-DEF"
export TELEGRAM_CHAT_ID="123456789"
 
# WhatsApp
export WHATSAPP_PHONE_ID="123456789"
export WHATSAPP_PHONE="+1234567890"
export WHATSAPP_TOKEN="EAAxxxxx"
export WHATSAPP_SECRET="your-app-secret"
export WHATSAPP_VERIFY_TOKEN="your-verify-token"
export WHATSAPP_RECIPIENT="+1234567890"

Resilience

Retry with Backoff

Failed sends are retried with exponential backoff (configurable):

  • maxAttempts: 3 (default)
  • initialDelayMs: 1000
  • maxDelayMs: 30000
  • backoffMultiplier: 2
  • jitterMs: 500

Dead Letter Queue

Notifications that fail after all retries are persisted to the DLQ:

  • Content is redacted (truncated to 100 chars, metadata stripped) for security
  • JSONL persistence support
  • Manual retry: dispatcher.retryDLQ(index)
  • Manual purge: dispatcher.purgeDLQ()

Rate Limiting

Global and per-channel rate limits prevent platform API throttling:

  • Global: 20 requests/second (configurable)
  • Per-channel: configurable per platform (default: Discord 5/s, Telegram 25/s, WhatsApp 10/s)

Data Types

Notification

interface Notification {
  id: string;                          // UUID v7
  timestamp: string;                   // ISO 8601
  priority: 'low' | 'normal' | 'high' | 'critical';
  channels?: string[];                 // target channels (empty = all)
  content: NotificationContent;
  metadata?: Record<string, unknown>;
}
 
interface NotificationContent {
  text: string;                        // plain-text fallback (required)
  markdown?: string;                   // rich text
  media?: MediaAttachment[];           // images, video, audio, documents
  actions?: ActionButton[];            // interactive buttons
}

DeliveryReceipt

interface DeliveryReceipt {
  notificationId: string;
  channelId: string;
  status: 'accepted' | 'sent' | 'delivered' | 'read' | 'failed';
  platformMessageId?: string;
  error?: { code: string; message: string };
  timestamp: string;
}

Security

  • Secrets loaded from environment variables via ENV: prefix in config
  • WhatsApp webhook signature verified with crypto.timingSafeEqual() (timing-safe)
  • DLQ content redacted before persistence (no secret leakage)
  • No hardcoded credentials anywhere in the codebase

All adapter files are under 400 lines. The notification extension follows the same component size directive as the rest of the codebase.

Related