Development

Information for developers contributing to or building on NotifyHero/ntfy.

Architecture

NotifyHero is based on ntfy, which consists of:

  • Server - Go binary that handles HTTP/WebSocket connections
  • Web App - React SPA embedded in the server
  • Android App - Kotlin native app
  • iOS App - Swift native app
  • CLI - Command-line interface (same binary as server)

Building from Source

Server

# Clone
git clone https://github.com/binwiederhier/ntfy.git
cd ntfy

# Build
make build

# Run
./ntfy serve

Web App

cd web
npm install
npm start  # Development server
npm run build  # Production build

Android App

  1. Open android/ in Android Studio
  2. Build using Gradle

iOS App

  1. Open ios/ in Xcode
  2. Build for your target device

API Development

Test Endpoints

# Health check
curl https://app.notifyhero.com/v1/health

# Send test message
curl -d "Test" https://app.notifyhero.com/test-topic

# Subscribe (JSON stream)
curl https://app.notifyhero.com/test-topic/json

Response Formats

All endpoints support multiple formats:

  • /topic/json - Newline-delimited JSON
  • /topic/sse - Server-sent events
  • /topic/raw - Plain text
  • /topic/ws - WebSocket

Error Handling

Errors return JSON with structure:

{
  "code": 40001,
  "http": 400,
  "error": "invalid request"
}

Contributing

Guidelines

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Write/update tests
  5. Submit a pull request

Code Style

  • Go: Follow standard Go conventions
  • JavaScript: ESLint configuration provided
  • Commit messages: Conventional commits preferred

Testing

# Run tests
make test

# Run specific tests
go test ./server/...

Custom Integrations

Publishing

import requests

def notify(topic, message, **kwargs):
    headers = {}
    if 'title' in kwargs:
        headers['Title'] = kwargs['title']
    if 'priority' in kwargs:
        headers['Priority'] = str(kwargs['priority'])
    if 'tags' in kwargs:
        headers['Tags'] = ','.join(kwargs['tags'])

    response = requests.post(
        f'https://app.notifyhero.com/{topic}',
        data=message.encode('utf-8'),
        headers=headers
    )
    return response.status_code == 200

Subscribing

import requests
import json

def subscribe(topic, callback):
    response = requests.get(
        f'https://app.notifyhero.com/{topic}/json',
        stream=True
    )
    for line in response.iter_lines():
        if line:
            message = json.loads(line)
            callback(message)

Resources