Google Calendar, Outlook, and iCloud send a fresh meeting-invite email the moment someone books time with you — and every one of them lays the event out differently. The fields a calendar sync, a CRM activity log, or a meeting-prep assistant needs are always the same: what’s the event, who’s running it, who’s attending, when (and in what time zone), and where. MailFrame extracts those fields from the invite email into a typed, schema-validated JSON object you can route straight into a calendar backend, a meeting-prep workflow, or a customer-engagement timeline.
It works on the body of meeting-invite, update, and cancellation emails from
Google Calendar (typically calendar-notification@google.com), Microsoft
Outlook (often outlook@office365.com for cloud tenants or an Exchange
on-prem address for self-hosted), and Apple iCloud
(noreply@caldav.icloud.com). Match on the invite body and the embedded
iCalendar parts (Outlook BEGIN:VCALENDAR, Google text/calendar invite
attachment) rather than the From address alone — providers can relay
invites through tenant-specific addresses. Subject lines, start/end times with the original
time zone, organizer, attendees, location (including conferencing links), and
response status are normalized into one consistent shape regardless of which
provider sent the message.
Fields MailFrame extracts
| Field | Type | Example | Notes |
|---|---|---|---|
event_title | string | Q3 roadmap review | The meeting subject, as written on the invite |
event_uid | string | 040000008200E00074C5B7101A82E00800000000B0...@google.com | Provider’s event UID when present — stable join key for updates and cancellations |
invitation_type | enum | invitation | One of invitation, update, cancellation |
organizer_email | string | amelia.chen@example.com | The organizer’s address |
organizer_name | string | Amelia Chen | The organizer’s display name when present |
attendees | array | see below | One entry per invitee, with name, email, and response status |
start_datetime | string | 2026-07-08T14:00:00-04:00 | Event start in ISO 8601 with offset |
end_datetime | string | 2026-07-08T15:00:00-04:00 | Event end in ISO 8601 with offset |
timezone | string | America/New_York | IANA time zone when present; otherwise the offset is preserved on the datetime fields |
location | string | Conference Room 4B | Physical location or conferencing room name |
location_url | string | https://meet.google.com/abc-defg-hij | Video-conferencing link when present (Google Meet, Zoom, Teams, Webex) |
description | string | Review the Q3 OKR draft before the 1:1 with the exec team. | The invite body, lightly trimmed |
is_recurring | boolean | false | true when the invite is part of a recurring series |
response_status | enum | needs_action | One of accepted, declined, tentative, needs_action |
date | string | 2026-07-07 | Date the invite was sent, normalized to ISO 8601 |
Each entry in attendees carries the same shape regardless of provider:
| Field | Type | Example | Notes |
|---|---|---|---|
email | string | jordan.rivera@example.com | Invitee address |
name | string | Jordan Rivera | Invitee display name when present |
required | boolean | true | false for optional/CC’d attendees |
response_status | enum | accepted | One of accepted, declined, tentative, needs_action |
Sample input
A typical Google Calendar invitation email looks like this:
From: amelia.chen@example.com
Subject: Invitation: Q3 roadmap review @ Wed Jul 8, 2026 2pm - 3pm (EDT)
Date: Tue, 07 Jul 2026 16:42:00 -0400
To: jordan.rivera@example.com
Amelia Chen has invited you to the following event:
Title: Q3 roadmap review
When: Wed Jul 8, 2026 14:00 - 15:00 (EDT)
Where: https://meet.google.com/abc-defg-hij
Organizer: Amelia Chen <amelia.chen@example.com>
Attendees: Amelia Chen (organizer), Jordan Rivera, Priya Shah
Description:
Review the Q3 OKR draft before the 1:1 with the exec team.
Do you want to accept?
https://www.google.com/calendar/event?eid=...
The same shape is produced for Outlook meeting-request messages
(outlook@office365.com, Exchange BEGIN:VCALENDAR parts) and iCloud
noreply@caldav.icloud.com invitations — invitation_type distinguishes
invitation from update and cancellation, and the rest of the fields
normalize to the same JSON keys.
Structured JSON output
{
"event_title": "Q3 roadmap review",
"event_uid": "040000008200E00074C5B7101A82E00800000000B0CAFED@example.com",
"invitation_type": "invitation",
"organizer_email": "amelia.chen@example.com",
"organizer_name": "Amelia Chen",
"attendees": [
{
"email": "amelia.chen@example.com",
"name": "Amelia Chen",
"required": true,
"response_status": "accepted"
},
{
"email": "jordan.rivera@example.com",
"name": "Jordan Rivera",
"required": true,
"response_status": "needs_action"
},
{
"email": "priya.shah@example.com",
"name": "Priya Shah",
"required": false,
"response_status": "needs_action"
}
],
"start_datetime": "2026-07-08T14:00:00-04:00",
"end_datetime": "2026-07-08T15:00:00-04:00",
"timezone": "America/New_York",
"location": "",
"location_url": "https://meet.google.com/abc-defg-hij",
"description": "Review the Q3 OKR draft before the 1:1 with the exec team.",
"is_recurring": false,
"response_status": "needs_action",
"date": "2026-07-07"
}
JSON Schema definition
Every field is validated against the schema before MailFrame returns it.
You can copy this as a starting point and tighten it for your own use
case — for example, require attendees if every event in your workflow
should carry at least one invitee, or restrict response_status to
{"accepted", "declined"} if you only care about confirmed outcomes:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "calendar_invite",
"type": "object",
"required": [
"event_title",
"invitation_type",
"start_datetime",
"end_datetime",
"organizer_email"
],
"properties": {
"event_title": { "type": "string", "minLength": 1 },
"event_uid": { "type": "string" },
"invitation_type": {
"type": "string",
"enum": ["invitation", "update", "cancellation"]
},
"organizer_email": { "type": "string", "format": "email" },
"organizer_name": { "type": "string" },
"attendees": {
"type": "array",
"items": {
"type": "object",
"required": ["email"],
"properties": {
"email": { "type": "string", "format": "email" },
"name": { "type": "string" },
"required": { "type": "boolean" },
"response_status": {
"type": "string",
"enum": ["accepted", "declined", "tentative", "needs_action"]
}
}
}
},
"start_datetime": { "type": "string", "format": "date-time" },
"end_datetime": { "type": "string", "format": "date-time" },
"timezone": { "type": "string" },
"location": { "type": "string" },
"location_url": { "type": "string", "format": "uri" },
"description": { "type": "string" },
"is_recurring": { "type": "boolean" },
"response_status": {
"type": "string",
"enum": ["accepted", "declined", "tentative", "needs_action"]
},
"date": { "type": "string", "format": "date" }
}
}
Parse via the API
POST the raw email (MIME or plain text) to /v1/parse with the schema you
want to extract against:
The example below sends this page’s Sample input as raw_mime.
curl https://api.mailframe.ai/v1/parse \
-H "Authorization: Bearer $MAILF...KEY" \
-H "Content-Type: application/json" \
-d '{
"schema_id": "calendar_invite",
"raw_mime": "From: amelia.chen@example.com\r\nTo: jordan.rivera@example.com\r\nSubject: Invitation: Q3 roadmap review @ Wed Jul 8, 2026 2pm - 3pm (EDT)\r\n\r\nAmelia Chen has invited you to the following event:"
}'
The call is synchronous: /v1/parse validates the extraction against
your schema and returns the typed JSON in the HTTP response, so you can
act on it inline — push the event into your calendar backend, surface a
meeting-prep briefing, or trigger a follow-up workflow on cancellation.
Routing on the result
The most useful split is by invitation_type: any value of
{"invitation", "update"} should refresh the local calendar entry keyed
on event_uid (carrying forward start_datetime, end_datetime,
location_url, and the per-attendee response_status), while
cancellation should soft-delete the local entry keyed on the same
event_uid rather than removing it from history. Use event_uid as the
deduplication key — providers send one message per change, so a single
event in your calendar will produce several messages over its lifetime
(invitation, update, cancellation), and the same event_uid is the
right join across all of them.
Signed webhook delivery
Signed async webhook delivery — where MailFrame POSTs a signed envelope
to your endpoint when a parse completes — is available during early
access alongside the synchronous API. Each delivery carries an
HMAC-SHA256 signature in the MailFrame-Signature header for
verification, with exponential-backoff retries on failure. Inbox
forwarding — pointing a Gmail or Outlook filter at a unique inbox
address MailFrame assigns you — is on the roadmap.
Working with related email types? See the shipping notification schema for carrier tracking, the travel booking schema for flight/hotel/car confirmations, and the email-to-JSON API guide for a full walkthrough of
POST /v1/parse.