Every modern bank and card issuer sends a real-time alert when money moves on your account: a debit-card tap at a coffee shop, a wire out to a vendor, a payroll deposit, an ATM withdrawal, a recurring charge, a fraud hold. The emails come from Chase, Bank of America, Wells Fargo, Capital One, Citi, USAA, PNC, TD Bank, Discover, American Express, HSBC, Barclays, Santander, Revolut, Monzo, Starling, and dozens of regional banks and credit unions. Each one writes the alert in its own template, but the fields you actually want are the same handful: what type of transaction, how much, in which currency, on which account, who the merchant or counterparty is, when it posted, and what the running balance is now.
MailFrame extracts those fields from any bank or card transaction alert into
typed, schema-validated JSON you can route straight into a personal-finance
tracker (YNAB, Monarch, Copilot Money, Lunch Money, Firefly III), an expense
or AP pipeline (Brex, Ramp, Expensify, a custom reconciliation job), or a
fraud-monitoring queue. Match on the message body and the bank-specific
wording (“a charge of”, “has been authorized on your card”, “your deposit
has posted”, “you withdrew”) rather than the From address alone — banks
frequently relay alerts through tenant-specific, white-labeled, or
notification-platform domains (no-reply@alert.chase.com,
alerts@bofa.com, noreply@capitalone.com, transaction@wise.com,
alerts@revolut.com) and the sender domain drifts over time. The
transaction type, amount, currency, merchant, account mask, posted date, and
running balance all normalize into one consistent shape regardless of which
bank sent the message. PDF, image, and calendar-attachment input are on the
roadmap; inbox forwarding is planned.
Fields MailFrame extracts
| Field | Type | Example | Notes |
|---|---|---|---|
transaction_type | enum | purchase | One of purchase, withdrawal, deposit, transfer, payment, refund, fee, interest, chargeback, fraud_hold, authorization, unknown |
direction | enum | debit | One of debit, credit — debit for money leaving the account (purchase, withdrawal, fee, payment), credit for money arriving (deposit, refund, interest) |
amount | number | 42.17 | Transaction amount as a decimal; positive in both directions (sign comes from direction) |
currency | string | USD | ISO 4217 currency code on the alert; USD, EUR, GBP, CAD, AUD, JPY, etc. |
balance | number | 4312.55 | Account balance immediately after the transaction posted, when the bank includes it |
account_mask | string | ••••1234 | Last 4 (sometimes 5) digits of the card or account number, with or without the leading bullets — the dedup key for a cardholder’s accounts |
account_type | enum | credit_card | One of checking, savings, credit_card, debit_card, line_of_credit, investment, unknown |
merchant_name | string | Blue Bottle Coffee | The counterparty as printed on the alert — the merchant for a purchase, the payer for a deposit, the recipient for a transfer |
merchant_category | string | 5814 | Merchant Category Code (MCC) when the bank includes one; blank string when not present |
merchant_location | string | San Francisco, CA | City / state / country from the bank when it includes transaction location data |
authorization_code | string | 048221 | Bank or card-network authorization code on the alert, when present |
posted_date | string | 2026-07-03 | Date the transaction posted to the account, normalized to ISO 8601 |
transaction_date | string | 2026-07-03T09:12:00-04:00 | Date/time the transaction was initiated (often slightly earlier than posted_date), ISO 8601 |
payment_method | enum | card | One of card, ach, wire, check, cash, transfer, unknown |
card_brand | enum | visa | One of visa, mastercard, amex, discover, diners, jcb, unionpay, unknown |
reference_id | string | CHK-20260703-018421 | Bank’s internal reference / confirmation number, when present |
notes | string | Recurring monthly charge | Free-text note or memo line from the alert, when present |
alert_url | string | https://... | Link to the bank’s transaction-detail page when the alert includes one |
date | string | 2026-07-03 | Date the email was sent, normalized to ISO 8601 |
Sample input
A typical Chase credit-card purchase alert looks like this:
From: Chase Alerts <no-reply@alert.chase.com>
Subject: A charge of $42.17 was made on your credit card
Date: Thu, 03 Jul 2026 09:12:00 -0400
To: jordan.rivera@example.com
A charge of $42.17 was made on your Chase credit card ending in 1234
on July 3, 2026 at Blue Bottle Coffee in San Francisco, CA.
Your current available balance is $4,312.55.
Authorization code: 048221
Manage alerts: https://chase.com/online-banking/alert/1234
The same shape is produced for direct deposits, ATM withdrawals, transfers,
fees, refunds, fraud holds, and card authorizations from every major US and
EU bank — transaction_type distinguishes a purchase from a withdrawal,
deposit, transfer, payment, refund, fee, interest,
chargeback, fraud_hold, or authorization — and the rest of the fields
normalize to the same JSON keys regardless of which bank or card issuer
sent the message.
Structured JSON output
{
"transaction_type": "purchase",
"direction": "debit",
"amount": 42.17,
"currency": "USD",
"balance": 4312.55,
"account_mask": "••••1234",
"account_type": "credit_card",
"merchant_name": "Blue Bottle Coffee",
"merchant_category": "",
"merchant_location": "San Francisco, CA",
"authorization_code": "048221",
"posted_date": "2026-07-03",
"transaction_date": "2026-07-03T09:12:00-04:00",
"payment_method": "card",
"card_brand": "visa",
"alert_url": "https://chase.com/online-banking/alert/1234",
"date": "2026-07-03"
}
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 merchant_category to be a 4-digit string
when you reconcile to MCC data, or restrict transaction_type to
{"purchase", "deposit", "withdrawal"} if you only care about cash
flow on a personal checking account:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "bank_transaction",
"type": "object",
"required": [
"transaction_type",
"direction",
"amount",
"currency",
"account_type"
],
"properties": {
"transaction_type": {
"type": "string",
"enum": [
"purchase",
"withdrawal",
"deposit",
"transfer",
"payment",
"refund",
"fee",
"interest",
"chargeback",
"fraud_hold",
"authorization",
"unknown"
]
},
"direction": {
"type": "string",
"enum": ["debit", "credit"]
},
"amount": { "type": "number", "minimum": 0 },
"currency": { "type": "string", "minLength": 3, "maxLength": 3 },
"balance": { "type": "number" },
"account_mask":{ "type": "string" },
"account_type": {
"type": "string",
"enum": [
"checking",
"savings",
"credit_card",
"debit_card",
"line_of_credit",
"investment",
"unknown"
]
},
"merchant_name": { "type": "string" },
"merchant_category": { "type": "string" },
"merchant_location": { "type": "string" },
"authorization_code":{ "type": "string" },
"posted_date": { "type": "string", "format": "date" },
"transaction_date": { "type": "string", "format": "date-time" },
"payment_method": {
"type": "string",
"enum": ["card", "ach", "wire", "check", "cash", "transfer", "unknown"]
},
"card_brand": {
"type": "string",
"enum": [
"visa",
"mastercard",
"amex",
"discover",
"diners",
"jcb",
"unionpay",
"unknown"
]
},
"reference_id": { "type": "string" },
"notes": { "type": "string" },
"alert_url": { "type": "string", "format": "uri" },
"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_id": "bank_transaction",
"raw_mime": "From: Chase Alerts <no-reply@alert.chase.com>\r\nTo: jordan.rivera@example.com\r\nSubject: A charge of $42.17 was made on your credit card\r\nDate: Thu, 03 Jul 2026 09:12:00 -0400\r\n\r\nA charge of $42.17 was made on your Chase credit card ending in 1234\r\non July 3, 2026 at Blue Bottle Coffee in San Francisco, CA.\r\n\r\nYour current available balance is $4,312.55.\r\nAuthorization code: 048221\r\n\r\nManage alerts: https://chase.com/online-banking/alert/1234"
}'
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 — append the transaction to a personal-finance
spreadsheet, drop a deposit onto a cash-flow dashboard, or page on a
fraud_hold while it is still actionable.
Routing on the result
The most useful splits are by direction and transaction_type. The
first is the broad bucket: credit (deposits, refunds, interest) goes
to a cash-flow or income lane; debit (purchases, withdrawals, fees)
goes to a spending lane. Inside those, transaction_type lets you
route by intent: a deposit typically deserves its own alerting
channel (payroll landed, refund cleared, dividend posted); a
fraud_hold or chargeback is high-signal and should page; a fee
or interest entry is a low-priority ledger update. Use account_mask
plus posted_date as the dedup key — the same transaction can show up
twice in your inbox (an authorization email when the merchant
pre-auths, then a purchase email when it actually posts) and you
want one row per real movement, not two. When account_mask is
absent, fall back to (merchant_name, amount, currency, posted_date).
Signed webhook delivery
Prefer asynchronous delivery? Signed webhook delivery — where MailFrame POSTs
the extraction result to your endpoint with an HMAC-SHA256 signature in the
MailFrame-Signature header and exponential-backoff retries — is available
during early access alongside the synchronous API. 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 Parse Subscription Renewal Emails schema, the Parse Invoice & Payment-Due Emails schema, and the email-to-JSON API guide for a full walkthrough of POST /v1/parse. For a step-by-step tutorial that wires this schema into a real
/v1/parserequest, see Parse Bank & Card Transaction Emails to JSON.