Skip to content

Monthly Amazon Order Import

How the Amazon order export gets into heezy-finance, so a card charge stops reading as AMAZON MKTPL*RM1MB6TF3 and starts showing what was in the box.

The short version

Request Your Orders from Amazon's privacy portal, wait for the email, drop Order History.csv into heezy/ingest/raw/amazon/ on the NFS share, then run the Amazon CSV Import workflow in heezy-containers with dry_run unchecked. Everything below is why, and what to check afterwards.


What this actually does

A bank statement gives you a merchant, a date, and an amount. Amazon never tells the bank what you bought. The import closes that gap in three steps:

  1. Load the export. Each CSV row is one item on one shipment. Rows group into orders (orders), items (order_items), and shipments (order_shipments).
  2. Classify each item. item_classifier.py runs three tiers in order: keyword rules, then the item_category_cache table, then a local Ollama call. A title seen before never reaches Ollama.
  3. Match shipments to charges. Each Amazon debit is paired with a shipment of the same amount that shipped near the statement date. The pairing is written to order_shipments.bank_transaction_id.

From there the reconcile tab draws the charge as identified, and a basket holding pet food and groceries splits across both categories instead of landing entirely in one.

Why shipments and not orders

Amazon charges the card once per shipment, on the ship date. An order that arrives in two boxes is two charges, and neither equals the order total. Ship lag runs from same-day to over a week. Matching on order totals and order dates catches roughly a quarter of what shipment matching does.


Step 1 — Request the export

Amazon does not offer a one-click download. The request takes hours to a day.

  1. Go to amazon.com/hz/privacy-central/data-requests/preview.html.
  2. Choose Your Orders from the category list. Not "Request All Your Data", which is enormous and arrives as dozens of files.
  3. Submit, then confirm from the verification email Amazon sends immediately.
  4. Wait for the second email, "Your Amazon.com data is ready", and download the archive.

The archive contains several CSVs. The one that matters is the retail order history: one row per item, with Order ID, Order Date, Ship Date, Product Name, Unit Price and Total Amount columns. Those column names are what the importer reads, so if a future export renames them the parse comes back empty rather than wrong.

Step 2 — Stage the file

Rename it to Order History.csv and put it in the ingest folder on the NFS share:

heezy/ingest/raw/amazon/Order History.csv

That is the heezy/ingest/raw/amazon/ folder on the SMB share from a desktop, and /nfs/heezy/ingest/raw/amazon/ on big-boi. Overwrite the previous file. Only one export lives here at a time; each new export is a full history, not a delta.

The filename matters. The workflow passes that exact path, spaces and all.

Step 3 — Run the workflow

In heezy-containers, run Amazon CSV Import from the Actions tab. It is dispatch-only and never fires on a push.

Input Default What it controls
since 2025-05-01 Only recategorize bank transactions on or after this date
import_since 2025-04-01 Only import orders placed on or after this date
dry_run false Parse and classify, write nothing

Keep import_since earlier than since. A charge posting on the 2nd of a month can belong to an order placed the previous month, and it will never find its order if the import window starts at the same date.

For a routine monthly run the defaults are fine: the importer skips orders it already holds, so re-importing the same range costs nothing but a few seconds of lookups.

The job builds the finance image, then SSHes to big-boi and runs both scripts there. It runs on big-boi rather than in the job container because the job container cannot mount the NFS share.

How long it takes

Minutes if most item titles are already in item_category_cache. A first-time import over a fresh range is much slower, because every unseen title costs one Ollama call and big-boi classifies on CPU. Roughly 8 orders per minute is normal for uncached items.

Step 4 — Check the result

The workflow log ends with four numbers:

Done. Inserted 58 orders, 81 items. Skipped 898 already-existing (830 existing item rows backfilled).
Found 219 Amazon debit transactions to evaluate
Updated 39 transactions. No match: 36. Mixed category: 21. Already correct: 123.
Linked 160 shipments to a transaction (24 left unlinked as ambiguous).
  • Updated — the charge's category changed to what its items say.
  • Already correct — it already matched. Normal and expected to dominate on a repeat run.
  • Mixed category — the shipment holds more than one category, so no single category is written. These still get linked and still split correctly in the reports.
  • No match — no shipment of that amount shipped near that date.
  • Ambiguous — two shipments of the same amount in the same window. Deliberately left unlinked rather than guessed.

Then open the Reconcile tab on finance.heezy.info and spot-check the newest month. Amazon rows should show a green check and a clickable item count. Clicking it opens the order, its items, and a link out to the order on Amazon.


When something looks wrong

The item count badge is missing

The charge has no linked shipment. Either the amount never matched, or two candidates tied. Check the workflow's No match and ambiguous counts.

A charge sits under the wrong category

Open the item modal and change the item's category from the dropdown. It saves immediately, and the category splits recompute on their own because they are read from order_items live. There is no re-import needed and no cache to bust.

Fix it at the source too, if the title is one that recurs: add a keyword rule to item_classifier.py in heezy-containers/dockerfiles/heezy-finance/. Rules beat both the cache and Ollama, so the next import gets it right without a manual correction.

Nothing matches for an older stretch

Some periods cannot be reconciled by amount at all. Heavily discounted Subscribe & Save orders ship many small boxes on the same day, and a single card charge bundles an arbitrary subset of them. There is no amount to match against. This is a limitation of the export, not a bug to chase.

The workflow fails on the SSH step

Check that OpenBao still holds production/heezy/ssh/mcp-admin-private-key and production/heezy/postgres/heezy-credentials. Both are read fresh on every run.


What the import never touches

  • Receipts. Receipt line items come from the mailbot and OCR, on their own schedule. The two sources are read together but written separately.
  • Non-Amazon merchants. A Costco or Meijer charge has no item data anywhere in the system unless a receipt was captured for it. Its category stays merchant-derived.
  • Titles and prices. Editing them in the UI is not offered, because the next import would overwrite the edit. Category is the one field the UI can change.