- Python 100%
| docs | ||
| src/nextcloud_calendar_webhook | ||
| .gitignore | ||
| .style.yapf | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
📅 Nextcloud Calendar Webhook
A tiny, self-hosted bridge that turns Nextcloud calendar events into beautiful ntfy notifications.
When a calendar event is created or updated in Nextcloud, this service receives the webhook payload, extracts the event from its iCalendar data, formats it into a human-friendly notification, and sends it to an ntfy topic.
Simple. Lightweight. No database. No frontend. Just calendars → notifications. ✨
✨ How it works
flowchart LR
NC["☁️ Nextcloud<br/>Calendar"]
WH["📡 Calendar Webhook"]
APP["🐍 Nextcloud Calendar Webhook<br/><br/>Flask<br/>iCalendar parser"]
NTFY["🔔 ntfy<br/>Notification Server"]
DEV["📱 Your Devices"]
NC -->|Calendar event| WH
WH -->|POST /webhook| APP
APP -->|Formatted Markdown<br/>notification| NTFY
NTFY -->|Push notification| DEV
classDef nextcloud fill:#0082c9,stroke:#005b8f,color:#fff,stroke-width:2px
classDef webhook fill:#6366f1,stroke:#4338ca,color:#fff,stroke-width:2px
classDef app fill:#3776ab,stroke:#245a80,color:#fff,stroke-width:2px
classDef ntfy fill:#ff6b35,stroke:#c94d20,color:#fff,stroke-width:2px
classDef device fill:#2d2d2d,stroke:#111,color:#fff,stroke-width:2px
class NC nextcloud
class WH webhook
class APP app
class NTFY ntfy
class DEV device
The service exposes two endpoints:
| Endpoint | Method | Purpose |
|---|---|---|
/webhook |
POST |
Receives Nextcloud calendar webhooks |
/health |
GET |
Simple health check |
🔔 Notifications
Calendar events are converted into Markdown notifications containing the useful bits of the event:
- 📅 Event title
- 🕐 Date and time
- 📁 Calendar name
- 📍 Location
- 👤 Organizer
- 📝 Description
- All-day event information
For example:
📅 Team Meeting
Monday, 12 October 2026 · 14:00–15:00
📁 Work
📍 Meeting Room 42
👤 mailto:alice@example.com
Discuss the upcoming release.
The notification title is automatically set to the calendar event's summary, and
the calendar tag is attached to the ntfy notification.
🚀 Getting started
Requirements
- Python 3.10+
- A Nextcloud instance with calendar/webhook support
- An ntfy server or access to ntfy.sh
- An ntfy topic and authentication token
1. Clone the repository
git clone <repository-url>
cd nextcloud-calendar-webhook
2. Create a virtual environment
python3 -m venv .venv
source .venv/bin/activate
On Windows:
.venv\Scripts\Activate.ps1
3. Install the project
For normal use:
python -m pip install .
For development, including documentation and YAPF:
python -m pip install -e ".[dev,docs]"
4. Configure ntfy
The service is configured entirely through environment variables.
export NTFY_URL="https://ntfy.example.com/my-calendar"
export NTFY_TOKEN="your-ntfy-token"
The following variables are required:
| Variable | Description |
|---|---|
NTFY_URL |
Full URL of the ntfy topic to publish notifications to |
NTFY_TOKEN |
Bearer token used to authenticate with ntfy |
⚠️ Both variables are required when the application starts.
5. Start the application
The Flask application listens on port 8080:
python -m nextcloud_calendar_webhook.scripts.listener
Or, if the listener console command is configured:
listener
The service will be available at:
http://localhost:8080
Check that it is alive:
curl http://localhost:8080/health
Expected response:
OK
🔗 Nextcloud configuration
Configure a Nextcloud calendar webhook to point at:
https://your-domain.example/webhook
The service expects the webhook payload to contain the calendar event data produced by Nextcloud, including the iCalendar representation of the event.
The relevant structure is approximately:
{
"event": {
"calendarData": {
"{DAV:}displayname": "My Calendar"
},
"objectData": {
"calendardata": "BEGIN:VCALENDAR\r\n..."
}
},
"user": {}
}
The service extracts the first VEVENT component from the supplied iCalendar
data.
🐳 Running behind a reverse proxy
For a production deployment, it is recommended to put the Flask application behind a proper WSGI server and reverse proxy rather than exposing Flask's development server directly.
A typical setup might look like:
flowchart LR
NC["☁️ Nextcloud"]
RP["🔒 Reverse Proxy<br/>nginx / Caddy<br/><br/>HTTPS"]
APP["🐍 Calendar Webhook<br/><br/>Flask :8080"]
NTFY["🔔 ntfy"]
NC -->|POST /webhook| RP
RP -->|HTTP| APP
APP -->|HTTPS| NTFY
classDef cloud fill:#0082c9,stroke:#005b8f,color:#fff,stroke-width:2px
classDef proxy fill:#059669,stroke:#047857,color:#fff,stroke-width:2px
classDef app fill:#3776ab,stroke:#245a80,color:#fff,stroke-width:2px
classDef notify fill:#ff6b35,stroke:#c94d20,color:#fff,stroke-width:2px
class NC cloud
class RP proxy
class APP app
class NTFY notify
This also gives you HTTPS termination, which is important when receiving webhooks from your Nextcloud instance.
🛠️ Development
Install the development dependencies:
python -m pip install -e ".[dev,docs]"
Formatting
The project uses YAPF for Python formatting.
Format the source tree:
yapf -ir src/
Or check what YAPF would change:
yapf -dr src/
The formatter configuration lives in pyproject.toml.
Project layout
.
├── pyproject.toml
├── README.md
├── src/
│ └── nextcloud_calendar_webhook/
│ ├── __init__.py
│ └── scripts/
│ ├── __init__.py
│ └── listener.py
└── docs/
├── conf.py
└── index.md
The project uses the modern Python src/ layout to keep the importable package
separate from project-level files.
📚 Documentation
Documentation is built using:
Build the documentation with:
sphinx-build -b html docs/ docs/_build/html
Then open:
docs/_build/html/index.html
🔐 Security
The /webhook endpoint is intended to be reachable by your Nextcloud instance.
Because the service forwards calendar information to ntfy, you should:
- Run it behind HTTPS.
- Keep the
NTFY_TOKENsecret. - Avoid exposing the service unnecessarily to the public internet.
- Consider restricting access to the webhook endpoint to your Nextcloud server where practical.
- Use a dedicated ntfy topic/token for calendar notifications.
🧩 Dependencies
The runtime is deliberately small:
- Flask — HTTP webhook server
- icalendar — parsing iCalendar/VEVENT data
- requests — sending notifications to ntfy
Development and documentation dependencies are kept separate from the runtime dependencies.
📜 License
MIT license.
Made with ☕, Python, and a healthy dislike of missing calendar notifications.