Every Amazon purchase — from a USB-C cable to a new laptop — generates a confirmation email, a shipping notification, and a delivery alert. The data is there: order ID, every line item, the total, the estimated delivery date, and the seller. But it’s buried in dense HTML that changes across Amazon’s storefronts (Amazon.com, Amazon.co.uk, Amazon.de, etc.) and across email types (order confirmation, shipped, delivered, refund).
MailFrame turns those emails into a single typed JSON object you can write straight to your order-tracking system, expense pipeline, or customer-support dashboard.
The Amazon Orders schema page covers the full field reference; this tutorial walks through parsing a real Amazon order email end to end, handling multi-item orders, and wiring the result into your backend.
What you get
MailFrame’s amazon_order schema extracts these fields from every Amazon order
email:
| Field | Type | Example | Notes |
|---|---|---|---|
order_id | string | 113-1234567-1234567 | Amazon order ID in NNN-NNNNNNN-NNNNNNN format |
items | array | see output | Each item: title, quantity, price_cents |
order_total_cents | integer | 3498 | Grand total charged in minor units |
currency | string | usd | ISO 4217, lower-cased |
ship_to_name | string | Morgan Chen | Recipient name on the shipping address |
estimated_delivery | string | 2026-05-24 | Estimated or confirmed delivery date, ISO 8601 |
order_date | string | 2026-05-21 | Date the order was placed, ISO 8601 |
seller | string | Amazon.com | Fulfilled by Amazon or third-party seller name |
Sample input
A typical Amazon order confirmation email looks like this:
From: auto-confirm@amazon.com
Subject: Your Amazon.com order of "USB-C Charging Cable..." has shipped!
Date: Thu, 21 May 2026 09:14:00 -0800
To: morgan@example.com
Hello Morgan,
Your package is on its way!
Order #113-1234567-1234567
Placed: May 21, 2026
Estimated delivery: Sunday, May 24, 2026
Items shipped:
Anker USB-C Charging Cable 6ft (2-Pack) Qty: 1 $13.00
Screen Cleaning Kit Qty: 2 $10.99 each
Order total: $34.98
Sold by: Amazon.com
Track your package: https://www.amazon.com/progress-tracker/...
Structured JSON output
Pass the raw email and the schema to MailFrame. You can reference the pre-built
schema by id (schema_id) or pass an inline JSON Schema. Here it is with curl,
using the built-in amazon_order schema against the confirmation above:
curl -X POST https://api.mailframe.ai/v1/parse \
-H "Authorization: Bearer $MAILF..._KEY" \
-H "Content-Type: application/json" \
-d '{
"schema_id": "amazon_order",
"raw_mime": "From: auto-confirm@amazon.com\r\nTo: morgan@example.com\r\nSubject: Your Amazon.com order of \"USB-C Charging Cable...\" has shipped!\r\nDate: Thu, 21 May 2026 09:14:00 -0800\r\n\r\nHello Morgan,\r\n\r\nYour package is on its way!\r\n\r\nOrder #113-1234567-1234567\r\nPlaced: May 21, 2026\r\nEstimated delivery: Sunday, May 24, 2026\r\n\r\nItems shipped:\r\n Anker USB-C Charging Cable 6ft (2-Pack) Qty: 1 $13.00\r\n Screen Cleaning Kit Qty: 2 $10.99 each\r\n\r\nOrder total: $34.98\r\nSold by: Amazon.com\r\n\r\nTrack your package: https://www.amazon.com/progress-tracker/..."
}'
The response wraps the extracted object in the standard MailFrame envelope:
{
"id": "parse_a1b4f2",
"status": "completed",
"validation_errors": [],
"data": {
"order_id": "113-1234567-1234567",
"items": [
{ "title": "Anker USB-C Charging Cable 6ft (2-Pack)", "quantity": 1, "price_cents": 1300 },
{ "title": "Screen Cleaning Kit", "quantity": 2, "price_cents": 1099 }
],
"order_total_cents": 3498,
"currency": "usd",
"ship_to_name": "Morgan Chen",
"estimated_delivery": "2026-05-24",
"order_date": "2026-05-21",
"seller": "Amazon.com"
}
}
TypeScript
const res = await fetch("https://api.mailframe.ai/v1/parse", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MAILFRAME_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
schema_id: "amazon_order",
raw_mime: rawEmail,
}),
});
const result = await res.json();
// result.data.order_id → "113-1234567-1234567"
// result.data.items[0].title → "Anker USB-C Charging Cable 6ft (2-Pack)"
// result.data.order_total_cents → 3498
Python
import os
import requests
resp = requests.post(
"https://api.mailframe.ai/v1/parse",
headers={
"Authorization": f"Bearer {os.environ['MAILFRAME_API_KEY']}",
"Content-Type": "application/json",
},
json={
"schema_id": "amazon_order",
"raw_mime": raw_email,
},
)
result = resp.json()
# result["data"]["order_id"] → "113-1234567-1234567"
# result["data"]["items"][0]["title"] → "Anker USB-C Charging Cable 6ft (2-Pack)"
# result["data"]["order_total_cents"] → 3498
Real-world patterns
Multi-item orders
Amazon orders often contain several line items with different quantities. The
items array captures each product independently so you can store, sum, and
query per-item data without re-parsing:
// Store each item as its own row for per-product queries
for (const item of result.data.items) {
await db.query(
`INSERT INTO order_items (order_id, title, quantity, price_cents)
VALUES ($1, $2, $3, $4)`,
[result.data.order_id, item.title, item.quantity, item.price_cents],
);
}
Deduplication by order ID
Amazon sometimes sends the same order confirmation twice — once when the order
is placed and again when it ships. The order_id field is your natural
idempotency key:
// Upsert on order_id — second parse updates instead of duplicating
await db.query(
`INSERT INTO orders (order_id, order_total_cents, currency, ship_to_name, seller, order_date, raw_data)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (order_id) DO UPDATE SET
estimated_delivery = EXCLUDED.estimated_delivery,
raw_data = EXCLUDED.raw_data,
updated_at = NOW()`,
[
result.data.order_id,
result.data.order_total_cents,
result.data.currency,
result.data.ship_to_name,
result.data.seller,
result.data.order_date,
result.data,
],
);
Tracking delivery status over time
Amazon sends separate emails as an order moves through its lifecycle: order
confirmation → shipped → delivered. By parsing each email against the same
amazon_order schema, you can track when estimated_delivery changes and
trigger downstream actions — like sending a customer notification when the
delivery date shifts:
// Compare estimated_delivery against the stored value
const prev = await db.query(
"SELECT estimated_delivery FROM orders WHERE order_id = $1",
[result.data.order_id],
);
if (prev.rows[0] && prev.rows[0].estimated_delivery !== result.data.estimated_delivery) {
await notify.send({
type: "delivery_date_changed",
order_id: result.data.order_id,
old_date: prev.rows[0].estimated_delivery,
new_date: result.data.estimated_delivery,
});
}
Expense tracking for business purchases
If your team uses Amazon Business or personal Amazon accounts for company
purchases, the amazon_order schema gives you structured data for expense
reports:
await expenses.create({
category: "office_supplies",
merchant: result.data.seller,
amount: result.data.order_total_cents / 100,
currency: result.data.currency,
description: `Amazon order ${result.data.order_id}: ${result.data.items.map(i => i.title).join(", ")}`,
date: result.data.order_date,
});
Best practices
- Use
order_idas your idempotency key. Amazon sends the same order multiple times (confirmation, shipped, delivered). Upsert on the order ID to avoid duplicate rows. - Store
itemsas separate rows. A single parse response may contain several products — flatten them into per-item records so you can query “all orders containing this product” without re-parsing. - Use
order_total_cents(integer minor units) for all money calculations. Avoid floating-point rounding; convert to dollars only at the display layer. - Keep the raw MIME intact. Don’t pre-strip the HTML before sending — the sender domain and the order structure are signal the extractor leans on to tell Amazon.com from Amazon.co.uk from Amazon.de. Send the message as you receive it.
- Watch for email type variations. Amazon sends different layouts for order confirmations, shipped notifications, delivered alerts, and refund confirmations. Test against all four variants before going to production.
- Handle international storefronts. Amazon’s international domains (amazon.co.uk, amazon.de, amazon.co.jp) use different currency symbols and date formats. The schema normalises these into ISO 4217 currency codes and ISO 8601 dates.
Next steps
The example above POSTs from wherever you already hold the message — an IMAP poller on a shared purchasing inbox, an S3 trigger on a landing bucket, or a no-code pipeline forwarding order confirmations to a single mailbox. That keeps ingestion in your own code and under your own retries, which is the model we generally recommend; the trade-offs are laid out in How to Choose an Email Parsing API.
For teams that would rather receive results asynchronously, signed webhook
delivery (HMAC-SHA256, with retries and delivery history) is available in early
access — see Designing Webhooks That Don’t Break
for how to verify and handle those payloads. Forwarding a purchasing inbox to a
unique MailFrame inbox address, instead of POSTing it yourself, is on the
roadmap; until it ships, the direct POST shown here is the supported path.
The full endpoint reference — request and response shapes, error handling, and language samples — lives in the API docs. If you want to try the Amazon order schema against your own confirmations, request early access and we’ll help you wire it up.