Skip to main content

One command. Every notification channel.

Project description

notifykit

One command. Every notification channel.

Stars License Python Tests Rounds


Send notifications to Slack, Discord, Email, Telegram, and Webhooks from a single YAML config.


Why This Exists

Every project eventually needs notifications. Deploy alerts go to Slack. Error pages ping the on-call Telegram group. Weekly reports land in email. Monitoring webhooks fire to custom dashboards.

The problem: each channel has its own SDK, authentication flow, payload format, and retry logic. You end up writing the same boilerplate in every project — a Slack webhook here, a SendGrid integration there, a Telegram bot scattered somewhere else.

notifykit solves this by providing a single unified interface. One YAML config file declares all your channels. One CLI command — or one Python function — routes messages to the right places based on severity level and tags. Rate limiting, retries, formatting, and audit logging come built-in.

No more copy-pasting webhook code. No more forgetting to add retries. No more wondering which notification actually got delivered.


Table of Contents


Features

  • 6 built-in channels — Slack, Discord, Email, Telegram, Webhook, Console
  • YAML configuration — One file declares all channels with ${ENV_VAR} expansion
  • Level-based routing — Send debug to console, errors to Slack, critical to PagerDuty webhook
  • Tag-based routing — Route deploy tags to one channel, alerts to another
  • Template engine{{variable}} substitution with dotted access ({{deploy.env.name}})
  • Multi-format output — Plain text, Markdown, HTML — each channel gets the right format
  • Rate limiting — Per-channel token bucket prevents flooding
  • Retry with backoff — Exponential backoff with configurable max delay
  • Audit log — Every send attempt recorded with timestamp, result, and timing
  • Dry-run mode — See which channels would receive a message without sending
  • Extensible — Register custom channels with a simple ABC
  • CLI-first — Every feature accessible from the command line

Installation

pip install notifykit

Or install from source:

git clone https://github.com/JSLEEKR/notifykit.git
cd notifykit
pip install -e ".[dev]"

Requirements

  • Python 3.10+
  • click (CLI framework)
  • pyyaml (configuration)
  • requests (HTTP channels)

Quick Start

1. Generate a config file

notifykit init

This creates notifykit.yaml with a console channel enabled and all other channels commented out as examples.

2. Send a notification

notifykit send "Hello from notifykit!" --level info

3. Send with title and tags

notifykit send "Deployment complete" --title "Deploy v1.2.3" --tag deploy --tag prod --level info

4. Test all channels

notifykit test

Configuration

notifykit uses a YAML file (notifykit.yaml) to declare channels and their settings.

Minimal config

channels:
  console:
    type: console
    enabled: true
    levels: [debug, info, warning, error, critical]

Full config example

channels:
  console:
    type: console
    enabled: true
    levels: [debug, info, warning, error, critical]

  slack-alerts:
    type: slack
    enabled: true
    levels: [warning, error, critical]
    tags: [alerts]
    settings:
      webhook_url: ${SLACK_WEBHOOK_URL}
      channel: "#alerts"
      username: notifykit

  discord-dev:
    type: discord
    enabled: true
    levels: [info, warning, error, critical]
    settings:
      webhook_url: ${DISCORD_WEBHOOK_URL}

  email-ops:
    type: email
    enabled: true
    levels: [error, critical]
    settings:
      smtp_host: smtp.gmail.com
      smtp_port: 587
      use_tls: true
      username: ${EMAIL_USER}
      password: ${EMAIL_PASS}
      from_addr: alerts@example.com
      to_addrs:
        - ops@example.com

  telegram-oncall:
    type: telegram
    enabled: true
    levels: [critical]
    tags: [oncall]
    settings:
      bot_token: ${TELEGRAM_BOT_TOKEN}
      chat_id: ${TELEGRAM_CHAT_ID}

  webhook-monitor:
    type: webhook
    enabled: true
    settings:
      url: https://monitor.example.com/api/events
      method: POST
      headers:
        Authorization: "Bearer ${MONITOR_TOKEN}"

defaults:
  format: plain
  retry_count: 2
  retry_delay: 1.0

templates:
  deploy: "Deployment {{status}}: {{service}} ({{version}}) at {{timestamp}}"
  alert: "[{{level}}] {{title}}: {{body}}"

Channel Types

Type Description Required Settings
console Print to stdout/stderr None
slack Slack incoming webhook webhook_url
discord Discord webhook webhook_url
email SMTP email smtp_host, from_addr, to_addrs
telegram Telegram Bot API bot_token, chat_id
webhook Generic HTTP webhook url

Environment Variables

Use ${VAR_NAME} syntax in your config to reference environment variables:

settings:
  webhook_url: ${SLACK_WEBHOOK_URL}
  channel: ${SLACK_CHANNEL:-#general}  # with default value

The ${VAR:-default} syntax provides a fallback when the variable is not set.

Level-Based Routing

Each channel declares which severity levels it accepts:

channels:
  console:
    levels: [debug, info, warning, error, critical]  # everything
  slack:
    levels: [warning, error, critical]                # warnings and above
  pagerduty:
    levels: [critical]                                # critical only

Available levels: debug, info, warning, error, critical

Tag-Based Routing

Channels can filter by message tags. If a channel has tags configured, only messages with at least one matching tag are routed to it:

channels:
  slack-deploy:
    tags: [deploy, release]    # only deploy/release messages
  slack-alerts:
    tags: [alerts, monitoring] # only alert messages
  console:
    # no tags = receives everything that matches level filter

CLI Reference

send

Send a notification message to all matching channels.

notifykit send "Message body" [OPTIONS]
Option Description
-l, --level Severity level: debug/info/warning/error/critical (default: info)
-t, --title Message title
--tag Add a tag (repeatable)
-f, --format Output format: plain, markdown, html
-c, --config Path to config file
--channel Send to specific channel(s) only
--template Use a named template from config
--var Template variable as key=value (repeatable)
--dry-run Show routing without actually sending
--json Output results as JSON (for CI pipelines)
-q, --quiet Suppress non-error output

Examples:

# Basic send
notifykit send "Server is healthy" -l info

# With title and tags
notifykit send "CPU at 95%" -t "High CPU Alert" --tag alerts -l warning

# Using a template
notifykit send "" --template deploy --var status=success --var service=api --var version=1.2.3

# Dry run
notifykit send "Test message" --dry-run

# Send to specific channel only
notifykit send "Debug info" --channel console

test

Send a test notification to all enabled channels to verify configuration.

notifykit test [-c CONFIG]

channels

List all configured channels with their type, status, levels, and tags.

notifykit channels [-c CONFIG]

validate

Validate the configuration file for errors.

notifykit validate [-c CONFIG]

Returns exit code 0 if valid, 1 if issues are found.

history

Show notification history from the audit log.

notifykit history [OPTIONS]
Option Description
-n, --limit Number of entries (default: 20)
--channel Filter by channel name
--failures Show only failed sends
--json Output as JSON

init

Generate a default notifykit.yaml configuration file.

notifykit init [-o FILENAME] [--force]

Python API

Sending Messages

from notifykit.models import Message, Level
from notifykit.config import load_config, parse_channels
from notifykit.exceptions import ConfigNotFoundError
from notifykit.router import Router

# Load config and create router
try:
    config = load_config("notifykit.yaml")
except ConfigNotFoundError:
    print("Run 'notifykit init' first")
    raise

channels = parse_channels(config)
router = Router(channels)

# Create and send a message
msg = Message(
    body="Deployment complete",
    level=Level.INFO,
    title="Deploy v1.2.3",
    tags=["deploy", "prod"],
)

results = router.send(msg)
for r in results:
    print(r.summary)  # "[OK] slack (120ms)" or "[FAIL] email - SMTP error"

Templates

from notifykit.templates import render, list_variables, validate_template

template = "Deployment {{status}}: {{service}} ({{version}})"
variables = {"status": "success", "service": "api", "version": "1.2.3"}

# Render
output = render(template, variables)
# "Deployment success: api (1.2.3)"

# List variables in template
vars = list_variables(template)
# ["status", "service", "version"]

# Validate all variables are available
missing = validate_template(template, variables)
# [] (empty = all resolved)

Dotted access is supported:

render("{{user.name}}", {"user": {"name": "Alice"}})
# "Alice"

Formatting

from notifykit.formatter import format_message, convert_format
from notifykit.models import Format, Message

msg = Message(body="Hello", title="Test", level="error", tags=["prod"])

plain = format_message(msg, Format.PLAIN)
# "[ERROR] Test Hello (tags: prod)"

md = format_message(msg, Format.MARKDOWN)
# "## Test\n\n**Level:** ERROR\n\nHello\n\n**Tags:** `prod`"

html = format_message(msg, Format.HTML)
# "<div>...<h2>Test</h2>...[ERROR]...<p>Hello</p>...</div>"

# Convert between formats
convert_format("**bold**", Format.MARKDOWN, Format.PLAIN)
# "bold"

Rate Limiting

from notifykit.rate_limiter import RateLimiter

rl = RateLimiter()
rl.configure("slack", max_per_minute=10)

if rl.allow("slack"):
    # send message
    pass
else:
    wait = rl.wait_time("slack")
    print(f"Rate limited. Wait {wait:.1f}s")

Retry Logic

from notifykit.retry import retry_send

result = retry_send(
    channel.send,
    message,
    max_retries=3,
    base_delay=1.0,
    backoff_factor=2.0,
)

Delay schedule: 1s, 2s, 4s, 8s, ... capped at max_delay.

Audit Log

from notifykit.audit import AuditLog

audit = AuditLog("notifications.jsonl")

# Record send results
audit.record(result)
audit.record_many(results)

# Query history
recent = audit.query(limit=10)
failures = audit.query(success=False)
slack_history = audit.query(channel="slack")

# Statistics
stats = audit.stats()
print(f"Success rate: {stats['success_rate']:.0%}")
print(f"Total: {stats['total']}, Failed: {stats['failed']}")

Channel Details

Slack

Sends via Incoming Webhooks with Block Kit formatting.

slack-alerts:
  type: slack
  levels: [warning, error, critical]
  settings:
    webhook_url: ${SLACK_WEBHOOK_URL}
    channel: "#alerts"     # optional override
    username: notifykit    # optional bot name

Features: header blocks, section blocks, context blocks with tags, level-specific emoji.

Discord

Sends via Discord Webhooks with rich embeds.

discord-dev:
  type: discord
  settings:
    webhook_url: ${DISCORD_WEBHOOK_URL}
    username: NotifyBot    # optional

Features: colored embeds by severity, title/description, footer with tags.

Email (SMTP)

Sends via any SMTP server with TLS support.

email-ops:
  type: email
  levels: [error, critical]
  settings:
    smtp_host: smtp.gmail.com
    smtp_port: 587
    use_tls: true
    username: ${EMAIL_USER}
    password: ${EMAIL_PASS}
    from_addr: alerts@example.com
    to_addrs:
      - ops@example.com
      - oncall@example.com

Features: HTML and plain text MIME, TLS, authentication optional.

Telegram

Sends via Telegram Bot API.

telegram-oncall:
  type: telegram
  levels: [critical]
  settings:
    bot_token: ${TELEGRAM_BOT_TOKEN}
    chat_id: ${TELEGRAM_CHAT_ID}
    disable_preview: true  # optional

Features: HTML and MarkdownV2 parse modes, hashtag-style tags, web preview control.

Webhook

Generic HTTP webhook for any endpoint.

webhook-monitor:
  type: webhook
  settings:
    url: https://monitor.example.com/api/events
    method: POST           # default
    timeout: 10            # seconds
    headers:
      Authorization: "Bearer ${TOKEN}"
    success_codes: [200, 201, 202, 204]  # default

The payload includes: id, level, title, body, tags, timestamp, metadata.

Console

Prints to stdout (info/debug/warning) or stderr (error/critical) with ANSI colors.

console:
  type: console
  settings:
    color: true  # default

Custom Channels

Register your own channel by extending BaseChannel:

from notifykit.channels.base import BaseChannel
from notifykit.channels.registry import register_channel
from notifykit.models import Message, SendResult

class PagerDutyChannel(BaseChannel):
    def send(self, message: Message) -> SendResult:
        # Your implementation here
        api_key = self.config.settings.get("api_key")
        # ... send to PagerDuty ...
        return self._make_result(message, success=True)

register_channel("pagerduty", PagerDutyChannel)

Then use it in your config:

channels:
  pagerduty:
    type: pagerduty
    levels: [critical]
    settings:
      api_key: ${PAGERDUTY_KEY}

Architecture

src/notifykit/
  __init__.py          # Package version
  models.py            # Message, SendResult, Level, ChannelConfig
  config.py            # YAML loader, env expansion, validation
  exceptions.py        # Typed exception hierarchy
  router.py            # Level/tag routing engine
  formatter.py         # Plain/Markdown/HTML formatting
  templates.py         # {{variable}} template engine
  rate_limiter.py      # Token bucket rate limiting
  retry.py             # Exponential backoff retry
  audit.py             # Notification audit log
  cli.py               # Click CLI commands
  channels/
    __init__.py
    base.py            # BaseChannel ABC + error sanitization
    registry.py        # Channel type registry
    console.py         # Console (stdout/stderr)
    slack.py           # Slack incoming webhook
    discord.py         # Discord webhook
    email_channel.py   # SMTP email
    telegram.py        # Telegram Bot API
    webhook.py         # Generic HTTP webhook

Message Flow

CLI/API  -->  Router  -->  Channel  -->  Destination
  |             |            |
  |        Level/Tag     Rate Limit
  |        Matching      + Retry
  |             |            |
  +-- Template  |       Audit Log
      Rendering |
                |
           Formatter
        (Plain/MD/HTML)

When to Use This

Scenario What to configure Command
Deploy pipeline alerts Slack channel for deploy tag, warn level+ notifykit send "Deploy done" --tag deploy --level info
On-call paging Telegram with oncall tag, critical only notifykit send "DB down" --tag oncall --level critical
Daily reports Email on a schedule (cron + notifykit) notifykit send "Daily report ready" --tag report
Error monitoring Multiple channels, error+ level notifykit send "500 spike" --level error
Local dev debugging Console channel only notifykit send "Checkpoint hit" --level debug
Webhook integrations Custom webhook channel with Bearer auth notifykit send "Event fired" --channel webhook-monitor
CI/CD build status Discord embed + Slack, with templates notifykit send "" --template deploy --var status=passed
Security alerts Slack + Telegram + Email, critical only notifykit send "Auth bypass detected" --level critical

notifykit shines when you need to send notifications to multiple channels from scripts, CI pipelines, or background workers — without installing five different SDKs.


Cross-Tool Integration

CI/CD Pipelines

GitHub Actions

- name: Notify on deploy
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
  run: |
    notifykit send "Deploy ${{ github.sha }}" \
      --title "Deploy to production" \
      --level info --tag deploy --json

Use --json for machine-readable output. Exit code 1 on any channel failure.

See examples/github-actions.yml for a complete workflow.

GitLab CI

notify:
  stage: notify
  script:
    - pip install notifykit
    - notifykit send "Pipeline $CI_PIPELINE_ID finished" --level info --tag deploy --json
  when: always

Jenkins

post {
    success {
        sh 'notifykit send "Build #${BUILD_NUMBER} passed" --level info --tag ci --json'
    }
    failure {
        sh 'notifykit send "Build #${BUILD_NUMBER} failed" --level error --tag ci --tag alerts --json'
    }
}

Cron Jobs

Run notifykit from cron for scheduled notifications:

# Daily health check report at 9am
0 9 * * * cd /app && notifykit send "Daily health check passed" --level info --tag report

# Alert if disk usage exceeds 90%
*/5 * * * * df -h / | awk 'NR==2 && $5+0 > 90 {print "Disk usage: "$5}' | xargs -I{} notifykit send "{}" --level warning --tag alerts

Monitoring Tools

Prometheus Alertmanager

Configure Alertmanager to call notifykit via a webhook receiver:

# alertmanager.yml
receivers:
  - name: notifykit
    webhook_configs:
      - url: 'http://localhost:8080/alert'  # your bridge script

Bridge script:

#!/bin/bash
# alertmanager-bridge.sh — receives Alertmanager webhook, forwards via notifykit
BODY=$(cat)
ALERT_NAME=$(echo "$BODY" | jq -r '.alerts[0].labels.alertname')
STATUS=$(echo "$BODY" | jq -r '.alerts[0].status')
notifykit send "$ALERT_NAME: $STATUS" --level error --tag alerts --json

Systemd Service Watchdog

# /etc/systemd/system/notifykit-watchdog.service
[Unit]
Description=Notify on service failure

[Service]
ExecStart=/usr/local/bin/notifykit send "Service %i failed" --level critical --tag oncall
Type=oneshot

[Install]
WantedBy=multi-user.target
# Trigger on another service failure
systemctl enable notifykit-watchdog@myapp.service

Docker Health Checks

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD curl -f http://localhost:8080/health || \
    (notifykit send "Container unhealthy" --level error --tag alerts && exit 1)

Python Integration

# Use as a library in your application
from notifykit.config import load_config, parse_channels
from notifykit.router import Router
from notifykit.models import Message, Level

router = Router(parse_channels(load_config()))

# In your error handler
def on_error(error: Exception):
    router.send(Message(
        body=str(error),
        level=Level.ERROR,
        title="Application Error",
        tags=["alerts"],
    ))

Example Output

notifykit init

Created notifykit.yaml

Next steps:
  1. Open notifykit.yaml and configure your channels
  2. Uncomment and fill in the channels you want to use
  3. Run 'notifykit send "Hello!"' to send your first notification
  4. Run 'notifykit channels' to see all configured channels
  5. Run 'notifykit validate' to check your config for errors

The console channel is already enabled - try sending a message now!

notifykit send "Deploy complete" --title "v1.2.3" --level info

[INFO] v1.2.3 Deploy complete
  [OK] console (0ms)
  [OK] slack-alerts (120ms)

notifykit send "Test" --dry-run

Would send to 2 channel(s):
  - console (console)
  - slack-alerts (slack)

notifykit channels

Channels (3):

  console
    Type:   console
    Status: enabled
    Levels: debug, info, warning, error, critical

  slack-alerts
    Type:   slack
    Status: enabled
    Levels: warning, error, critical
    Tags:   alerts

  telegram-oncall
    Type:   telegram
    Status: enabled
    Levels: critical
    Tags:   oncall

notifykit validate

Configuration is valid.

Or on failure:

Found 2 issue(s):
  - Channel 'slack-alerts': Slack requires 'settings.webhook_url'
  - Channel 'email-ops': Email requires 'settings.smtp_host'

notifykit history

  [2026-03-28 07:35:23] OK console msg=50c70d432e9e (0ms)
  [2026-03-28 07:35:20] OK slack-alerts msg=a3b4f6e0ef23 (120ms)
  [2026-03-28 07:34:01] FAIL telegram-oncall msg=f1b2c3d4e5f6 (5001ms)
                         Error: Connection timeout

notifykit test

[INFO] Test Notification This is a test notification from notifykit.
  [OK] console
  [OK] slack-alerts
  [FAIL] email-ops
         Error: SMTP authentication failed

Troubleshooting

No config file found

Error: No notifykit config file found. Create notifykit.yaml or run 'notifykit init'.

Fix: Run notifykit init in your project root to create a default config.


Channel not receiving messages

Symptoms: send exits 0 but the channel is silent.

Check list:

  1. Run notifykit channels — is the channel enabled?
  2. Does the message level match the channel's levels list? (warning won't route to a channel that only accepts error, critical)
  3. Does the message have a tag that matches the channel's tags filter? (Channel with tags: [deploy] only receives messages with --tag deploy)
  4. Run notifykit send "Test" --dry-run to see which channels would receive it.

Slack / Discord webhook fails

  [FAIL] slack-alerts
         Error: 400 Bad Request

Fixes:

  • Verify the webhook URL is correct: notifykit validate
  • Check that ${SLACK_WEBHOOK_URL} is exported in your environment
  • Test the webhook directly: curl -X POST -H 'Content-type: application/json' --data '{"text":"test"}' "$SLACK_WEBHOOK_URL"

Email SMTP authentication error

  [FAIL] email-ops
         Error: SMTP authentication failed

Fixes:

  • For Gmail: use an App Password, not your regular password
  • Verify smtp_host, smtp_port, and use_tls match your provider's settings
  • Check environment variables are set: echo $EMAIL_USER

Telegram: chat not found

  [FAIL] telegram-oncall
         Error: 400 Bad Request: chat not found

Fixes:

  • The bot must be added to the chat/group before it can send messages
  • chat_id for a group is negative (e.g., -1001234567890) — use @userinfobot to find it
  • Send a message to the bot first to activate it for private chats

Environment variable not expanded

If you see ${SLACK_WEBHOOK_URL} literally in error messages, the variable is not exported.

export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..."
notifykit validate

Use ${VAR:-fallback} syntax to provide a default value for optional variables.


notifykit history shows nothing

The audit log is written to .notifykit/audit.jsonl in the current working directory where you ran notifykit send. Run history from the same directory.


Rate limit exceeded

No channels matched for this message.

If you configured a rate_limit on a channel and are sending too fast, the router will skip that channel. The rate limit resets after one minute.


FAQ

Q: Does notifykit make real HTTP requests during tests?

No. All channel tests use mocked HTTP via the responses library. No API calls are made during pytest.


Q: Can I use notifykit without a config file?

No. notifykit requires a notifykit.yaml config file. Run notifykit init to create one with a working console channel — no credentials needed.


Q: Can I send to multiple specific channels at once?

Yes. Use --channel multiple times:

notifykit send "Alert!" --channel slack-alerts --channel telegram-oncall

Q: How do I send the same message to all channels regardless of level/tag filters?

Use a channel with no levels or tags restrictions, or use --channel to target channels directly.


Q: Where is the audit log stored?

At .notifykit/audit.jsonl relative to the working directory. Each line is a JSON record of one send attempt.


Q: How do I rotate or clear the audit log?

Delete or truncate .notifykit/audit.jsonl manually. notifykit will create a new one on the next send.


Q: Does notifykit support async sending?

Not natively — sends are synchronous. For high-volume use, call notifykit from a task queue worker.


Q: Can I add my own channel type?

Yes. See Custom Channels for a step-by-step example.


Q: What happens if a channel fails? Does it block other channels?

No. Each channel is sent independently. A failure in one channel does not block others. All results are collected and reported at the end. The exit code is non-zero only if at least one channel failed.


Migration Guide

From email-only notifications

If you currently send notifications through email (e.g., Python smtplib, SendGrid API):

  1. Run notifykit init to create a config
  2. Configure the email channel with your SMTP settings
  3. Add additional channels (Slack, Discord) for instant alerts
  4. Replace your email-sending code with router.send(message)

Before:

import smtplib
from email.mime.text import MIMEText

msg = MIMEText("Server is down!")
msg["Subject"] = "CRITICAL ALERT"
msg["From"] = "alerts@example.com"
msg["To"] = "ops@example.com"
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login(user, password)
server.sendmail("alerts@example.com", ["ops@example.com"], msg.as_string())
server.quit()

After:

from notifykit.config import load_config, parse_channels
from notifykit.router import Router
from notifykit.models import Message, Level

router = Router(parse_channels(load_config()))
router.send(Message(
    body="Server is down!",
    level=Level.CRITICAL,
    title="CRITICAL ALERT",
    tags=["alerts"],
))
# Now also goes to Slack + Telegram based on your config

From Slack webhook scripts

If you currently call Slack webhooks directly:

  1. Move your webhook URL to an environment variable
  2. Configure a Slack channel in notifykit.yaml
  3. Replace requests.post(webhook_url, json=...) with notifykit send

Before:

curl -X POST -H 'Content-type: application/json' \
  --data '{"text":"Deploy complete"}' \
  "$SLACK_WEBHOOK_URL"

After:

notifykit send "Deploy complete" --level info --tag deploy

From multiple notification libraries

If your project uses different libraries for each channel (slack-sdk, python-telegram-bot, etc.):

  1. Remove individual channel dependencies
  2. Add notifykit as a single dependency
  3. Configure all channels in one YAML file
  4. Replace all send calls with router.send()

Benefits: unified retry logic, rate limiting, audit logging, and a single config file.


Advanced Usage

Custom Payload for Webhooks

Override the default JSON payload for webhook channels:

channels:
  pagerduty:
    type: webhook
    levels: [critical]
    settings:
      url: https://events.pagerduty.com/v2/enqueue
      headers:
        Content-Type: application/json
      payload:
        routing_key: ${PAGERDUTY_KEY}
        event_action: trigger
        payload:
          summary: "{{body}}"
          severity: critical
          source: notifykit

Rate Limiting Configuration

Prevent flooding channels with per-channel rate limits:

channels:
  slack-alerts:
    type: slack
    rate_limit: 10  # max 10 messages per minute
    settings:
      webhook_url: ${SLACK_WEBHOOK_URL}

Template Variables from Environment

Combine templates with environment variables for dynamic notifications:

export DEPLOY_ENV=production
export DEPLOY_VERSION=2.1.0

notifykit send "" \
  --template deploy \
  --var "env=$DEPLOY_ENV" \
  --var "version=$DEPLOY_VERSION" \
  --var "status=success" \
  --tag deploy

Quiet Mode for Scripts

Use --quiet to suppress output in scripts that only need the exit code:

notifykit send "Health check passed" --level info --quiet
if [ $? -eq 0 ]; then
  echo "Notifications sent successfully"
fi

JSON Output for CI Parsing

Parse send results in CI pipelines:

RESULT=$(notifykit send "Build passed" --level info --json)
SUCCESS=$(echo "$RESULT" | jq -r '.success')
FAILED=$(echo "$RESULT" | jq -r '.failed')

if [ "$SUCCESS" = "false" ]; then
  echo "Warning: $FAILED channel(s) failed"
fi

See examples/real-world.py for Python integration patterns including FastAPI error handlers, batch notifications, and escalation configurations.


Testing

# Install dev dependencies
pip install -e ".[dev]"

# Run all tests
pytest

# Run with verbose output
pytest -v

# Run specific test module
pytest tests/test_channels.py

# Run specific test class
pytest tests/test_channels.py::TestSlackChannel

All channel tests use mock HTTP (responses library) — no real API calls are made during testing.

Test Coverage

Module Tests Coverage
models 16 Level, Format, Message, SendResult (summary), ChannelConfig
config 32 Env expansion, validation, input sanitization, typed exceptions
channels 61 All 6 channels + registry + base + edge cases + security sanitization
router 11 Level routing, tag routing, direct send
formatter 15 Plain, Markdown, HTML, format conversion
templates 16 Render, dotted access, variables, validation
rate_limiter 11 Token bucket, reset, wait time
retry 9 Success, failure, backoff calculation
audit 19 Record, query, stats, file I/O, skipped line tracking
cli 21 All 6 commands + version + JSON output + quiet mode
integration 8 Full pipeline: config -> router -> channel -> audit
performance 8 Batch 100, rate limiter under load
stress 8 1000 notifications, multi-channel, large payloads
Total 260

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing)
  3. Write tests for new functionality
  4. Ensure all tests pass (pytest)
  5. Submit a pull request

Development Setup

git clone https://github.com/JSLEEKR/notifykit.git
cd notifykit
pip install -e ".[dev]"
pytest

Round Log

Round Perspective Focus Tests Commit
1 User Onboarding — init guidance, error messages 201 fix: user onboarding
2 User Troubleshooting docs + examples 201 docs: troubleshooting + examples
3 Developer Coverage gaps — edge cases across modules 212 test: coverage gaps
4 Developer Code quality — dead code, unused imports 212 refactor: code quality
5 Security Input validation — env var injection, depth limits 218 fix: input validation
6 Security Error recovery — connection/timeout handling 222 fix: error recovery
7 Ecosystem CI integration — --json flag, GitHub Actions 222 feat: CI integration
8 Ecosystem Cross-tool docs — GitLab, Jenkins, cron, systemd 222 docs: cross-tool integration
9 Production Performance — 100-batch, rate limiter stress 230 test: performance
10 Production Cycle 1 complete — badge, changelog 230 docs: cycle 1 complete
11 User C2 UX refinements — --quiet flag, dry-run detail 230 fix: UX refinements
12 User C2 Advanced docs — migration guide, real-world examples 230 docs: advanced guide
13 Developer C2 Cycle 1 review — SendResult.summary, audit tracking 234 refactor: cycle 1 review
14 Developer C2 Test organization — helpers module, shared fixtures 234 refactor: test organization
15 Security C2 Security hardening — secret redaction in errors 240 fix: security hardening
16 Security C2 Error audit — typed exception hierarchy 244 fix: error audit
17 Ecosystem C2 Integration tests — full send pipeline 252 test: integration
18 Ecosystem C2 Doc-code sync — README matches actual API 252 docs: doc-code sync
19 Production C2 Stress tests — 1000 notifications, large payloads 260 test: stress
20 Production C2 Final — badges, ROUND_LOG, CHANGELOG 260 docs: final polish

License

MIT - JSLEEKR 2026

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jsleekr_notifykit-1.2.0.tar.gz (70.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jsleekr_notifykit-1.2.0-py3-none-any.whl (37.8 kB view details)

Uploaded Python 3

File details

Details for the file jsleekr_notifykit-1.2.0.tar.gz.

File metadata

  • Download URL: jsleekr_notifykit-1.2.0.tar.gz
  • Upload date:
  • Size: 70.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for jsleekr_notifykit-1.2.0.tar.gz
Algorithm Hash digest
SHA256 263729e67b396c81b5b6bede6d2ddea176427236b609d1f49e165a8dff842e1f
MD5 6c81e9648e64e0740dd238f5a7f7e5f5
BLAKE2b-256 94443edaa140857fc879aab23936a31b998ab1ff8eb5bc41b8a423d92463c412

See more details on using hashes here.

File details

Details for the file jsleekr_notifykit-1.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for jsleekr_notifykit-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 050b284258a4b3662e5e7f053bb8bdc8d20e2e1fc45925038d20adf9af7053e6
MD5 a62965ecfa2888138c1e515b2a20a770
BLAKE2b-256 d6dde3fb21954f98f35783a96bf1f85901cf81dc30f60dc9417b2c1a7a944e25

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page