Skip to content
amazon ecommerce orders shipping receipts schema email-to-json tutorial

Parse Amazon Order Emails to JSON

Turn Amazon order confirmations and shipping notifications into typed JSON — order ID, line items, totals, delivery estimate, seller, and order date. Schema-first tutorial.

MailFrame Team

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:

FieldTypeExampleNotes
order_idstring113-1234567-1234567Amazon order ID in NNN-NNNNNNN-NNNNNNN format
itemsarraysee outputEach item: title, quantity, price_cents
order_total_centsinteger3498Grand total charged in minor units
currencystringusdISO 4217, lower-cased
ship_to_namestringMorgan ChenRecipient name on the shipping address
estimated_deliverystring2026-05-24Estimated or confirmed delivery date, ISO 8601
order_datestring2026-05-21Date the order was placed, ISO 8601
sellerstringAmazon.comFulfilled 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_id as your idempotency key. Amazon sends the same order multiple times (confirmation, shipped, delivered). Upsert on the order ID to avoid duplicate rows.
  • Store items as 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.

Turn your inbox into an API

MailFrame parses raw email into typed JSON against your own schema — PDF and image input are planned. Request early developer access.

Request early access