Skip to main content
Glama

Stop writing different code for each notification channel. NotifyHub gives you a single fluent API to send notifications via Email, SMS, WhatsApp, Slack, Telegram, Discord, Microsoft Teams, Firebase Push, Webhooks, WebSocket, Google Chat, Twitter/X, LinkedIn, Notion, Twitch, YouTube, Instagram, SendGrid, TikTok Shop, Facebook, AWS SNS, Mailgun, PagerDuty — or any custom channel you create.

notify.to(user)
    .via(EMAIL)
    .fallback(SMS)
    .priority(Priority.HIGH)
    .subject("Order confirmed")
    .template("order-confirmed")
    .param("orderId", order.getId())
    .attach(invoicePdf)
    .send();

Why NotifyHub?

Problem

Without NotifyHub

With NotifyHub

Email

JavaMail config, MIME types, Session...

.via(EMAIL)

📱

SMS

Twilio SDK, different API entirely

.via(SMS)

WhatsApp

Another Twilio setup, prefix logic

.via(WHATSAPP)

Slack

Webhook HTTP, JSON payload

.via(SLACK)

Telegram

Bot API, HTTP client setup

.via(TELEGRAM)

Discord

Webhook HTTP, JSON payload

.via(DISCORD)

👥

Teams

Incoming Webhook, MessageCard JSON

.via(TEAMS)

Push

Firebase Admin SDK, credentials...

.via(PUSH)

🔗

Webhook

Custom HTTP, payload template

.via(Channel.custom("pagerduty"))

WebSocket

Java WebSocket API, reconnect logic

.via(WEBSOCKET)

Google Chat

Webhook HTTP, JSON payload

.via(GOOGLE_CHAT)

Twitter/X

OAuth 1.0a, API v2 setup

.via(TWITTER)

💼

LinkedIn

OAuth 2.0, REST API setup

.via(LINKEDIN)

Notion

Integration Token, API setup

.via(NOTION)

Twitch

OAuth 2.0, Twitch API setup

.via(TWITCH)

YouTube

YouTube Data API v3 setup

.via(YOUTUBE)

Instagram

Meta Graph API setup

.via(INSTAGRAM)

📧

SendGrid

SendGrid API, webhook tracking

.via(Channel.custom("sendgrid"))

TikTok Shop

HMAC-SHA256, Shop API

.via(TIKTOK_SHOP)

Facebook

Graph API, Page tokens

.via(FACEBOOK)

☁️

AWS SNS

AWS SDK, credentials, ARN

.via(Channel.custom("aws-sns"))

Mailgun

Mailgun API, domain setup

.via(Channel.custom("mailgun"))

PagerDuty

Events API v2, routing key

.via(Channel.custom("pagerduty"))

Multiple channels

Completely different code for each

Same fluent API

Fallback

Manual try/catch chain

.fallback(SMS)

Retry

Implement yourself

Built-in exponential backoff

Async

Thread pools, CompletableFuture

.sendAsync()

Scheduling

ScheduledExecutor, timer logic

.schedule(Duration.ofMinutes(30))

Templates

Each channel has its own engine

One template, all channels

i18n

Manual locale resolution

.locale(Locale.PT_BR)

Rate limiting

Token bucket from scratch

Config-driven per-channel

Tracking

Build your own delivery log

Built-in receipts + JPA

Dead letters

Lost in the void

Auto-captured in DLQ

Deduplication

Track sent messages yourself

Built-in content hash / explicit key

Template versions

Manage files manually

.templateVersion("v2") + A/B test

Batch

Loop and pray

.toAll(users).send()

Monitoring

Wire Micrometer yourself

Auto-configured counters

Health checks

Write an Actuator indicator

Auto-configured

Admin UI

Build your own dashboard

Built-in /notify-admin

Circuit breaker

Implement yourself per channel

Built-in per-channel circuit breaker

Orchestration

Manual escalation logic

.orchestrate().first(EMAIL).ifNoOpen(24h).then(PUSH)

A/B testing

External service + glue code

Built-in .abTest("exp").variant(...).split(50,50)

Testing

Mock everything

TestNotifyHub captures all sends

New channel

Build from scratch

Implement one interface


Table of Contents


Quick Start

1. Add the dependency

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

Need extra channels? Add optional modules:

<!-- SMS + WhatsApp (Twilio) -->
<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-sms</artifactId>
    <version>1.0.0</version>
</dependency>

<!-- Slack / Telegram / Discord / Teams / Firebase Push / Webhook -->
<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-slack</artifactId>
    <version>1.0.0</version>
</dependency>

<!-- WebSocket / Google Chat -->
<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-websocket</artifactId>
    <version>1.0.0</version>
</dependency>

2. Configure in application.yml

notify:
  channels:
    email:
      host: smtp.gmail.com
      port: 587
      username: ${GMAIL_USER}
      password: ${GMAIL_PASS}
      from: noreply@myapp.com
      from-name: MyApp
      tls: true
  retry:
    max-attempts: 3
    strategy: exponential
  tracking:
    enabled: true

3. Inject and use

@Service
public class OrderService {

    private final NotifyHub notify;

    public OrderService(NotifyHub notify) {
        this.notify = notify;
    }

    public void confirmOrder(Order order) {
        notify.to(order.getCustomer())
            .via(Channel.EMAIL)
            .subject("Order confirmed!")
            .template("order-confirmed")
            .param("customerName", order.getCustomer().getName())
            .param("orderId", order.getId())
            .param("total", order.getTotal())
            .send();
    }
}

That's it. Three steps.


Features

Fallback Chain

If the primary channel fails, automatically try the next one:

notify.to(user)
    .via(Channel.WHATSAPP)
    .fallback(Channel.SMS)
    .fallback(Channel.EMAIL)
    .template("payment-reminder")
    .param("amount", "R$ 150,00")
    .send();
// Tries WhatsApp -> SMS -> Email

Multi-Channel Send

Send through ALL channels simultaneously:

notify.to(user)
    .via(Channel.EMAIL)
    .via(Channel.SLACK)
    .via(Channel.TEAMS)
    .subject("Security Alert")
    .content("Login from a new device detected")
    .sendAll();

Async Sending

Send notifications without blocking:

// Fire and forget
notify.to(user)
    .via(Channel.EMAIL)
    .template("welcome")
    .sendAsync();

// Or wait for result
CompletableFuture<Void> future = notify.to(user)
    .via(Channel.EMAIL)
    .via(Channel.SLACK)
    .content("Deploy complete!")
    .sendAllAsync();

future.thenRun(() -> log.info("All notifications sent!"));

Retry with Backoff

Automatic retry with exponential or fixed backoff:

# application.yml (global)
notify:
  retry:
    max-attempts: 3
    strategy: exponential  # waits 1s, 2s, 4s...
// Or per-notification
notify.to(user)
    .via(Channel.EMAIL)
    .retry(3)
    .template("invoice")
    .send();

Templates (Mustache)

Create templates in src/main/resources/templates/notify/:

order-confirmed.html (auto-used for email):

<h1>Hello, {{customerName}}!</h1>
<p>Your order <strong>#{{orderId}}</strong> has been confirmed.</p>
<p>Total: <strong>{{total}}</strong></p>

order-confirmed.txt (auto-used for SMS/WhatsApp/Slack/Telegram/Discord/Teams):

Hello {{customerName}}, your order #{{orderId}} is confirmed. Total: {{total}}

The library picks .html for email and .txt for other channels automatically.

i18n (Internationalization)

Templates support locale-based resolution with automatic fallback:

// User with locale
notify.to(user)
    .via(Channel.EMAIL)
    .locale(Locale.forLanguageTag("pt-BR"))
    .template("welcome")
    .param("name", user.getName())
    .send();

Template resolution order: welcome_pt_BR.html -> welcome_pt.html -> welcome.html

Your Notifiable can also return a locale:

public class User implements Notifiable {
    @Override
    public Locale getLocale() {
        return Locale.forLanguageTag("pt-BR");
    }
}

Attachments

Attach files to email notifications:

notify.to(user)
    .via(Channel.EMAIL)
    .subject("Your Invoice")
    .template("invoice")
    .attach("invoice.pdf", pdfBytes, "application/pdf")
    .attach(new File("/reports/monthly.xlsx"))
    .attach(Attachment.fromFile(contractFile))
    .send();

Priority Levels

Set notification priority. URGENT notifications bypass rate limiting:

notify.to(user)
    .via(Channel.EMAIL)
    .priority(Priority.URGENT)
    .subject("SERVER DOWN!")
    .content("Production server is unresponsive")
    .send();

Available priorities: URGENT (bypasses rate limits), HIGH, NORMAL (default), LOW.

Rate Limiting

Control notification throughput per-channel:

notify:
  rate-limit:
    enabled: true
    max-requests: 100
    window: 1m
    channels:
      email:
        max-requests: 50
        window: 1m
      sms:
        max-requests: 10
        window: 1m

Rate limiting uses a token bucket algorithm. URGENT priority notifications always bypass rate limits.

Dead Letter Queue (DLQ)

Failed notifications (after all retries) are automatically captured in the DLQ:

notify:
  tracking:
    enabled: true
    dlq-enabled: true

View and manage dead letters via the admin dashboard at /notify-admin/dlq, or programmatically:

DeadLetterQueue dlq = hub.getDeadLetterQueue();
List<DeadLetter> failed = dlq.findAll();
dlq.remove(deadLetterId); // after manual reprocessing

Batch Send

Send notifications to multiple recipients at once:

// By email addresses
notify.toAll(List.of("user1@test.com", "user2@test.com", "user3@test.com"))
    .via(Channel.EMAIL)
    .subject("System Maintenance")
    .template("maintenance-notice")
    .param("date", "2025-03-01")
    .send();

// By Notifiable entities
notify.toAllNotifiable(users)
    .via(Channel.EMAIL)
    .template("newsletter")
    .send();

// Async batch
notify.toAll(recipients)
    .via(Channel.EMAIL)
    .template("promo")
    .sendAsync();

Delivery Tracking

Track every notification with delivery receipts:

notify:
  tracking:
    enabled: true
    type: memory  # or "jpa" for database persistence
// Send and get a receipt
DeliveryReceipt receipt = notify.to(user)
    .via(Channel.EMAIL)
    .content("Hello!")
    .sendTracked();

System.out.println(receipt.getStatus());    // SENT
System.out.println(receipt.getId());         // uuid
System.out.println(receipt.getTimestamp());  // 2025-01-15T10:30:00Z

For database persistence, add the JPA tracker module:

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-tracker-jpa</artifactId>
    <version>1.0.0</version>
</dependency>
notify:
  tracking:
    enabled: true
    type: jpa

Scheduled Notifications

Schedule notifications for future delivery:

ScheduledNotification scheduled = notify.to(user)
    .via(Channel.EMAIL)
    .subject("Reminder")
    .content("Don't forget your appointment tomorrow!")
    .schedule(Duration.ofHours(24));

// Check status
scheduled.getStatus();        // SCHEDULED, SENT, FAILED, CANCELLED
scheduled.getRemainingDelay(); // PT23H59M...

// Cancel if needed
scheduled.cancel();

Notification Routing

Auto-route notifications based on user preferences:

public class User implements Notifiable {
    @Override
    public List<Channel> getPreferredChannels() {
        return List.of(Channel.WHATSAPP, Channel.SMS, Channel.EMAIL);
    }
}

// Auto-routes: WhatsApp (primary) -> SMS (fallback) -> Email (fallback)
notify.notify(user)
    .template("order-update")
    .param("orderId", "12345")
    .send();

Conditional routing with rules:

NotificationRouter router = NotificationRouter.builder()
    .rule(RoutingRule.timeBasedRule(
        LocalTime.of(9, 0), LocalTime.of(18, 0),
        Channel.SLACK, Channel.EMAIL))  // Slack during business hours, email after
    .build();

Notifiable Interface

Make your User entity a notification recipient:

@Entity
public class User implements Notifiable {

    private String name;
    private String email;
    private String phone;

    @Override
    public String getNotifyEmail() { return email; }

    @Override
    public String getNotifyPhone() { return phone; }

    @Override
    public String getNotifyName() { return name; }

    @Override
    public Locale getLocale() { return Locale.forLanguageTag("pt-BR"); }

    @Override
    public List<Channel> getPreferredChannels() {
        return List.of(Channel.EMAIL, Channel.SMS);
    }
}

Then just pass the user object:

notify.to(user)       // resolves email/phone automatically
    .via(Channel.EMAIL)
    .template("welcome")
    .send();

Or use raw addresses:

notify.to("user@email.com").via(Channel.EMAIL).content("Hello!").send();
notify.toPhone("+5511999999999").via(Channel.SMS).content("Code: 1234").send();

Message Deduplication

Prevent duplicate notifications automatically with content hashing or explicit keys:

notify:
  deduplication:
    enabled: true
    ttl: 24h
    strategy: content-hash  # content-hash | explicit-key | both
// Auto-dedup by content hash (same recipient + channel + content = skipped)
notify.to(user).via(EMAIL).content("Order confirmed").send();
notify.to(user).via(EMAIL).content("Order confirmed").send(); // skipped!

// Dedup by explicit key
notify.to(user).via(EMAIL)
    .deduplicationKey("order-" + orderId)
    .template("order-confirmed")
    .send();

Strategies:

  • content-hash — SHA-256 hash of recipient + channel + subject + content

  • explicit-key — uses the key provided via .deduplicationKey("...")

  • both — uses explicit key if provided, otherwise falls back to content hash

Without Spring Boot:

NotifyHub notify = NotifyHub.builder()
    .deduplicationStore(new InMemoryDeduplicationStore(Duration.ofHours(12)))
    .channel(emailChannel)
    .build();

Template Versioning

Manage multiple versions of templates for A/B testing or gradual rollouts:

templates/notify/
├── order-confirmed.html           ← default version
├── order-confirmed@v1.html        ← version v1
├── order-confirmed@v2.html        ← version v2
├── order-confirmed_pt_BR@v2.html  ← v2 with i18n
└── order-confirmed.txt            ← text default
// Use a specific version
notify.to(user).via(EMAIL)
    .template("order-confirmed")
    .templateVersion("v2")
    .param("orderId", "123")
    .send();

// No version = default template (backward compatible)
notify.to(user).via(EMAIL)
    .template("order-confirmed")
    .send();

// A/B testing
String version = abTestService.getVariant(user, "email-template");
notify.to(user).via(EMAIL)
    .template("welcome")
    .templateVersion(version)  // "v1" or "v2"
    .send();

Resolution order: {name}@{version}_{locale}.{variant}{name}@{version}.{variant}{name}_{locale}.{variant}{name}.{variant}

Custom Channels

Create your own channel by implementing one interface:

@Component
public class PushChannel implements NotificationChannel {

    @Override
    public String getName() { return "push"; }

    @Override
    public void send(Notification notification) {
        firebaseClient.send(notification.getRecipient(), notification.getRenderedContent());
    }

    @Override
    public boolean isAvailable() { return true; }
}

Use it:

notify.to(user)
    .via(Channel.custom("push"))
    .template("new-message")
    .send();

Spring Boot auto-discovers any NotificationChannel bean. No extra config needed.

Event Listeners + Spring Events

Monitor notification outcomes with the listener interface:

@Component
public class NotifyMonitor implements NotificationListener {

    @Override
    public void onSuccess(String channel, String template) {
        metrics.increment("notifications.sent." + channel);
    }

    @Override
    public void onFailure(String channel, String template, Exception error) {
        log.error("Failed on {}: {}", channel, error.getMessage());
        alertService.warn("Channel " + channel + " is failing");
    }

    @Override
    public void onScheduled(String channel, String recipient, Duration delay) {
        log.info("Scheduled for {} in {}", recipient, delay);
    }
}

Or use Spring Application Events (auto-configured):

@Component
public class NotificationEventHandler {

    @EventListener
    public void onSent(NotificationSentEvent event) {
        log.info("Sent via {} to {}", event.getChannel(), event.getRecipient());
    }

    @EventListener
    public void onFailed(NotificationFailedEvent event) {
        log.error("Failed: {}", event.getError().getMessage());
    }
}

Named Recipients

Send notifications to multiple destinations per channel using named aliases. Instead of one hardcoded webhook URL or chat ID, configure as many as you need:

Configure in

notify:
  channels:
    discord:
      webhook-url: ${DISCORD_DEFAULT}      # default destination
      username: NotifyHub
      avatar-url: https://example.com/logo.png
      recipients:
        alerts: https://discord.com/api/webhooks/111/aaa
        devops: https://discord.com/api/webhooks/222/bbb
        general: https://discord.com/api/webhooks/333/ccc

    slack:
      webhook-url: ${SLACK_DEFAULT}
      recipients:
        engineering: https://hooks.slack.com/services/XXX/YYY/ZZZ
        marketing: https://hooks.slack.com/services/AAA/BBB/CCC

    telegram:
      bot-token: ${TELEGRAM_BOT_TOKEN}
      chat-id: ${TELEGRAM_DEFAULT_CHAT}
      recipients:
        alerts: "-1001234567890"
        devops: "-1009876543210"

Use with the Java API:

// Send to a named alias
notify.to("alerts").via(DISCORD).content("Server is down!").send();
notify.to("engineering").via(SLACK).content("Deploy complete").send();
notify.to("devops").via(TELEGRAM).content("CPU at 95%").send();

// Send to default (no alias)
notify.to("user").via(DISCORD).content("Hello!").send();

// Pass a raw URL directly (no alias needed)
notify.to("https://discord.com/api/webhooks/444/ddd").via(DISCORD).content("Direct!").send();

Use with the MCP Server (AI Agents):

send_discord(recipient="alerts", body="Server is down!")
send_slack(recipient="engineering", body="Deploy complete")
send_telegram(recipient="devops", body="CPU at 95%")

Environment variables for MCP/Docker:

# Default webhook
NOTIFY_CHANNELS_DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/111/aaa

# Named recipients (RECIPIENTS_<NAME>)
NOTIFY_CHANNELS_DISCORD_RECIPIENTS_ALERTS=https://discord.com/api/webhooks/222/bbb
NOTIFY_CHANNELS_DISCORD_RECIPIENTS_DEVOPS=https://discord.com/api/webhooks/333/ccc

# Same pattern for all channels
NOTIFY_CHANNELS_SLACK_RECIPIENTS_ENGINEERING=https://hooks.slack.com/services/XXX
NOTIFY_CHANNELS_TELEGRAM_RECIPIENTS_ALERTS=-1001234567890
NOTIFY_CHANNELS_TEAMS_RECIPIENTS_GENERAL=https://outlook.office.com/webhook/XXX
NOTIFY_CHANNELS_GOOGLE_CHAT_RECIPIENTS_TEAM=https://chat.googleapis.com/v1/spaces/XXX

Resolution order: alias match in recipients map > raw URL/value passthrough > default from config.

Supported on: Discord, Slack, Telegram, Teams, Google Chat.

Message Queue (RabbitMQ / Kafka)

Decouple notification sending with async message queues. NotifyHub provides two modules:

RabbitMQ

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-queue-rabbitmq</artifactId>
    <version>1.0.0</version>
</dependency>
spring.rabbitmq.host: localhost
spring.rabbitmq.port: 5672

notify.queue.rabbitmq:
  enabled: true
  queue-name: notifyhub-notifications
  exchange-name: notifyhub-exchange
  routing-key: notification
  consumer:
    enabled: true
    concurrency: 1
    max-concurrency: 5
@Autowired RabbitNotificationProducer producer;

// Enqueue for async delivery
producer.enqueue(QueuedNotification.builder()
    .recipient("user@example.com")
    .channelName("email")
    .subject("Welcome!")
    .templateName("welcome")
    .params(Map.of("name", "Gabriel"))
    .build());
// Consumer picks it up and sends via NotifyHub automatically

Apache Kafka

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-queue-kafka</artifactId>
    <version>1.0.0</version>
</dependency>
spring.kafka.bootstrap-servers: localhost:9092

notify.queue.kafka:
  enabled: true
  topic: notifyhub-notifications
  consumer:
    enabled: true
    group-id: notifyhub-group
    concurrency: 1
@Autowired KafkaNotificationProducer producer;

// Same API as RabbitMQ — just different transport
producer.enqueue(QueuedNotification.builder()
    .recipient("+5548999999999")
    .channelName("sms")
    .rawContent("Your code is 1234")
    .priority("URGENT")
    .build());

Both modules support: templates, priority, deduplication keys, delivery tracking, and phone number routing for SMS/WhatsApp.

Circuit Breaker

Per-channel circuit breaker prevents cascading failures. If a channel fails repeatedly, the circuit opens and short-circuits further attempts:

// Without Spring Boot
NotifyHub notify = NotifyHub.builder()
    .channel(emailChannel)
    .circuitBreaker(CircuitBreakerConfig.defaults()) // 5 failures → open for 30s
    .build();

// Custom thresholds
NotifyHub notify = NotifyHub.builder()
    .channel(emailChannel)
    .circuitBreaker(CircuitBreakerConfig.custom()
        .failureThreshold(3)
        .openDuration(Duration.ofMinutes(1))
        .windowSize(Duration.ofSeconds(30))
        .build())
    .build();
# Spring Boot
notify:
  circuit-breaker:
    enabled: true
    failure-threshold: 5
    open-duration: 30s
    window-size: 60s

States: CLOSED (normal) → OPEN (rejecting) → HALF_OPEN (testing recovery). The health endpoint includes circuit breaker status per channel.

Bulkhead (Concurrency Isolation)

Limit concurrent sends per channel to prevent resource exhaustion:

NotifyHub notify = NotifyHub.builder()
    .channel(emailChannel)
    .bulkhead(BulkheadConfig.defaults())       // 10 concurrent per channel
    .bulkhead(BulkheadConfig.perChannel(5))    // or custom limit
    .build();

Multi-Channel Orchestration

Build escalation workflows that promote through channels if the user doesn't engage:

notify.to(user)
    .orchestrate()
    .first(Channel.EMAIL)
        .template("order-update")
    .ifNoOpen(Duration.ofHours(24))
    .then(Channel.PUSH)
        .content("You have an unread order update")
    .ifNoOpen(Duration.ofHours(48))
    .then(Channel.SMS)
        .content("Order update waiting — check your email")
    .execute();

Each step waits for the specified duration before escalating to the next channel.

A/B Testing

Built-in deterministic A/B testing for notifications. Variant assignment is hash-based (SHA-256) — the same recipient always gets the same variant:

notify.to(user)
    .via(Channel.EMAIL)
    .subject("Welcome!")
    .abTest("welcome-experiment")
        .variant("control", b -> b.template("welcome-v1"))
        .variant("new-design", b -> b.template("welcome-v2"))
        .split(50, 50);

Supports any number of variants with weighted splits. Deterministic hashing ensures consistent experiences across sends.

Cron Scheduling

Schedule recurring notifications with cron expressions:

ScheduledNotification job = notify.to(user)
    .via(Channel.EMAIL)
    .template("weekly-digest")
    .cron("0 9 * * MON"); // Every Monday at 9 AM

Supports standard 5-field cron syntax: minute, hour, day-of-month, month, day-of-week. Includes ranges, lists, steps, and named days/months.

Quiet Hours

Respect user preferences for notification timing:

public class User implements Notifiable {
    @Override
    public QuietHours getQuietHours() {
        return QuietHours.between(
            LocalTime.of(22, 0),  // 10 PM
            LocalTime.of(8, 0),   // 8 AM
            ZoneId.of("America/Sao_Paulo")
        );
    }

    @Override
    public Set<Channel> getOptedOutChannels() {
        return Set.of(Channel.SMS); // User opted out of SMS
    }
}

Notifications sent during quiet hours are delayed to the next allowed window. Opted-out channels are silently skipped.

Testing Utilities

TestNotifyHub provides a test-friendly wrapper with capturing channels for all built-in channel types:

@Test
void shouldSendWelcomeEmail() {
    TestNotifyHub test = TestNotifyHub.create();

    test.to("user@test.com")
        .via(Channel.EMAIL)
        .subject("Welcome")
        .content("Hello!")
        .send();

    assertThat(test.sent()).hasSize(1);
    assertThat(test.sent("email").get(0).subject()).isEqualTo("Welcome");
}

No mocking needed — TestNotifyHub captures all notifications in memory for assertions. Call test.reset() between tests.


Supported Channels

Channel

Provider

Module

Email

SMTP (Gmail, SES, Outlook, any)

notify-email

📱

SMS

Twilio

notify-sms

WhatsApp

Twilio

notify-sms

Slack

Incoming Webhooks

notify-slack

Telegram

Bot API

notify-telegram

Discord

Webhooks

notify-discord

👥

Microsoft Teams

Incoming Webhooks

notify-teams

Push (FCM)

Firebase Cloud Messaging

notify-push-firebase

🔗

Webhook

Any HTTP endpoint

notify-webhook

WebSocket

JDK WebSocket (java.net.http)

notify-websocket

Google Chat

Webhooks

notify-google-chat

Twitter/X

API v2 (OAuth 1.0a)

notify-twitter

💼

LinkedIn

REST API (OAuth 2.0)

notify-linkedin

Notion

API (Integration Token)

notify-notion

Twitch

Helix API (OAuth 2.0 auto-refresh)

notify-twitch

YouTube

Data API v3 (OAuth auto-refresh)

notify-youtube

Instagram

Meta Graph API

notify-instagram

📧

SendGrid

SendGrid API (delivery tracking)

notify-sendgrid

TikTok Shop

TikTok Shop API (HMAC-SHA256)

notify-tiktok-shop

Facebook

Graph API (Page + Messenger)

notify-facebook

WhatsApp Cloud

Meta Cloud API (direct, no Twilio)

notify-whatsapp

☁️

AWS SNS

AWS SDK v2

notify-aws-sns

Mailgun

Mailgun REST API

notify-mailgun

PagerDuty

Events API v2

notify-pagerduty

Custom

Any — implement one interface

notify-core


Admin Dashboard

NotifyHub includes a built-in admin dashboard for monitoring your notification system.

notify:
  admin:
    enabled: true
<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-admin</artifactId>
    <version>1.0.0</version>
</dependency>

Access at /notify-admin to see:

  • Dashboard — metric cards (sent/failed/pending/DLQ/channels/contacts), recent activity feed, system status grid, registered channels

  • Analytics — Chart.js charts with send volume, channel distribution, success rate, hourly heatmap

  • Tracking — delivery receipts with channel filter and status badges

  • Dead Letter Queue — failed notifications with error details and remove action

  • Channels — status of each registered channel with health indicators

  • Audit Log — complete history of all notification events with event type filter

  • Audiences — manage contacts and audience segments with tag-based filtering

  • Status Webhook — real-time HTTP callback configuration and delivery history

Dark/light theme toggle included — persists across pages via localStorage.


Spring Boot Integration

Micrometer Metrics

Auto-configured when Micrometer is on the classpath:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-core</artifactId>
</dependency>

Exposes counters and gauges via the unified EventBus:

  • notifyhub.notifications.sent (tags: channel)

  • notifyhub.notifications.failed (tags: channel)

  • notifyhub.notifications.retried (tags: channel)

  • notifyhub.notifications.rate_limited (tags: channel)

  • notifyhub.notifications.deduped (tags: channel)

  • notifyhub.notifications.circuit_opened (tags: channel)

  • notifyhub.notifications.circuit_closed (tags: channel)

  • notifyhub.notifications.send_duration (timer, tags: channel)

Actuator Health Check

Auto-configured when Spring Boot Actuator is on the classpath:

GET /actuator/health/notifyhub
{
  "status": "UP",
  "details": {
    "email": { "status": "UP", "circuitBreaker": "CLOSED" },
    "slack": { "status": "UP", "circuitBreaker": "CLOSED" },
    "totalChannels": 2,
    "availableChannels": 2
  }
}

Status: UP (all channels available), DEGRADED (some down), DOWN (all down). When circuit breaker is configured, each channel also reports its circuit state.

Actuator Info

GET /actuator/info
{
  "notifyhub": {
    "version": "1.0.0",
    "channels": ["email", "slack", "teams"],
    "tracking.enabled": true,
    "dlq.enabled": true
  }
}

OpenTelemetry Tracing

Auto-configured when Micrometer Observation and an OTel bridge are on the classpath. Add these dependencies to export distributed traces via OTLP:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>

Configure the OTLP endpoint in application.yml:

management:
  tracing:
    sampling:
      probability: 1.0       # 100% sampling (adjust for production)
  otlp:
    tracing:
      endpoint: http://localhost:4318/v1/traces

Creates observations (spans):

  • notifyhub.send (tags: channel, template, outcome)

  • notifyhub.schedule (tags: channel, outcome)

Compatible with Jaeger, Zipkin, Grafana Tempo, Datadog, and any OTLP-compatible collector.

Webhook HMAC Signing

The Status Webhook listener supports HMAC-SHA256 request signing for security. When configured, every webhook POST includes a X-NotifyHub-Signature header that your server can use to verify the request came from NotifyHub.

notify:
  status-webhook:
    url: https://your-server.com/webhook
    signing-secret: ${WEBHOOK_SECRET}   # any secret string

Each request includes the header:

X-NotifyHub-Signature: sha256=<hex-encoded HMAC-SHA256 of request body>

Verify in your server:

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
String expected = "sha256=" + HexFormat.of().formatHex(mac.doFinal(body.getBytes()));
boolean valid = MessageDigest.isEqual(expected.getBytes(), signature.getBytes());

Configuration Reference

Full application.yml with all options:

notify:
  channels:
    email:
      host: smtp.gmail.com
      port: 587
      username: ${GMAIL_USER}
      password: ${GMAIL_PASS}
      from: noreply@myapp.com
      from-name: MyApp
      tls: true
      ssl: false

    sms:
      account-sid: ${TWILIO_SID}
      auth-token: ${TWILIO_TOKEN}
      from-number: "+1234567890"

    whatsapp:
      account-sid: ${TWILIO_SID}
      auth-token: ${TWILIO_TOKEN}
      from-number: "+14155238886"

    slack:
      webhook-url: ${SLACK_WEBHOOK}
      recipients:                          # named aliases (optional)
        engineering: https://hooks.slack.com/services/XXX
        marketing: https://hooks.slack.com/services/YYY

    telegram:
      bot-token: ${TELEGRAM_BOT_TOKEN}
      chat-id: ${TELEGRAM_CHAT_ID}
      recipients:                          # named aliases (optional)
        alerts: "-1001234567890"
        devops: "-1009876543210"

    discord:
      webhook-url: ${DISCORD_WEBHOOK}
      username: NotifyHub
      avatar-url: https://example.com/logo.png
      recipients:                          # named aliases (optional)
        alerts: https://discord.com/api/webhooks/111/aaa
        devops: https://discord.com/api/webhooks/222/bbb

    teams:
      webhook-url: ${TEAMS_WEBHOOK}
      recipients:                          # named aliases (optional)
        general: https://outlook.office.com/webhook/XXX

    push:
      credentials-path: ${FIREBASE_CREDENTIALS}
      project-id: ${FIREBASE_PROJECT_ID}

    webhooks:
      - name: pagerduty
        url: https://events.pagerduty.com/v2/enqueue
        headers:
          Authorization: "Token ${PAGERDUTY_TOKEN}"
        payload-template: '{"summary":"{{content}}"}'

    websocket:
      uri: wss://echo.example.com/ws
      timeout-ms: 10000
      reconnect-enabled: true
      reconnect-delay-ms: 5000
      max-reconnect-attempts: 3
      headers:
        Authorization: "Bearer ${WS_TOKEN}"
      message-format: '{"type":"notification","content":"{{content}}"}'

    google-chat:
      webhook-url: ${GOOGLE_CHAT_WEBHOOK}
      timeout-ms: 10000
      recipients:                          # named aliases (optional)
        team-a: https://chat.googleapis.com/v1/spaces/XXX/messages?key=YYY
        team-b: https://chat.googleapis.com/v1/spaces/ZZZ/messages?key=WWW

  retry:
    max-attempts: 3
    strategy: exponential

  rate-limit:
    enabled: true
    max-requests: 100
    window: 1m
    channels:
      email:
        max-requests: 50
        window: 1m

  tracking:
    enabled: true
    type: memory         # memory | jpa
    dlq-enabled: true

  deduplication:
    enabled: true
    ttl: 24h
    strategy: content-hash  # content-hash | explicit-key | both

  admin:
    enabled: true

Without Spring Boot

NotifyHub works without Spring — use the builder directly:

NotifyHub notify = NotifyHub.builder()
    .templateEngine(new MustacheTemplateEngine())
    .channel(new SmtpEmailChannel(
        SmtpConfig.builder()
            .host("smtp.gmail.com").port(587)
            .username("user@gmail.com").password("app-password")
            .from("noreply@myapp.com").tls(true)
            .build()
    ))
    .channel(new SlackChannel(
        SlackConfig.builder()
            .webhookUrl("https://hooks.slack.com/services/XXX/YYY/ZZZ")
            .recipients(Map.of("engineering", "https://hooks.slack.com/services/AAA/BBB/CCC"))
            .build()
    ))
    .channel(new TeamsChannel(
        TeamsConfig.builder()
            .webhookUrl("https://outlook.office.com/webhook/XXX/YYY/ZZZ")
            .build()
    ))
    .channel(new WebhookChannel(
        WebhookConfig.builder()
            .name("pagerduty")
            .url("https://events.pagerduty.com/v2/enqueue")
            .payloadTemplate("{\"summary\":\"{{content}}\"}")
            .build()
    ))
    .channel(new WebSocketChannel(
        WebSocketConfig.builder()
            .uri("wss://echo.example.com/ws")
            .messageFormat("{\"text\":\"{{content}}\"}")
            .build()
    ))
    .channel(new GoogleChatChannel(
        GoogleChatConfig.builder()
            .webhookUrl("https://chat.googleapis.com/v1/spaces/XXX/messages?key=YYY")
            .build()
    ))
    .deduplicationStore(new InMemoryDeduplicationStore(Duration.ofHours(24)))
    .defaultRetryPolicy(RetryPolicy.exponential(3))
    .rateLimiter(new TokenBucketRateLimiter(
        RateLimitConfig.perMinute(100)))
    .deadLetterQueue(new InMemoryDeadLetterQueue())
    .tracker(new InMemoryNotificationTracker())
    .circuitBreaker(CircuitBreakerConfig.defaults())
    .bulkhead(BulkheadConfig.defaults())
    .build();

// Sync
notify.to("user@email.com")
    .via(Channel.EMAIL)
    .subject("Hello!")
    .content("Welcome to the app!")
    .send();

// Async
notify.to("#general")
    .via(Channel.SLACK)
    .content("Deploy complete!")
    .sendAsync();

// Tracked
DeliveryReceipt receipt = notify.to(user)
    .via(Channel.EMAIL)
    .content("Invoice attached")
    .sendTracked();

// Scheduled
notify.to(user)
    .via(Channel.EMAIL)
    .content("Reminder!")
    .schedule(Duration.ofMinutes(30));

// Batch
notify.toAll(List.of("a@test.com", "b@test.com"))
    .via(Channel.EMAIL)
    .template("announcement")
    .send();

Only notify-core + channel modules needed. No Spring dependency.


MCP Server (AI Agents)

NotifyHub includes an MCP (Model Context Protocol) server that exposes all notification channels as tools for AI agents like Claude Desktop, Claude Code, Cursor, and any MCP-compatible client.

How it works

The notify-mcp module is a standalone Java application that communicates via STDIO using the JSON-RPC protocol. AI agents discover the available tools and can send notifications through any configured channel.

Setup

1. Build the MCP server:

mvn clean package -pl notify-mcp -am -DskipTests

2. Configure in Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "notify-hub": {
      "command": "java",
      "args": ["-jar", "path/to/notify-mcp-1.0.0.jar"],
      "env": {
        "NOTIFY_CHANNELS_EMAIL_HOST": "smtp.gmail.com",
        "NOTIFY_CHANNELS_EMAIL_PORT": "587",
        "NOTIFY_CHANNELS_EMAIL_USERNAME": "you@gmail.com",
        "NOTIFY_CHANNELS_EMAIL_PASSWORD": "app-password",
        "NOTIFY_CHANNELS_SLACK_WEBHOOK_URL": "https://hooks.slack.com/...",
        "NOTIFY_CHANNELS_DISCORD_WEBHOOK_URL": "https://discord.com/api/webhooks/..."
      }
    }
  }
}

Or for Claude Code (.mcp.json in project root):

{
  "mcpServers": {
    "notify-hub": {
      "command": "java",
      "args": ["-jar", "path/to/notify-mcp-1.0.0.jar"],
      "env": {
        "NOTIFY_CHANNELS_DISCORD_WEBHOOK_URL": "https://discord.com/api/webhooks/..."
      }
    }
  }
}

Available MCP Tools

Tool

Description

Required Params

send_notification

Send via any channel (generic)

channel, recipient, body or template

send_email

Send email

to, body or template

send_sms

Send SMS via Twilio

phone, body or template

send_slack

Send to Slack channel

recipient, body or template

send_telegram

Send via Telegram Bot

recipient, body or template

send_discord

Send to Discord channel

recipient, body or template

send_whatsapp

Send WhatsApp via Twilio

phone, body or template

send_teams

Send to Microsoft Teams

recipient, body or template

send_google_chat

Send to Google Chat

recipient, body or template

send_push

Send push via Firebase

push_token, body

send_twitter

Post a tweet on Twitter/X

body or template

send_linkedin

Publish a post on LinkedIn

body or template

send_notion

Create a page in Notion

recipient, body or template

send_twitch

Send Twitch chat message + polls

recipient, body or template

send_youtube

Send YouTube live chat message

recipient, body or template

send_instagram

Send Instagram DM or feed post

recipient, body or template

send_multi_channel

Send to multiple channels

channels[], recipient, body or template

send_batch

Send to multiple recipients at once

recipients[], channel, body or template

send_to_audience

Send to a named audience

audience, channel, body or template

list_channels

List configured channels

(none)

list_delivery_receipts

Query delivery history

(none)

list_dead_letters

View failed notifications (DLQ)

(none)

create_contact

Create a contact with tags

name

list_contacts

List contacts (filter by tag)

(none)

create_audience

Create audience with tag filters

name, tags[]

list_audiences

List audiences with contact counts

(none)

get_analytics

Delivery stats by channel/status

(none)

send_tiktok_shop

Send TikTok Shop notification

recipient, body or template

send_facebook

Send Facebook page post or Messenger DM

recipient, body or template

check_email_status

Check SendGrid email delivery status

message_id

schedule_notification

Schedule a notification for later delivery

channel, recipient, body, send_at

list_scheduled_notifications

List all scheduled notifications

(none)

cancel_scheduled_notification

Cancel a pending scheduled notification

notification_id

All send tools optionally accept: subject, template, params, priority.

Usage example (from an AI agent)

Once configured, you can simply ask your AI agent:

"Send a Discord message to #alerts saying the deploy is complete"

The agent will call the send_discord tool with the appropriate parameters.

Docker

Run the MCP server without Java installed — only Docker required:

# Build
docker build -t notifyhub-mcp .

# Run with Discord
docker run -i --rm \
  -e NOTIFY_CHANNELS_DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..." \
  -e NOTIFY_CHANNELS_DISCORD_USERNAME="NotifyHub" \
  gabrielbbal10/notifyhub-mcp

# Run with multiple Discord channels + Email
docker run -i --rm \
  -e NOTIFY_CHANNELS_DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..." \
  -e NOTIFY_CHANNELS_DISCORD_USERNAME="NotifyHub" \
  -e NOTIFY_CHANNELS_DISCORD_RECIPIENTS_ALERTS="https://discord.com/api/webhooks/111/aaa" \
  -e NOTIFY_CHANNELS_DISCORD_RECIPIENTS_DEVOPS="https://discord.com/api/webhooks/222/bbb" \
  -e NOTIFY_CHANNELS_EMAIL_HOST="smtp.gmail.com" \
  -e NOTIFY_CHANNELS_EMAIL_PORT="587" \
  -e NOTIFY_CHANNELS_EMAIL_USERNAME="you@gmail.com" \
  -e NOTIFY_CHANNELS_EMAIL_PASSWORD="app-password" \
  -e NOTIFY_CHANNELS_EMAIL_FROM="you@gmail.com" \
  gabrielbbal10/notifyhub-mcp

Configure in Claude Code (.mcp.json):

{
  "mcpServers": {
    "notify-hub": {
      "command": "docker",
      "args": ["run", "-i", "--rm",
        "-e", "NOTIFY_CHANNELS_DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...",
        "-e", "NOTIFY_CHANNELS_DISCORD_USERNAME=NotifyHub",
        "-e", "NOTIFY_CHANNELS_DISCORD_RECIPIENTS_ALERTS=https://discord.com/api/webhooks/111/aaa",
        "gabrielbbal10/notifyhub-mcp"
      ]
    }
  }
}

Docker REST API

Run a full REST API with Swagger UI — no Java required, just Docker:

docker run -d -p 8080:8080 \
  -e NOTIFY_CHANNELS_EMAIL_USERNAME=you@gmail.com \
  -e NOTIFY_CHANNELS_EMAIL_PASSWORD=your-app-password \
  -e NOTIFY_CHANNELS_EMAIL_FROM=you@gmail.com \
  gabrielbbal10/notifyhub-api:latest

Open http://localhost:8080/swagger-ui.html for interactive API docs.

Environment variables — same for both MCP and API images, add only the channels you need:

Channel

Variable

Required

Example

Email

NOTIFY_CHANNELS_EMAIL_HOST

No (default: smtp.gmail.com)

smtp.gmail.com

NOTIFY_CHANNELS_EMAIL_PORT

No (default: 587)

587

NOTIFY_CHANNELS_EMAIL_USERNAME

Yes (for email)

you@gmail.com

NOTIFY_CHANNELS_EMAIL_PASSWORD

Yes (for email)

abcd efgh ijkl mnop (App Password)

NOTIFY_CHANNELS_EMAIL_FROM

Yes (for email)

you@gmail.com

Discord

NOTIFY_CHANNELS_DISCORD_WEBHOOK_URL

Yes (for discord)

https://discord.com/api/webhooks/...

NOTIFY_CHANNELS_DISCORD_USERNAME

No

NotifyHub

NOTIFY_CHANNELS_DISCORD_AVATAR_URL

No

https://example.com/avatar.png

NOTIFY_CHANNELS_DISCORD_RECIPIENTS_<NAME>

No

Named alias webhook URL

Slack

NOTIFY_CHANNELS_SLACK_WEBHOOK_URL

Yes (for slack)

https://hooks.slack.com/services/...

NOTIFY_CHANNELS_SLACK_RECIPIENTS_<NAME>

No

Named alias webhook URL

Telegram

NOTIFY_CHANNELS_TELEGRAM_BOT_TOKEN

Yes (for telegram)

123456:ABC-DEF...

NOTIFY_CHANNELS_TELEGRAM_CHAT_ID

Yes (for telegram)

123456789

NOTIFY_CHANNELS_TELEGRAM_RECIPIENTS_<NAME>

No

Named alias chat ID

Google Chat

NOTIFY_CHANNELS_GOOGLE_CHAT_WEBHOOK_URL

Yes (for gchat)

https://chat.googleapis.com/v1/spaces/...

NOTIFY_CHANNELS_GOOGLE_CHAT_RECIPIENTS_<NAME>

No

Named alias webhook URL

Teams

NOTIFY_CHANNELS_TEAMS_WEBHOOK_URL

Yes (for teams)

https://outlook.office.com/webhook/...

NOTIFY_CHANNELS_TEAMS_RECIPIENTS_<NAME>

No

Named alias webhook URL

SMS

NOTIFY_CHANNELS_SMS_ACCOUNT_SID

Yes (for sms)

Twilio Account SID

NOTIFY_CHANNELS_SMS_AUTH_TOKEN

Yes (for sms)

Twilio Auth Token

NOTIFY_CHANNELS_SMS_FROM_NUMBER

Yes (for sms)

+12025551234

WhatsApp

NOTIFY_CHANNELS_WHATSAPP_ACCOUNT_SID

Yes (for whatsapp)

Twilio Account SID

NOTIFY_CHANNELS_WHATSAPP_AUTH_TOKEN

Yes (for whatsapp)

Twilio Auth Token

NOTIFY_CHANNELS_WHATSAPP_FROM_NUMBER

Yes (for whatsapp)

+14155238886

Example with multiple channels:

docker run -d -p 8080:8080 \
  -e NOTIFY_CHANNELS_EMAIL_USERNAME=you@gmail.com \
  -e NOTIFY_CHANNELS_EMAIL_PASSWORD=your-app-password \
  -e NOTIFY_CHANNELS_EMAIL_FROM=you@gmail.com \
  -e NOTIFY_CHANNELS_DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..." \
  -e NOTIFY_CHANNELS_DISCORD_USERNAME="NotifyHub" \
  -e NOTIFY_CHANNELS_TELEGRAM_BOT_TOKEN="123456:ABC-DEF..." \
  -e NOTIFY_CHANNELS_TELEGRAM_CHAT_ID="123456789" \
  -e NOTIFY_CHANNELS_GOOGLE_CHAT_WEBHOOK_URL="https://chat.googleapis.com/v1/spaces/..." \
  gabrielbbal10/notifyhub-api:latest

Usage from any language:

# Send email
curl -X POST "http://localhost:8080/send/email?to=user@example.com&subject=Hello&body=Hi!"

# Send Discord
curl -X POST "http://localhost:8080/send/discord?message=Deploy done!"

# Send Telegram
curl -X POST "http://localhost:8080/send/telegram?chatId=123456789&message=Alert!"

# Send Google Chat
curl -X POST "http://localhost:8080/send/google-chat?message=Build passed!"

Docker Hub: gabrielbbal10/notifyhub-api


Running the Demo

The demo app showcases every feature with a built-in SMTP server — zero external config needed.

git clone https://github.com/GabrielBBaldez/notify-hub.git
cd notify-hub
mvn clean install -DskipTests

# Run the demo
mvn -pl notify-demo spring-boot:run

Then open:

Demo Endpoints

Method

Endpoint

Description

GET

/

Home — lists all endpoints

POST

/send/email

Send a simple email

POST

/send/template

Send email with Mustache template

POST

/send/notifiable

Send to a Notifiable entity

POST

/send/sms

Send SMS (requires Twilio)

POST

/send/whatsapp

Send WhatsApp (requires Twilio)

POST

/send/telegram

Send to Telegram via Bot

POST

/send/discord

Send to Discord via Webhook

POST

/send/slack

Send to Slack channel

POST

/send/teams

Send to Microsoft Teams via Webhook

POST

/send/google-chat

Send to Google Chat via Webhook

POST

/send/push

Send push notification via Firebase

POST

/send/websocket

Send message via WebSocket

POST

/send/multi

Send to email + Slack simultaneously

POST

/send/fallback

Test fallback (email fails -> Slack)

POST

/send/tracked

Send with delivery tracking

POST

/send/scheduled

Schedule notification for future

GET

/tracking

Delivery tracking history

GET

/notify-admin

Admin dashboard

GET

/inbox

View captured emails

DELETE

/inbox

Clear all inboxes


Architecture

notify-hub/
├── notify-core/                          # Zero Spring dependency
│   ├── NotifyHub                         # Thin facade — delegates to executor/scheduler/eventbus
│   ├── NotificationExecutor              # Channel resolution, send logic, fallback chains
│   ├── NotificationScheduler             # Scheduling with delay/cancel/list
│   ├── NotificationBuilder               # Fluent builder (send/async/tracked/scheduled)
│   ├── BatchNotificationBuilder          # Batch send to multiple recipients
│   ├── Notification                      # Immutable notification object
│   ├── Channel / ChannelRef              # Built-in + custom channel refs
│   ├── Priority                          # URGENT, HIGH, NORMAL, LOW
│   ├── Notifiable                        # Recipient interface (i18n + routing + quiet hours)
│   ├── NotificationChannel               # Channel SPI (implement this!)
│   ├── QuietHours                        # Per-recipient quiet time windows
│   ├── pipeline/                         # Resilience handler chain
│   │   ├── SendPipeline                  # Assembles: Dedup → RateLimit → CircuitBreaker → Template → Retry
│   │   ├── SendHandler                   # Handler chain interface
│   │   ├── SendContext                   # Per-send state (channel, builder, notification)
│   │   ├── DeduplicationHandler          # Skip duplicates
│   │   ├── RateLimitHandler              # Enforce per-channel limits
│   │   ├── CircuitBreakerHandler         # Short-circuit failing channels
│   │   ├── TemplateHandler               # Build + render notification
│   │   └── RetrySendHandler              # Terminal: retry with backoff → DLQ
│   ├── resilience/                       # Resilience primitives
│   │   ├── ChannelCircuitBreaker         # Per-channel circuit breaker (sliding window)
│   │   ├── CircuitBreakerConfig          # Thresholds + durations
│   │   ├── CircuitState                  # CLOSED, OPEN, HALF_OPEN
│   │   └── BulkheadConfig               # Per-channel concurrency limits
│   ├── event/                            # Unified event system
│   │   ├── NotificationEventBus          # Publish events to listeners
│   │   ├── NotificationEvent             # Immutable event record
│   │   ├── EventType                     # SENT, FAILED, RETRIED, RATE_LIMITED, DEDUPED, CIRCUIT_*
│   │   ├── NotificationEventListener     # Listener interface
│   │   └── LegacyListenerAdapter         # Bridge: old NotificationListener → EventBus
│   ├── orchestration/                    # Multi-step notification workflows
│   │   ├── OrchestrationBuilder          # first(EMAIL).ifNoOpen(24h).then(PUSH)
│   │   └── OrchestrationStep            # Individual step record
│   ├── abtest/                           # A/B testing
│   │   └── AbTestBuilder                # Deterministic SHA-256 variant selection
│   ├── schedule/                         # Cron support
│   │   └── CronExpression               # Lightweight 5-field cron parser
│   ├── attachment/                       # File attachments
│   │   └── Attachment                    # Email file attachments
│   ├── testing/                          # Test utilities
│   │   ├── TestNotifyHub                 # Test wrapper with capturing channels
│   │   └── SentNotification             # Captured notification record
│   ├── RateLimiter / TokenBucket         # Rate limiting
│   ├── NotificationRouter / RoutingRule  # Conditional routing
│   ├── MustacheTemplateEngine            # Template engine (i18n-aware + versioning)
│   ├── DeduplicationStore                # Dedup interface (in-memory impl)
│   └── RetryPolicy                       # Retry + backoff strategies
│
├── notify-channels/
│   ├── notify-email/                     # SMTP email (Jakarta Mail + attachments)
│   ├── notify-sms/                       # Twilio SMS + WhatsApp
│   ├── notify-slack/                     # Slack webhooks (JDK HttpClient)
│   ├── notify-telegram/                  # Telegram Bot API (JDK HttpClient)
│   ├── notify-discord/                   # Discord webhooks (JDK HttpClient)
│   ├── notify-teams/                     # Microsoft Teams webhooks (JDK HttpClient)
│   ├── notify-push-firebase/             # Firebase Cloud Messaging (FCM)
│   ├── notify-webhook/                   # Generic webhook (configurable)
│   ├── notify-websocket/                 # WebSocket (JDK java.net.http)
│   ├── notify-google-chat/              # Google Chat webhooks (JDK HttpClient)
│   ├── notify-twitch/                   # Twitch chat + polls via Helix API (JDK HttpClient)
│   ├── notify-youtube/                  # YouTube live chat via Data API v3 (JDK HttpClient)
│   ├── notify-instagram/               # Instagram DM + feed via Meta Graph API (JDK HttpClient)
│   ├── notify-sendgrid/                # SendGrid email with delivery tracking (JDK HttpClient)
│   ├── notify-tiktok-shop/             # TikTok Shop API (HMAC-SHA256, JDK HttpClient)
│   ├── notify-facebook/                # Facebook Graph API (Page + Messenger)
│   ├── notify-whatsapp/                # WhatsApp Cloud API (Meta Graph API, no Twilio)
│   ├── notify-aws-sns/                 # AWS SNS (AWS SDK v2)
│   ├── notify-mailgun/                 # Mailgun transactional email (JDK HttpClient)
│   ├── notify-pagerduty/              # PagerDuty Events API v2 (JDK HttpClient)
│   └── notify-channel-template/       # Template/archetype for creating new channels
│
├── notify-tracker-jpa/                   # JPA-backed delivery tracker
├── notify-audit-jpa/                     # JPA-backed audit logging
│
├── notify-spring-boot-starter/           # Auto-config for Spring Boot
│   ├── NotifyAutoConfiguration           # Auto-discovers all channels + event listeners
│   ├── MetricsEventListener              # Micrometer metrics via EventBus
│   ├── TracingNotificationListener       # OpenTelemetry tracing (Observation API)
│   ├── NotifyHubHealthIndicator          # Actuator health (+ circuit breaker status)
│   ├── NotifyHubInfoContributor          # Actuator info endpoint
│   ├── SpringEventNotificationListener   # Spring ApplicationEvents
│   └── NotifyProperties                  # application.yml binding
│
├── notify-admin/                         # Admin dashboard (Thymeleaf)
│   └── NotifyAdminController             # /notify-admin/*
│
├── notify-mcp/                           # MCP Server for AI agents
│   ├── NotifyMcpServer                   # Spring Boot headless main
│   ├── McpServerRunner                   # STDIO MCP server bootstrap
│   └── tools/                            # 36 MCP tools (send, batch, audiences, DLQ, analytics)
│
├── notify-queue-rabbitmq/                # RabbitMQ integration
├── notify-queue-kafka/                   # Kafka integration
│
└── notify-demo/                          # Demo app (run it!)

Resilience Pipeline

Every notification passes through a handler chain before hitting the channel:

Deduplication → Rate Limit → Circuit Breaker → Template Rendering → Retry + Send → DLQ

Each handler can short-circuit (e.g., dedup skips duplicates, circuit breaker rejects when open). The pipeline is fully optional — handlers are skipped when their dependency is null.

Design Principles

  • notify-core — use it in any Java project

  • Channels are pluggable — implement NotificationChannel, register as a Spring bean

  • Slack, Telegram, Discord, Teams, WebSocket, Google Chat use zero external SDKs — only JDK java.net.http.HttpClient

  • Template engine is replaceable — implement TemplateEngine interface

  • Spring Boot starter auto-configures everything — just add the dependency

  • Async supportsendAsync() and sendAllAsync() with CompletableFuture

  • Conditional auto-config — channel beans only load when their module is on classpath

  • Unified event systemNotificationEventBus replaces scattered listener calls, backward-compatible via LegacyListenerAdapter


Maven Central

NotifyHub is published on Maven Central. No extra repositories needed.

Search on Maven Central: io.github.gabrielbbaldez

Available Modules

Below is every module, what it does, when you need it, and how to add it.


notify-spring-boot-starter — The Main Dependency

What it does: Auto-configures NotifyHub inside a Spring Boot application. Automatically discovers channel beans, wires retry policies, tracking, rate limiting, DLQ, Micrometer metrics, OpenTelemetry tracing, Actuator health checks, and Spring events. Includes notify-core and notify-email transitively.

When to use: You're building a Spring Boot app and want automatic setup. This is the only required dependency for most projects.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

notify-core — Core API (No Spring)

What it does: Contains the entire fluent API (NotifyHub, NotificationBuilder, Channel, Notification, Priority, Attachment, RetryPolicy), plus interfaces for channels, templates, tracking, DLQ, rate limiting, and routing. Has zero Spring dependency — uses only SLF4J and Mustache.

When to use: You want to use NotifyHub in a plain Java project without Spring Boot, or you're building a library/framework on top of it.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-core</artifactId>
    <version>1.0.0</version>
</dependency>

notify-email — SMTP Email Channel

What it does: Sends emails via any SMTP server (Gmail, Outlook, Amazon SES, Mailtrap, etc). Supports HTML and plain text, file attachments, TLS/SSL, and custom sender name. Uses Jakarta Mail internally.

When to use: You want to send email notifications. Already included by notify-spring-boot-starter.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-email</artifactId>
    <version>1.0.0</version>
</dependency>

notify-sms — Twilio SMS + WhatsApp Channel

What it does: Sends SMS and WhatsApp messages through the Twilio API. Handles phone number formatting (E.164) and the whatsapp: prefix automatically.

When to use: You need to send SMS or WhatsApp messages. Requires a Twilio account with Account SID, Auth Token, and a phone number.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-sms</artifactId>
    <version>1.0.0</version>
</dependency>

notify-slack — Slack Webhook Channel

What it does: Sends messages to a Slack channel via Incoming Webhooks. Uses the JDK HttpClient — no external SDK needed.

When to use: You want to post notifications to Slack. Requires a Slack Incoming Webhook URL (created at api.slack.com/apps).

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-slack</artifactId>
    <version>1.0.0</version>
</dependency>

notify-telegram — Telegram Bot Channel

What it does: Sends messages to Telegram chats/groups/channels via the Bot API. Supports a default chat ID and per-notification targeting. Uses the JDK HttpClient.

When to use: You want to send Telegram messages. Requires a bot token from @BotFather.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-telegram</artifactId>
    <version>1.0.0</version>
</dependency>

notify-discord — Discord Webhook Channel

What it does: Sends messages to a Discord channel via Webhooks. Supports custom bot username and avatar. Uses the JDK HttpClient.

When to use: You want to post notifications to Discord. Requires a Discord webhook URL (channel Settings > Integrations > Webhooks).

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-discord</artifactId>
    <version>1.0.0</version>
</dependency>

notify-teams — Microsoft Teams Channel

What it does: Sends MessageCard notifications to a Teams channel via Incoming Webhooks. Uses the JDK HttpClient.

When to use: You want to post notifications to Microsoft Teams. Requires a Teams Incoming Webhook URL (channel > Connectors > Incoming Webhook).

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-teams</artifactId>
    <version>1.0.0</version>
</dependency>

notify-push-firebase — Firebase Cloud Messaging (FCM)

What it does: Sends push notifications to mobile devices (Android/iOS) and web apps via Firebase Cloud Messaging. Uses the Firebase Admin SDK with service account credentials.

When to use: You want to send push notifications to mobile apps. Requires a Firebase project with a service account JSON credentials file.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-push-firebase</artifactId>
    <version>1.0.0</version>
</dependency>

notify-webhook — Generic Webhook Channel

What it does: Sends notifications to any HTTP endpoint (REST APIs, PagerDuty, Datadog, custom services). Supports configurable payload templates with {{recipient}}, {{subject}}, {{content}} placeholders, custom headers, PUT/POST methods, and timeouts.

When to use: You want to integrate with any external service that has an HTTP API, or create custom webhook integrations.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-webhook</artifactId>
    <version>1.0.0</version>
</dependency>

notify-websocket — WebSocket Channel

What it does: Sends notifications over WebSocket connections using the JDK java.net.http.WebSocket API. Supports configurable message format with {{recipient}}, {{subject}}, {{content}} placeholders, custom headers, auto-reconnect with backoff, and connection timeout. Zero external dependencies.

When to use: You want to send real-time notifications over WebSocket to a server (e.g., live dashboards, chat systems, or custom WebSocket consumers).

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-websocket</artifactId>
    <version>1.0.0</version>
</dependency>

notify-google-chat — Google Chat Channel

What it does: Sends messages to Google Chat spaces via Incoming Webhooks. Posts JSON payloads using the JDK HttpClient — no external SDK needed.

When to use: You want to send notifications to a Google Chat space. Requires a Google Chat webhook URL (space Settings > Apps & integrations > Webhooks).

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-google-chat</artifactId>
    <version>1.0.0</version>
</dependency>

notify-twitch — Twitch Channel

What it does: Sends chat messages and polls to Twitch channels via the Helix API. Uses the JDK HttpClient — no external SDK needed.

When to use: You want to send notifications to Twitch chat or create polls. Requires Twitch API OAuth 2.0 credentials (Client ID + OAuth token).

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-twitch</artifactId>
    <version>1.0.0</version>
</dependency>

notify-youtube — YouTube Channel

What it does: Sends live chat messages to YouTube live streams via the YouTube Data API v3. Uses the JDK HttpClient — no external SDK needed.

When to use: You want to send notifications to YouTube live chat. Requires a Google API key or OAuth 2.0 credentials with YouTube Data API v3 enabled.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-youtube</artifactId>
    <version>1.0.0</version>
</dependency>

notify-instagram — Instagram Channel

What it does: Sends DMs and feed posts via the Meta Graph API (Instagram Graph API). Uses the JDK HttpClient — no external SDK needed.

When to use: You want to send notifications via Instagram. Requires a Facebook Developer account with Instagram Graph API access.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-instagram</artifactId>
    <version>1.0.0</version>
</dependency>

notify-sendgrid — SendGrid Email Channel

What it does: Sends transactional emails via SendGrid API with built-in delivery event tracking (delivered, opened, clicked, bounced). Uses the JDK HttpClient — no external SDK needed.

When to use: You want email delivery tracking beyond basic SMTP, or you already use SendGrid as your email provider.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-sendgrid</artifactId>
    <version>1.0.0</version>
</dependency>

notify-tiktok-shop — TikTok Shop Channel

What it does: Sends messages to TikTok Shop sellers via the TikTok Shop API. Handles HMAC-SHA256 request signing automatically. Uses the JDK HttpClient.

When to use: You want to send notifications to TikTok Shop sellers (order updates, customer messages).

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-tiktok-shop</artifactId>
    <version>1.0.0</version>
</dependency>

notify-facebook — Facebook Channel

What it does: Sends Facebook Page posts and Messenger messages via the Graph API. Uses the JDK HttpClient — no external SDK needed.

When to use: You want to post to Facebook Pages or send Messenger messages.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-facebook</artifactId>
    <version>1.0.0</version>
</dependency>

notify-whatsapp — WhatsApp Cloud API Channel

What it does: Sends WhatsApp messages directly via Meta's Cloud API (no Twilio needed). Uses the JDK HttpClient — zero external dependencies.

When to use: You want to send WhatsApp messages using Meta's official Cloud API instead of Twilio.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-whatsapp</artifactId>
    <version>1.0.0</version>
</dependency>

notify-aws-sns — AWS SNS Channel

What it does: Publishes messages to AWS SNS topics or sends direct SMS via AWS Simple Notification Service. Uses AWS SDK v2.

When to use: You're already in the AWS ecosystem and want to use SNS for notifications.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-aws-sns</artifactId>
    <version>1.0.0</version>
</dependency>

notify-mailgun — Mailgun Channel

What it does: Sends transactional emails via Mailgun REST API. Uses the JDK HttpClient — no external SDK needed.

When to use: You use Mailgun as your email provider.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-mailgun</artifactId>
    <version>1.0.0</version>
</dependency>

notify-pagerduty — PagerDuty Channel

What it does: Creates PagerDuty incidents via the Events API v2. Uses the JDK HttpClient — no external SDK needed.

When to use: You want to trigger PagerDuty alerts/incidents from your notification pipeline.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-pagerduty</artifactId>
    <version>1.0.0</version>
</dependency>

notify-tracker-jpa — JPA Delivery Tracker

What it does: Persists delivery receipts to a relational database (MySQL, PostgreSQL, H2, etc.) using Spring Data JPA. Stores notification ID, channel, recipient, status, timestamp, and error messages. Provides query methods for filtering and counting.

When to use: You want delivery tracking data to survive restarts (instead of the default in-memory tracker). Requires Spring Data JPA and a database on the classpath.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-tracker-jpa</artifactId>
    <version>1.0.0</version>
</dependency>

notify-admin — Admin Dashboard

What it does: Provides a built-in web UI at /notify-admin with 4 pages: Dashboard (overview metrics), Tracking (delivery receipts), DLQ (failed notifications), and Channels (status). Built with Thymeleaf, dark theme, fully responsive.

When to use: You want a visual admin panel to monitor your notification system without building one from scratch. Requires notify.admin.enabled=true in your config.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-admin</artifactId>
    <version>1.0.0</version>
</dependency>

notify-queue-rabbitmq — RabbitMQ Message Queue

What it does: Adds async notification processing via RabbitMQ. Includes a producer (enqueue notifications), a consumer (reads from queue and sends via NotifyHub), and Spring Boot auto-configuration with exchange/queue/binding setup.

When to use: You need to decouple notification sending from your main application flow, or you're in a microservice architecture where one service enqueues and another sends.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-queue-rabbitmq</artifactId>
    <version>1.0.0</version>
</dependency>

notify-queue-kafka — Apache Kafka Message Queue

What it does: Same as RabbitMQ module but uses Apache Kafka as the message broker. Sends notifications to a Kafka topic with channel:recipient as the message key for partition ordering.

When to use: You're already using Kafka in your infrastructure, or you need high-throughput notification processing at scale.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-queue-kafka</artifactId>
    <version>1.0.0</version>
</dependency>

notify-mcp — MCP Server for AI Agents

What it does: Exposes all NotifyHub channels as MCP (Model Context Protocol) tools, allowing AI agents (Claude Desktop, Claude Code, Cursor) to send notifications through natural language commands. Runs as a headless Spring Boot app communicating via STDIO JSON-RPC. Provides 27 tools: send via any channel, batch send, audience management, DLQ monitoring, and delivery analytics.

When to use: You want AI agents to send notifications on your behalf. Configure the JAR path in your MCP client's config file and the agent will discover all available tools automatically.

<dependency>
    <groupId>io.github.gabrielbbaldez</groupId>
    <artifactId>notify-mcp</artifactId>
    <version>1.0.0</version>
</dependency>

What Do I Need?

I want to...

Add these dependencies

Send emails from Spring Boot

notify-spring-boot-starter (already includes email)

Send SMS or WhatsApp

notify-spring-boot-starter + notify-sms

Send to Slack

notify-spring-boot-starter + notify-slack

Send to Telegram

notify-spring-boot-starter + notify-telegram

Send to Discord

notify-spring-boot-starter + notify-discord

Send to Microsoft Teams

notify-spring-boot-starter + notify-teams

Send mobile push (FCM)

notify-spring-boot-starter + notify-push-firebase

Send to any HTTP API

notify-spring-boot-starter + notify-webhook

Send via WebSocket

notify-spring-boot-starter + notify-websocket

Send to Google Chat

notify-spring-boot-starter + notify-google-chat

Send to Twitch chat

notify-spring-boot-starter + notify-twitch

Send to YouTube live chat

notify-spring-boot-starter + notify-youtube

Send to Instagram

notify-spring-boot-starter + notify-instagram

Send via SendGrid (with tracking)

notify-spring-boot-starter + notify-sendgrid

Send to TikTok Shop

notify-spring-boot-starter + notify-tiktok-shop

Send to Facebook Page/Messenger

notify-spring-boot-starter + notify-facebook

Send WhatsApp via Cloud API

notify-spring-boot-starter + notify-whatsapp

Send via AWS SNS

notify-spring-boot-starter + notify-aws-sns

Send via Mailgun

notify-spring-boot-starter + notify-mailgun

Create PagerDuty incidents

notify-spring-boot-starter + notify-pagerduty

Prevent duplicate sends

notify-spring-boot-starter (built-in, config-driven)

A/B test templates

notify-spring-boot-starter (built-in, use .templateVersion())

Persist tracking to database

notify-spring-boot-starter + notify-tracker-jpa

Admin dashboard UI

notify-spring-boot-starter + notify-admin

Use without Spring Boot

notify-core + channel modules you need

Async processing via RabbitMQ

notify-spring-boot-starter + notify-queue-rabbitmq

Async processing via Kafka

notify-spring-boot-starter + notify-queue-kafka

Let AI agents send notifications

notify-mcp (standalone JAR)

Everything at once

notify-spring-boot-starter + all channel modules above


Roadmap

  • v0.1.0 — Core API, Email, SMS, WhatsApp, Mustache templates, Spring Boot starter

  • v0.2.0 — Slack, Telegram, Discord, async sending, scheduling, delivery tracking

  • v0.3.0 — Teams, Firebase Push, Webhook, attachments, priority, rate limiting, DLQ, i18n, batch send, JPA tracker, Micrometer metrics, Actuator health, Spring events, conditional routing, admin dashboard (80+ tests)

  • v0.4.0 — WebSocket channel, Google Chat channel, message deduplication, template versioning (105+ tests, 16 modules)

  • v0.5.0 — MCP Server module: 13 AI agent tools for sending notifications via Claude Desktop, Claude Code, Cursor (130+ tests, 17 modules)

  • v0.6.0 — Named recipients, Docker images (MCP + REST API), Swagger UI, RabbitMQ + Kafka message queue modules, GitHub Pages landing page

  • v0.7.0 — Twitter/X, LinkedIn, Notion channels (14 channels, 16 MCP tools, 22 modules)

  • v0.8.0 — Twitch, YouTube channels, admin dashboard redesign (16 channels, 18 MCP tools, 24 modules)

  • v0.9.0 — MCP advanced tools: audiences, contacts, batch send, DLQ, analytics (16 channels, 26 MCP tools)

  • v0.10.0 — Instagram channel: DMs and feed posts via Meta Graph API (17 channels, 27 MCP tools, 25 modules)

  • v1.0.0 — SendGrid, TikTok Shop, Facebook, WhatsApp Cloud API, AWS SNS, Mailgun, PagerDuty channels, scheduled notifications MCP tools, JaCoCo coverage (24 channels, 36 MCP tools, 27 modules)

  • v1.1.0 — Architecture improvements: resilience pipeline (circuit breaker, bulkhead, handler chain), unified event system (NotificationEventBus, EventType, MetricsEventListener), god object refactoring (NotificationExecutor, NotificationScheduler extracted from NotifyHub), multi-channel orchestration, built-in A/B testing, cron scheduling, quiet hours, TestNotifyHub test utility, channel template module, Levenshtein error suggestions, enhanced health indicator with circuit breaker status


Requirements

  • Java 17+

  • Spring Boot 3.x (for starter — optional, core works standalone)

  • Maven 3.8+ (for building)


License

MIT License — see LICENSE for details.


-
security - not tested
A
license - permissive license
-
quality - not tested

Resources

Looking for Admin?

Admins can modify the Dockerfile, update the server description, and track usage metrics. If you are the server author, to access the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/GabrielBBaldez/notify-hub'

If you have feedback or need assistance with the MCP directory API, please join our Discord server