Server-side scheduled invitations are not recognized as meeting requests in Outlook (ics arrives as plain attachment)

Issue Description

When an event with attendees is scheduled server-side, Stalwart sends the invitation email with the event summary as text/HTML and event.ics as a regular file attachment. Outlook does not recognize this email as a meeting request — it’s treated as a normal message with an attachment, so the recipient loses the native Accept/Decline/Tentative workflow.

Expected Behavior

An invitation sent by Stalwart’s server-side scheduling should be recognized by Outlook as a genuine meeting request, showing Accept/Decline/Tentative options directly in the message, without the recipient having to manually open an attached .ics file.

Actual Behavior

The invitation email arrives as a normal message (event summary as text/HTML body) with event.ics attached as a regular file.

Outlook classifies the message as a plain email, not a meeting request — no Accept/Decline/Tentative buttons are shown.

To view/import the event, the recipient has to manually open the event.ics attachment. Doing so triggers an Outlook security warning (“This location may not be safe”) because the file is opened from a temporary cache location, which is confusing for recipients.

Reproduction Steps

Create a new calendar event with a client using CalDAV/JMAP(eg. Thunderbird or Bulwark) and add an external attendee who uses Outlook als an client

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

PostgreSQL

Blob Storage

PostgreSQL

Search Engine

PostgreSQL

Directory Backend

Internal

Additional Context

  • For comparison: send an equivalent invitation from Thunderbird with “prefer client-side, email-based scheduling” enabled to the same Outlook recipient. This one is correctly recognized as a meeting request with Accept/Decline/Tentative options.
  • “Enable HTTP RSVP” is not a viable workaround for us, since it requires the Stalwart server to be reachable from the internet, which is not the case in our deployment (server is intentionally kept internal-only).

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Looking at the iMIP-sending code, this matches what you’re describing. When Stalwart builds a server-side scheduling message, it wraps the HTML/plaintext body in a multipart/related/multipart/alternative structure and then attaches the iCalendar data as a separate MIME part with Content-Type: text/calendar; method=<METHOD>; charset=utf-8 — but that part is explicitly marked .attachment("event.ics"), which sets Content-Disposition: attachment.

Outlook’s meeting-request detection generally requires the text/calendar part to be presented inline (no Content-Disposition: attachment), which is why a client like Thunderbird’s client-side iTIP messages (which typically send it without that disposition, or as the primary body) get recognized as invitations while Stalwart’s server-scheduled iMIP mail is treated as a generic message with a file attachment.

This is a source-level finding, not something documented as configurable behavior — there’s no setting currently exposed to change how the .ics part disposition is emitted for server-side scheduling. Since this is filed under Issues, a human/staff member will follow up on whether this warrants a fix to the iMIP builder (e.g. dropping the attachment disposition or presenting the calendar part inline).

For reference, the scheduling documentation only covers templates and RSVP options, not MIME structure: Scheduling | Stalwart

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

Fixed in v0.16.17 (yet to be released).

Thanks a lot for the quick fix! Once the release is out, I’ll happily test it on our side and report back with feedback.

Happy to confirm this is fixed in v0.16.17.