Invitations that arrive by email land on the calendar
Most calendars for agents stop at sending. This one reads too. Every inbound message is checked for a text/calendar part, and when one is there the calendar applies it before the message is stored:
- A new invitation (
METHOD:REQUEST) creates the event withsource: inbound, the sender as organizer, and your inbox listed among the attendees withneeds-action. Webhook:calendar.event.created. - An update to an invitation you already have is matched on its
ical_uidand applied in place: new time, new title, new attendee list. Webhook:calendar.event.updated. Stale copies with a lowerSEQUENCEare ignored. - A cancellation (
METHOD:CANCEL) marks the eventcancelled. Webhook:calendar.event.cancelled. - A reply (
METHOD:REPLY) to an invitation your agent sent records that attendee'saccepted,declinedortentativeon the event. Webhook:calendar.event.updated.
The message itself carries a compact calendar_event summary so an agent reading its mail sees the event without a second call. action says what the message did to the calendar.
{
"message_id": "e7a2c9d1-4b8f-4c3e-9d6a-5f1b0c8e2a74",
"from_email": "dana@example.com",
"subject": "Invitation: Vendor review @ Tue Sep 22, 2026 11am",
"calendar_method": "REQUEST",
"calendar_event": {
"event_id": "3d7a1f9c-52e4-4b8d-9a6f-1e0c7b2d8f35",
"inbox_id": "6f1c2d3e-8a4b-4c5d-9e6f-0a1b2c3d4e5f",
"ical_uid": "7f3a9c1e-2b4d-4e6f-8a0b@google.com",
"title": "Vendor review",
"start_at": "2026-09-22T15:00:00Z",
"end_at": "2026-09-22T15:45:00Z",
"status": "confirmed",
"organizer": { "email": "dana@example.com", "name": "Dana" },
"action": "created" // created | updated | cancelled | replied | unchanged
},
...
}
The same summary is the payload of the three calendar.event.* webhooks, so a handler that already routes message.received can subscribe to the calendar events and act on the event rather than re-parsing the mail. Calendar ingestion never loses a message: if parsing fails for any reason, the email is stored as normal and the calendar is simply left alone.