Skip to content

Proposal: add Home Depot provider #31

Description

@fnziman

Proposal: add Home Depot provider

Hi! Wanted to gauge interest and align on design before writing code.

I'd like to add a fourth retailer — Home Depot — as a sibling to the existing Walmart / Costco / Amazon providers, and contribute it upstream so all users get it. Filing this issue first per the standard "coordinate before coding" pattern.

Feasibility

A working JSON-API path exists and is documented end-to-end in joshellissh/homedepot-history (Python, MIT, May 2026). It uses the same internal API homedepot.com itself calls:

  • POST https://www.homedepot.com/oms/customer/order/v1/user/{userId}/orderhistory
  • POST https://www.homedepot.com/oms/customer/order/v1/user/{userId}/orderdetails

Auth is cookie-replay. The logged-in user has a THD_CUSTOMER cookie whose first segment base64-decodes to {u: userId, i: authToken, t: customerAccountId}. Send Authorization: <authToken> plus a handful of fixed headers. No CSRF or per-request signing.

Returns full line items (name, qty, unit price, tax) for both online orders (keyed by orderNumber) and instore orders (keyed by registerNumber / storeNumber / transactionId). Hard 24-month history cap on the API.

Important: don't attempt programmatic login — homedepot.com is Akamai-protected and blocks headless browsers at page load. Cookie replay is fine; logging in is not. UX would mirror the existing Walmart pattern: user logs in once in their real browser and exports the relevant cookies to ~/.homedepot-api/cookies.json.

Proposed design

Mirror existing conventions exactly:

  • Companion Go client homedepot-go, modeled on walmart-client-go and costco-go. Public surface roughly:

    type Client struct { /* ... */ }
    func NewClient(opts ...Option) (*Client, error)         // loads ~/.homedepot-api/cookies.json
    func (c *Client) ListOrders(ctx, start, end time.Time) ([]OrderSummary, error)
    func (c *Client) GetOrder(ctx, id string) (OrderDetail, error)
    func (c *Client) HealthCheck(ctx) error

    Two response shapes (online vs in-store) handled inside GetOrder. Attribution back to joshellissh/homedepot-history for the reverse-engineered protocol. MIT license to match walmart-client-go.

  • Provider package internal/adapters/providers/homedepot/ with provider.go, order.go, tests, fixtures — same shape as walmart/. Implements providers.OrderProvider (Name() = "homedepot", DisplayName() = "Home Depot"), wraps the companion client.

  • Registration sites:

    • internal/cli/providers.go: new NewHomeDepotProvider(cfg, verbose)
    • cmd/itemize/main.go: subcommand registration + printUsage row
    • internal/infrastructure/config/config.go: HomeDepotConfig struct with Enabled, RateLimit, LookbackDays, MaxOrders, Debug
    • config.yaml: new homedepot: section
    • .env.example: HOMEDEPOT_* env vars
  • Handler: most likely falls through to the existing simple handler — only needs a dedicated handler if in-store splits behave differently from online (will validate during implementation).

Open questions for you

  1. Companion repo location. Would you prefer eshaffer321/homedepot-go (matching how walmart-client-go and costco-go already live under your account) or that I build it under fnziman/homedepot-go and you import from there? Happy to transfer ownership on merge if that's cleanest.

  2. v1 scope — online + in-store, or online only? The reference impl handles both. Online-only is meaningfully simpler (one schema, no register/store ID juggling); both-at-once is a more complete first PR. Your call.

  3. Merchant matching at internal/application/sync/fetch.go:65. The current logic is strings.Contains(strings.ToLower(merchant.Name), providerName). That works for "walmart" against "WALMART.COM" etc., but won't match "homedepot" against Monarch's "THE HOME DEPOT". The docs/adding-providers.md example uses a hardcoded switch (containsProvider), but a slightly cleaner option would be adding a MerchantKeywords() []string method to the OrderProvider interface so each provider declares its own aliases. Which do you prefer? Or another approach entirely?

  4. License. The repo currently has no LICENSE file. Are you open to adding one (e.g. MIT to match the companion clients)? Without it, contributions are technically in a gray zone re: redistribution rights. Not blocking — happy to PR a LICENSE separately if you'd like.

  5. docs/adding-providers.md is stale. It still references the old module path monarchmoney-sync-backend and internal/providers/ (instead of internal/adapters/providers/). Want me to refresh it as part of this PR or in a separate housekeeping one?

Plan

If you're on board with the broad shape, I'd:

  1. Build homedepot-go (wherever you'd prefer it to live).
  2. Open the itemize PR on feat/homedepot-provider (already branched in my fork at fnziman/itemize).
  3. Cover unit tests with sanitized fixtures for both order shapes, document the cookie export flow in docs/homedepot-specifics.md, update README provider matrix.
  4. All quality gates green before review: go test ./... -race -cover, golangci-lint run, pre-commit run --all-files, manual -dry-run against my own Home Depot account.

Happy to iterate on the design if any of this doesn't fit. Thanks for building itemize — it's a great tool.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions