MailFlatDocs
Documentation/Common test cases/Calendar invites

Calendar invites for AI agents

An agent inbox reads meeting invitations, answers them, schedules its own meetings, and shares a calendar a person can follow in Google, Apple or Outlook.

How it works

A calendar invitation is an email with a calendar file inside. Every agent inbox understands that file: invitations it receives land on its calendar, it can accept or decline them, and it can send its own invitations that show Yes and No buttons in Gmail, Outlook and Apple Calendar. There is no Google account and no OAuth, only email. Invitations need a plain agent inbox; an end-to-end encrypted inbox never reads mail content, so it has no calendar. On the Free plan without a verified domain, the invitations an inbox sends go only to your own addresses.

Read an invitation that arrives

The message carries a parsed summary of the invitation, and the event lives on the inbox calendar with its current state. It stays there after retention deletes the email, so an agent can still ask what was booked for tomorrow.
msg = inbox.wait_for_message(timeout=60)
invite = msg.calendar_event # None when the mail carries no invitation
event = inbox.calendar_event(invite["event_id"])
print(event["title"], event["start"], event["my_status"])

Answer it

The answer goes back to the organizer as a standard calendar reply, so their Google, Outlook or Apple calendar shows it. It is sent like any mail from the inbox and is not retried on its own, because a retry after a lost response would answer twice.
inbox.rsvp(event["id"], "accepted", comment="See you then")

Schedule a meeting

The inbox is the organizer. Give the time with an offset, or without one plus a time zone name; a time that could mean two different hours is refused rather than guessed. Add a video link as the location: MailFlat does not create one for you.
res = inbox.create_calendar_event(
"Intro call", "2026-10-06T14:00:00", timezone="America/New_York",
duration_minutes=30, attendees=["ali@example.com"],
location="https://meet.google.com/abc-defg-hij")
event_id = res["event"]["id"]

Move it or cancel it

Pass only what changes. The same event updates in every guest's calendar, with no duplicate, and a new time asks everyone again. A cancellation removes it from their calendars.
inbox.update_calendar_event(event_id, start="2026-10-07T14:00:00-04:00")
inbox.cancel_calendar_event(event_id, message="Something came up")

Know when guests answer

Add a webhook in the dashboard (Webhooks) with the event calendar.* and your endpoint hears about every change: calendar.event.created, updated and cancelled for invitations the inbox receives, and calendar.attendee.responded when a guest answers a meeting it organized.
Python
@app.post("/mailflat")
def mailflat_hook(payload: dict):
if payload["event"] == "calendar.attendee.responded":
ev = payload["calendar_event"]
going = [a["email"] for a in ev["attendees"] if a["status"] == "accepted"]
print(ev["title"], "confirmed by", going)

Let a person follow the calendar

Hand a person the read-only subscribe link and they see the inbox's meetings, guests and answers in their own calendar app. Every call returns the same link, so sharing it twice never breaks a subscription; rotate it if it leaks. Apple Calendar and Outlook refresh it often, Google every 8 to 24 hours, so use the API above for live state.
feed = inbox.calendar_feed()
print(feed["subscribe_links"]["google"]) # also apple, outlook
inbox.rotate_calendar_feed() # the link leaked: the old one stops at once

Worth knowing

Prefer clicking? Use the dashboard
Open an inbox and choose the Calendar tab. New meeting sends invitations from the inbox, each meeting row lets you edit or cancel it, invitations you received have Accept and Decline, and the subscribe strip adds the calendar to Google, Apple or Outlook in one click. A booking page where people pick a free slot is not part of this, and recurring meetings are shown as one entry that repeats.