PayPal emails a receipt for every payment sent and received, but the transaction ID, fee breakdown, and net amount are scattered across inconsistent HTML layouts depending on whether the payment was personal, commercial, or a refund. MailFrame extracts all of it into a single typed JSON object, consistently.
It works on “You sent a payment”, “Receipt for your payment to”, and refund confirmation emails from PayPal.
Fields MailFrame extracts
| Field | Type | Example | Notes |
|---|---|---|---|
transaction_id | string | 8XJ12345AB678901C | PayPal transaction ID |
amount_cents | integer | 5000 | Gross payment amount in minor units |
currency | string | usd | ISO 4217, lower-cased |
fee_cents | integer | 175 | PayPal processing fee in minor units |
net_cents | integer | 4825 | Amount after fees (amount_cents - fee_cents) |
payer_email | string | sam@example.com | Email of the sender |
recipient_email | string | shop@acmeco.com | Email of the recipient |
payment_status | enum | completed | One of completed, pending, refunded |
date | string | 2026-05-21 | Transaction date normalized to ISO 8601 |
Sample input
A typical PayPal receipt email looks like this:
From: service@paypal.com
Subject: Receipt for your payment to Acme Co.
Date: Wed, 21 May 2026 11:45:00 -0700
To: sam@example.com
Hello Sam,
You sent a payment of $50.00 USD to Acme Co. (shop@acmeco.com).
Transaction ID: 8XJ12345AB678901C
Date: May 21, 2026, 11:45 AM PDT
Payment details:
Amount sent: $50.00
PayPal fee: $1.75
Net amount: $48.25
Payment method: PayPal balance
Status: Completed
Structured JSON output
{
"transaction_id": "8XJ12345AB678901C",
"amount_cents": 5000,
"currency": "usd",
"fee_cents": 175,
"net_cents": 4825,
"payer_email": "sam@example.com",
"recipient_email": "shop@acmeco.com",
"payment_status": "completed",
"date": "2026-05-21"
}
JSON Schema definition
Every field is validated against the schema before delivery. 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": "paypal_receipt",
"type": "object",
"required": ["transaction_id", "amount_cents", "currency", "payer_email", "recipient_email", "payment_status"],
"properties": {
"transaction_id": { "type": "string", "pattern": "^[A-Z0-9]{17}$" },
"amount_cents": { "type": "integer", "minimum": 0 },
"currency": { "type": "string", "minLength": 3, "maxLength": 3 },
"fee_cents": { "type": "integer", "minimum": 0 },
"net_cents": { "type": "integer", "minimum": 0 },
"payer_email": { "type": "string", "format": "email" },
"recipient_email": { "type": "string", "format": "email" },
"payment_status": { "type": "string", "enum": ["completed", "pending", "refunded"] },
"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": "paypal_receipt",
"input": { "type": "email", "raw": "<base64-encoded MIME>" }
}'
Prefer zero manual steps? Inbox forwarding — pointing PayPal’s notification forwarding
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.
Webhook delivery
When extraction completes, MailFrame POSTs a signed envelope to your endpoint. The
signature is an HMAC-SHA256 of the raw body using your webhook secret, sent in the
MailFrame-Signature header, with exponential-backoff retries on failure.
{
"event": "parse.completed",
"parse_id": "parse_d9e2c1",
"schema": "paypal_receipt",
"data": {
"transaction_id": "8XJ12345AB678901C",
"amount_cents": 5000,
"currency": "usd",
"fee_cents": 175,
"net_cents": 4825,
"payer_email": "sam@example.com",
"recipient_email": "shop@acmeco.com",
"payment_status": "completed",
"date": "2026-05-21"
},
"confidence": { "transaction_id": 0.99, "amount_cents": 0.99, "fee_cents": 0.96 },
"received_at": "2026-05-21T18:45:07Z"
}