GitHub sends a notification email for nearly every event you subscribe to — a new
issue, a pull request review request, a comment that @-mentions you. The signal you
actually want (which repo, which PR, who acted, and why you were notified) is spread
across the Subject, the X-GitHub-Reason header, and the message footer. MailFrame
extracts those fields into a typed, schema-validated JSON object you can route to a
queue, a chat bot, or your own triage dashboard.
It works on the notification emails GitHub sends for issues, pull requests,
discussions, mentions, and review requests — the plain-text and HTML parts of the
raw message you receive at notifications@github.com.
Fields MailFrame extracts
| Field | Type | Example | Notes |
|---|---|---|---|
repository | string | octocat/Hello-World | owner/repo, taken from the subject and List-ID header |
thread_type | enum | pull_request | One of issue, pull_request, discussion, commit, release |
thread_number | integer | 482 | Issue, PR, or discussion number parsed from the subject |
thread_title | string | Add retry backoff to webhook sender | Subject line with the Re:, repo, and #number stripped |
actor | string | alex-rivera | Login or display name of the user who triggered the event, from the From header and footer |
action | enum | commented | One of opened, closed, merged, commented, reopened, assigned, review_requested |
reason | enum | mention | Why you were notified, from X-GitHub-Reason: mention, team_mention, review_requested, assign, author, comment, subscribed, ci_activity |
url | string | https://github.com/octocat/Hello-World/pull/482#issuecomment-1234567890 | Canonical link from the “view it on GitHub” footer |
body_preview | string | @jordan can you take a look at the backoff logic here? | First line of the comment or event body |
date | string | 2026-05-21 | Notification date normalized to ISO 8601 |
Sample input
A typical GitHub notification email — here, a comment on a pull request that mentions you — looks like this:
From: Alex Rivera <notifications@github.com>
Subject: Re: [octocat/Hello-World] Add retry backoff to webhook sender (#482)
Date: Thu, 21 May 2026 09:14:00 -0700
To: octocat/Hello-World <Hello-World@noreply.github.com>
Cc: Jordan <jordan@example.com>, Mention <mention@noreply.github.com>
Message-ID: <octocat/Hello-World/pull/482/c1234567890@github.com>
X-GitHub-Reason: mention
List-ID: octocat/Hello-World <Hello-World.octocat.github.com>
@jordan can you take a look at the backoff logic here? The retry loop
doubles the delay but never caps it, so a flaky endpoint could stall the
whole worker.
—
Reply to this email directly, or view it on GitHub:
https://github.com/octocat/Hello-World/pull/482#issuecomment-1234567890
You are receiving this because you were mentioned.
Structured JSON output
{
"repository": "octocat/Hello-World",
"thread_type": "pull_request",
"thread_number": 482,
"thread_title": "Add retry backoff to webhook sender",
"actor": "alex-rivera",
"action": "commented",
"reason": "mention",
"url": "https://github.com/octocat/Hello-World/pull/482#issuecomment-1234567890",
"body_preview": "@jordan can you take a look at the backoff logic here?",
"date": "2026-05-21"
}
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:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "github_notification",
"type": "object",
"required": ["repository", "thread_type", "actor", "reason"],
"properties": {
"repository": { "type": "string", "pattern": "^[^/]+/[^/]+$" },
"thread_type": { "type": "string", "enum": ["issue", "pull_request", "discussion", "commit", "release"] },
"thread_number": { "type": "integer", "minimum": 1 },
"thread_title": { "type": "string" },
"actor": { "type": "string" },
"action": { "type": "string", "enum": ["opened", "closed", "merged", "commented", "reopened", "assigned", "review_requested"] },
"reason": { "type": "string", "enum": ["mention", "team_mention", "review_requested", "assign", "author", "comment", "subscribed", "ci_activity"] },
"url": { "type": "string", "format": "uri" },
"body_preview": { "type": "string" },
"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:
curl https://api.mailframe.ai/v1/parse \
-H "Authorization: Bearer $MAILFRAME_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"schema": "github_notification",
"input": { "type": "email", "raw": "<base64-encoded MIME>" }
}'
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 — route it to
a queue, a chat bot, or your own triage dashboard.
Prefer not to relay the email yourself? Inbox forwarding — pointing a Gmail or
Outlook filter for notifications@github.com at a unique inbox address MailFrame
assigns you — is on the roadmap. Until it ships, POST the raw email to /v1/parse
as shown above.
On the roadmap
Today every parse happens synchronously over the API call above. Asynchronous webhook delivery — having MailFrame POST a signed envelope to your endpoint when a parse completes, instead of you reading it from the HTTP response — is planned, alongside inbox forwarding. Both are roadmap items, not shipped features.