Accept Calendar Invitations in NeoMutt with Nextcloud
| A terminal, a paper planner, and one invitation answered. |
Table of Contents 📑
You read your mail in NeoMutt and keep your calendars in Nextcloud. Then a meeting invitation arrives, and a simple question interrupts that comfortable setup: how do you accept it without opening another mail client?
neomutt-nextcloud-rsvp connects the existing pieces. Open the calendar attachment, choose your response, and let Nextcloud send the calendar reply. NeoMutt remains your mail client, while khal and vdirsyncer handle the calendar entry.
The everyday workflow is simply message → attachments → invite.ics → response menu. It fits the existing l navigation used by mutt-wizard, without adding a separate calendar shortcut.
Changelog
| Date | Change |
|---|---|
| 2026-10-27 | Initial guide: attachment-menu replies through Nextcloud scheduling. |
1. Importing Is Not Accepting
An .ics attachment contains calendar data. Importing it can put the meeting into your calendar while leaving your participation at NEEDS-ACTION. That does not tell the organizer that you will attend.
A proper response also needs a calendar scheduling message. With server-side CalDAV scheduling, changing your own participant status can trigger that response on the server. sabre/dav, which provides this scheduling mechanism, documents automatic organizer notification when an attendee accepts or declines. CalDAV scheduling documentation
This workflow uses that division of work:
NeoMutt: choose a response for the invitation
↓
khal: import the event with your participant status
↓
vdirsyncer: synchronize the selected calendar collection
↓
Nextcloud: send the calendar reply to the organizerThe helper does not send an additional SMTP reply. It reads Nextcloud’s scheduling result after synchronization. The distinction between an unchanged import and an accepted invitation was checked with synthetic events and an isolated Nextcloud instance; the project’s verification notes describe those tests.
2. Before You Start
You need a working NeoMutt setup on Linux, Python 3.11–3.14 for the tested dependency combination, and an existing Nextcloud calendar configured in khal and vdirsyncer. The helper uses khal’s command line; ikhal is optional.
Nextcloud must have working email delivery and its Send invitations to attendees setting enabled. Its administration guide explains these requirements and the separate setting that can disable external participants. Nextcloud invitation settings
The address receiving the invitation must also be recognized by your Nextcloud account. For an alias, add the additional email address to your profile and complete any verification Nextcloud requests. The helper checks the server’s calendar-user-address-set before importing; a local configuration entry alone does not register an identity.
The initial integration was tested with Nextcloud 35.0.2, NeoMutt 20260616, khal 0.14.1, and vdirsyncer 0.21.0. Treat those as the tested combination, rather than a promise about every server or client version. Project requirements and limits
3. Install the Helper
Clone the repository and create a dedicated Python environment. These commands select the initial tested revision:
git clone https://github.com/delightfuldude/neomutt-nextcloud-rsvp.git
cd neomutt-nextcloud-rsvp
git checkout d2931657ff3f904c4bd43c2413bf7ef906fc0f90
python3 -m venv "$HOME/.local/share/neomutt-nextcloud-rsvp/venv"
"$HOME/.local/share/neomutt-nextcloud-rsvp/venv/bin/python" -m pip install -r requirements.txt
install -d -m 700 "$HOME/.config/mutt"
install -m 755 calendar-reply.py "$HOME/.config/mutt/calendar-reply.py"
install -m 755 integration/eventhandler.sh "$HOME/.config/mutt/eventhandler.sh"If files with those names already exist, preserve them before installing the new copies. The project is MIT-licensed; its README remains the detailed reference for installation and future updates.
4. Map Your Email Address to a Calendar
Create ~/.config/mutt/calendar-reply.json using your existing calendar names. For example:
{
"accounts": {
"alice@example.org": {
"calendar": "personal",
"path": "~/.local/share/vdir/calendars/personal",
"pair": "personal",
"collection": "personal"
}
}
}Here, calendar is the name reported by khal printcalendars. path is that calendar’s local directory. pair and collection identify the corresponding vdirsyncer target: personal/personal in this example.
For additional mail accounts, add another entry with the appropriate calendar mapping. Several addresses may use the same calendar, provided Nextcloud recognizes each address. Credentials stay in your existing vdirsyncer configuration, including its password.fetch provider.
The repository includes matching khal and vdirsyncer examples. Adapt them to your setup; this guide assumes calendar synchronization already works.
5. Connect the Attachment Menu
Add these settings to your NeoMutt configuration. In a multi-account setup, set NEOMUTT_CALENDAR_EMAIL after the active account sets $from, including whenever you switch accounts:
setenv NEOMUTT_RSVP_PYTHON "$HOME/.local/share/neomutt-nextcloud-rsvp/venv/bin/python"
setenv NEOMUTT_CALENDAR_EMAIL "$from"
auto_view text/calendar application/icsThe wrapper uses that Python environment for the helper and prepends its directory to the child process’s PATH, keeping khal and vdirsyncer in the same environment.
Next, merge these entries into the mailcap file your NeoMutt configuration actually uses:
# Interactive action when you explicitly open the calendar attachment.
text/calendar; ~/.config/mutt/eventhandler.sh %s; needsterminal
application/ics; ~/.config/mutt/eventhandler.sh %s; needsterminal
# Read-only preview while reading the message.
text/calendar; ~/.local/share/neomutt-nextcloud-rsvp/venv/bin/khal printics %s; copiousoutput
application/ics; ~/.local/share/neomutt-nextcloud-rsvp/venv/bin/khal printics %s; copiousoutputFor example, if that file is ~/.config/mailcap, select it with:
set mailcap_path = "~/.config/mailcap"Preserve your other MIME handlers. The interactive entries intentionally omit copiousoutput; the automatic preview entries include it. NeoMutt uses that flag to identify non-interactive viewers suitable for its pager. Reading the invitation therefore displays information; opening its attachment invokes the response menu. NeoMutt MIME and autoview documentation
mutt-wizard users already have these navigation bindings. Add them if you want the same workflow in another configuration:
bind pager l view-attachments
bind attach l view-mailcap
bind attach <return> view-mailcapThe operation that matters is <view-mailcap>, so you can retain different navigation keys if you prefer. This also fits alongside the general attachment file chooser: calendar files get their own response handler through mailcap.
6. Answer an Invitation
Restart NeoMutt after changing its configuration. Then:
- Open the invitation email.
- Press l to enter the attachment list.
- Select
invite.icswith j/k. - Press l or Enter to open it.
- Choose your participation status.
The current interface uses German labels:
| Choice | Meaning |
|---|---|
1 Annehmen | Accept |
2 Unter Vorbehalt | Tentatively accept |
3 Ablehnen | Decline |
| Empty answer | Cancel without importing or syncing |
After a response is selected, two short lines show the operation’s progress:
1/2 Kalender wird mit Nextcloud abgeglichen …
2/2 Antwort wird gespeichert und synchronisiert; Versandstatus wird geprüft …The first covers the calendar reconciliation. The second covers saving the response, synchronizing it, and checking Nextcloud’s scheduling result. The final message remains visible until you press Enter to return to the attachment list.
7. Checks and Troubleshooting
The helper preserves event identity and organizer revision data, changes your own participation status, and checks the server result. A repeated response that was already confirmed does not send another reply. The project tests also cover acknowledging an updated organizer revision while keeping the same participation status. Verification notes
If something fails, start with these checks:
- Unknown participant identity: compare the invitation’s attendee address with the active NeoMutt account and the additional addresses registered in Nextcloud.
- Calendar or collection mismatch: check the JSON mapping against your khal and vdirsyncer configuration. The initial release expects matching local and remote collection names, not renamed three-element mappings.
- Status saved, reply unconfirmed: inspect the reported scheduling status and Nextcloud’s mail configuration. The helper does not silently fall back to a second SMTP sender.
A scheduling status of 1.1 or 1.2 describes the server’s handling of the scheduling message; it does not prove arrival in an external recipient’s inbox. CalDAV scheduling status codes are defined in RFC 6638.
Full recurring invitations are supported, but detached recurrence exceptions are explicitly rejected. Incoming cancellation and reply messages also need separate handling; this helper processes METHOD:REQUEST invitations. Those limits are listed in the project README.
For a synthetic test of the helper itself, run the repository’s unit suite with the same Python environment:
"$HOME/.local/share/neomutt-nextcloud-rsvp/venv/bin/python" -m unittest discover -s tests -vRun that command from the repository directory. These tests use synthetic calendar data and mocked external operations; they do not answer real invitations.





