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
-
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.
-
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.
-
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?
-
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.
-
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:
- Build
homedepot-go (wherever you'd prefer it to live).
- Open the itemize PR on
feat/homedepot-provider (already branched in my fork at fnziman/itemize).
- Cover unit tests with sanitized fixtures for both order shapes, document the cookie export flow in
docs/homedepot-specifics.md, update README provider matrix.
- 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.
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}/orderhistoryPOST https://www.homedepot.com/oms/customer/order/v1/user/{userId}/orderdetailsAuth is cookie-replay. The logged-in user has a
THD_CUSTOMERcookie whose first segment base64-decodes to{u: userId, i: authToken, t: customerAccountId}. SendAuthorization: <authToken>plus a handful of fixed headers. No CSRF or per-request signing.Returns full line items (name, qty, unit price, tax) for both
onlineorders (keyed byorderNumber) andinstoreorders (keyed byregisterNumber/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 onwalmart-client-goandcostco-go. Public surface roughly:Two response shapes (online vs in-store) handled inside
GetOrder. Attribution back tojoshellissh/homedepot-historyfor the reverse-engineered protocol. MIT license to matchwalmart-client-go.Provider package
internal/adapters/providers/homedepot/withprovider.go,order.go, tests, fixtures — same shape aswalmart/. Implementsproviders.OrderProvider(Name() = "homedepot",DisplayName() = "Home Depot"), wraps the companion client.Registration sites:
internal/cli/providers.go: newNewHomeDepotProvider(cfg, verbose)cmd/itemize/main.go: subcommand registration +printUsagerowinternal/infrastructure/config/config.go:HomeDepotConfigstruct withEnabled,RateLimit,LookbackDays,MaxOrders,Debugconfig.yaml: newhomedepot:section.env.example:HOMEDEPOT_*env varsHandler: most likely falls through to the existing
simplehandler — only needs a dedicated handler if in-store splits behave differently from online (will validate during implementation).Open questions for you
Companion repo location. Would you prefer
eshaffer321/homedepot-go(matching howwalmart-client-goandcostco-goalready live under your account) or that I build it underfnziman/homedepot-goand you import from there? Happy to transfer ownership on merge if that's cleanest.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.
Merchant matching at
internal/application/sync/fetch.go:65. The current logic isstrings.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". Thedocs/adding-providers.mdexample uses a hardcoded switch (containsProvider), but a slightly cleaner option would be adding aMerchantKeywords() []stringmethod to theOrderProviderinterface so each provider declares its own aliases. Which do you prefer? Or another approach entirely?License. The repo currently has no
LICENSEfile. 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 aLICENSEseparately if you'd like.docs/adding-providers.mdis stale. It still references the old module pathmonarchmoney-sync-backendandinternal/providers/(instead ofinternal/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:
homedepot-go(wherever you'd prefer it to live).feat/homedepot-provider(already branched in my fork atfnziman/itemize).docs/homedepot-specifics.md, update README provider matrix.go test ./... -race -cover,golangci-lint run,pre-commit run --all-files, manual-dry-runagainst 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.