Every ride you take and every meal you order sends a receipt: an Uber or Lyft
trip summary the moment you step out of the car, a DoorDash or Grubhub order
confirmation when the courier drops the bag at your door, an Uber Eats or
Instacart receipt with the itemized subtotal, tax, and tip. The emails come
from receipts@uber.com, no-reply@lyftmail.com, no-reply@doordash.com,
receipts@grubhub.com, and dozens of regional and white-labeled variants, and
each one writes the receipt in its own template — but the fields you actually
want are the same handful: which service, the trip or order ID, the fare or
subtotal, the tax and tip, the total and its currency, where the trip started
and ended (or which merchant filled the order), who drove or delivered, and
when it happened.
MailFrame extracts those fields from any rideshare or delivery receipt into
typed, schema-validated JSON you can route straight into an expense report
(Expensify, Ramp, Brex, a custom T&E pipeline), a personal-spending tracker,
or a mileage- and meal-reimbursement workflow. Match on the message body and
the service-specific wording (“Thanks for riding”, “Your order from”, “Total”,
“You rode with”, “Your Dasher”) rather than the From address alone — these
platforms relay receipts through notification-platform and tenant-specific
domains that drift over time. The service, transaction ID, fare, subtotal,
tax, tip, total, currency, pickup/dropoff or merchant, driver/courier, and
timestamp all normalize into one consistent shape regardless of which app sent
the receipt. PDF, image, and calendar-attachment input are on the roadmap;
inbox forwarding is planned.
Fields MailFrame extracts
| Field | Type | Example | Notes |
|---|---|---|---|
service | enum | uber | One of uber, lyft, doordash, grubhub, uber_eats, instacart, unknown |
service_category | enum | rideshare | One of rideshare, delivery, unknown — rideshare for point-to-point trips, delivery for food/grocery/parcel orders |
transaction_id | string | 7f3c9a12-4b8e-4c21-9d6a-1e2f3a4b5c6d | The trip ID (rideshare) or order/delivery ID (delivery) — the dedup key for a single ride or order |
status | enum | completed | One of completed, canceled, adjusted, refunded, in_progress, unknown |
fare | number | 18.2 | Base trip fare before booking fees, tolls, and tip (rideshare); omitted for delivery receipts that only itemize a subtotal |
subtotal | number | 20.84 | Sum of line items before tip — trip charges for rideshare, item subtotal for delivery |
tax | number | 1.87 | Sales tax on the order, when the receipt breaks it out; 0 when bundled into fees |
tip | number | 3 | Tip to the driver or courier, when the receipt shows it separately |
fees | number | 2.64 | Booking, service, delivery, and small-order fees combined |
total | number | 23.84 | Grand total charged to the payment method |
currency | string | USD | ISO 4217 currency code on the receipt; USD, EUR, GBP, CAD, AUD, etc. |
pickup_location | string | 1200 Market St, San Francisco, CA | Trip origin (rideshare); blank for delivery |
dropoff_location | string | 500 Terry Francois Blvd, San Francisco, CA | Trip destination (rideshare) or delivery address |
merchant_name | string | Tartine Bakery | The restaurant or store that filled the order (delivery); blank for rideshare |
driver_name | string | Marcus | First name of the driver (rideshare) or courier/Dasher (delivery), as printed |
vehicle | string | UberX · Toyota Prius | Service tier and vehicle description (rideshare), when present |
distance | string | 3.4 mi | Trip distance as printed on the receipt (rideshare), when present |
duration | string | 18 min | Trip or delivery duration, when present |
item_count | number | 3 | Number of line items on a delivery order, when present |
payment_method | string | Visa ••••4242 | Masked payment method the charge landed on |
receipt_url | string | https://riders.uber.com/trips/7f3c9a12 | Link to the full receipt or trip-detail page |
transaction_date | string | 2026-07-03T08:21:00-04:00 | Date/time the trip started or the order was placed, ISO 8601 |
date | string | 2026-07-03 | Date the email was sent, normalized to ISO 8601 |
Sample input
A typical Uber ride receipt is a multipart message with a plain-text and an HTML alternative:
From: Uber Receipts <receipts@uber.com>
Subject: Your Thursday morning trip with Uber
Date: Thu, 03 Jul 2026 08:47:12 -0400
To: jordan.rivera@example.com
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="b1_a3f9c2e1"
--b1_a3f9c2e1
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: 7bit
Thanks for riding, Jordan
Total $23.84
July 3, 2026 | 8:21 AM
Trip ID: 7f3c9a12-4b8e-4c21-9d6a-1e2f3a4b5c6d
Pickup 8:21 AM 1200 Market St, San Francisco, CA
Dropoff 8:39 AM 500 Terry Francois Blvd, San Francisco, CA
18 min | 3.4 mi
Trip fare $18.20
Booking fee $2.10
Wait time $0.54
Subtotal $20.84
Tip $3.00
Total $23.84
You rode with Marcus
UberX · Toyota Prius · 7KRK••
Visa ••••4242
View receipt: https://riders.uber.com/trips/7f3c9a12
--b1_a3f9c2e1
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<html><body><h1>Thanks for riding, Jordan</h1>
<p>Total <strong>$23.84</strong> =E2=80=94 July 3, 2026</p>
</body></html>
--b1_a3f9c2e1--
The same shape is produced for a DoorDash or Grubhub delivery receipt —
service_category switches to delivery, merchant_name carries the
restaurant, dropoff_location carries the delivery address, driver_name
carries the courier, and item_count counts the line items — and the rest of
the fields normalize to the same JSON keys regardless of which app sent the
receipt.
Structured JSON output
{
"service": "uber",
"service_category": "rideshare",
"transaction_id": "7f3c9a12-4b8e-4c21-9d6a-1e2f3a4b5c6d",
"status": "completed",
"fare": 18.2,
"subtotal": 20.84,
"tax": 0,
"tip": 3,
"fees": 2.64,
"total": 23.84,
"currency": "USD",
"pickup_location": "1200 Market St, San Francisco, CA",
"dropoff_location": "500 Terry Francois Blvd, San Francisco, CA",
"driver_name": "Marcus",
"vehicle": "UberX · Toyota Prius",
"distance": "3.4 mi",
"duration": "18 min",
"payment_method": "Visa ••••4242",
"receipt_url": "https://riders.uber.com/trips/7f3c9a12",
"transaction_date": "2026-07-03T08:21:00-04:00",
"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_name when you only route delivery
receipts, or restrict service_category to {"rideshare"} if you only
reconcile trip fares for a mileage-reimbursement workflow:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "rideshare_delivery_receipt",
"type": "object",
"required": [
"service",
"service_category",
"total",
"currency"
],
"properties": {
"service": {
"type": "string",
"enum": ["uber", "lyft", "doordash", "grubhub", "uber_eats", "instacart", "unknown"]
},
"service_category": {
"type": "string",
"enum": ["rideshare", "delivery", "unknown"]
},
"transaction_id": { "type": "string" },
"status": {
"type": "string",
"enum": ["completed", "canceled", "adjusted", "refunded", "in_progress", "unknown"]
},
"fare": { "type": "number", "minimum": 0 },
"subtotal": { "type": "number", "minimum": 0 },
"tax": { "type": "number", "minimum": 0 },
"tip": { "type": "number", "minimum": 0 },
"fees": { "type": "number", "minimum": 0 },
"total": { "type": "number", "minimum": 0 },
"currency": { "type": "string", "minLength": 3, "maxLength": 3 },
"pickup_location": { "type": "string" },
"dropoff_location":{ "type": "string" },
"merchant_name": { "type": "string" },
"driver_name": { "type": "string" },
"vehicle": { "type": "string" },
"distance": { "type": "string" },
"duration": { "type": "string" },
"item_count": { "type": "integer", "minimum": 0 },
"payment_method": { "type": "string" },
"receipt_url": { "type": "string", "format": "uri" },
"transaction_date":{ "type": "string", "format": "date-time" },
"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": "rideshare_delivery_receipt",
"raw_mime": "From: Uber Receipts <receipts@uber.com>\r\nTo: jordan.rivera@example.com\r\nSubject: Your Thursday morning trip with Uber\r\nDate: Thu, 03 Jul 2026 08:47:12 -0400\r\n\r\nThanks for riding, Jordan\r\n\r\nTotal $23.84\r\nJuly 3, 2026 | 8:21 AM\r\n\r\nTrip ID: 7f3c9a12-4b8e-4c21-9d6a-1e2f3a4b5c6d\r\n\r\nPickup 8:21 AM 1200 Market St, San Francisco, CA\r\nDropoff 8:39 AM 500 Terry Francois Blvd, San Francisco, CA\r\n18 min | 3.4 mi\r\n\r\nTrip fare $18.20\r\nBooking fee $2.10\r\nWait time $0.54\r\nSubtotal $20.84\r\nTip $3.00\r\nTotal $23.84\r\n\r\nYou rode with Marcus\r\nUberX · Toyota Prius\r\n\r\nVisa ••••4242\r\n\r\nView receipt: https://riders.uber.com/trips/7f3c9a12"
}'
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 — attach the receipt to an open expense report, add the
fare to a mileage log, or drop the order onto a spending dashboard. The
response wraps the extracted data alongside the parse status and any schema
validation errors:
{
"id": "parse_9c41e7",
"status": "completed",
"validation_errors": [],
"data": {
"service": "uber",
"service_category": "rideshare",
"transaction_id": "7f3c9a12-4b8e-4c21-9d6a-1e2f3a4b5c6d",
"status": "completed",
"fare": 18.2,
"subtotal": 20.84,
"tax": 0,
"tip": 3,
"fees": 2.64,
"total": 23.84,
"currency": "USD",
"pickup_location": "1200 Market St, San Francisco, CA",
"dropoff_location": "500 Terry Francois Blvd, San Francisco, CA",
"driver_name": "Marcus",
"vehicle": "UberX · Toyota Prius",
"distance": "3.4 mi",
"duration": "18 min",
"payment_method": "Visa ••••4242",
"receipt_url": "https://riders.uber.com/trips/7f3c9a12",
"transaction_date": "2026-07-03T08:21:00-04:00",
"date": "2026-07-03"
}
}
Routing on the result
The most useful split is by service_category: rideshare receipts feed a
mileage- and travel-reimbursement lane (the pickup_location,
dropoff_location, distance, and duration are what a T&E policy checks),
while delivery receipts feed a meals-and-entertainment lane (the
merchant_name, item_count, and subtotal are what an expense category
needs). Inside those, status decides handling: a completed receipt is a
routine expense, while a canceled, adjusted, or refunded receipt should
reconcile against the original charge rather than create a new one. Use
transaction_id as the deduplication key — a single ride or order can produce
both an initial receipt and a later adjusted correction (a fare change, a
tip added after the trip, a partial refund), and pairing on transaction_id
keeps one row per real trip or order while letting you fold the correction
into it. When transaction_id is absent, fall back to
(service, total, transaction_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. The webhook body wraps the
same data payload in a delivery envelope; verify the signature over the raw
request body before you trust it:
{
"id": "parse_9c41e7",
"status": "completed",
"validation_errors": [],
"data": {
"service": "uber",
"service_category": "rideshare",
"transaction_id": "7f3c9a12-4b8e-4c21-9d6a-1e2f3a4b5c6d",
"status": "completed",
"total": 23.84,
"currency": "USD",
"pickup_location": "1200 Market St, San Francisco, CA",
"dropoff_location": "500 Terry Francois Blvd, San Francisco, CA",
"driver_name": "Marcus",
"transaction_date": "2026-07-03T08:21:00-04:00",
"date": "2026-07-03"
}
}
The signature header looks like MailFrame-Signature: t=1751541440,v1=3f9a…;
recompute HMAC-SHA256(secret, "{t}." + raw_body) and compare in constant
time. 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 Shipping & Delivery Notifications schema, the Parse Travel Booking Confirmations 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 Rideshare & Delivery Receipt Emails to JSON.