← back to Pattern Vault
etsy: auto-resolve digital-pattern taxonomy_id at wire-up (getSellerTaxonomyNodes) — removes the manual taxonomy step
1909be7c89865b8d74a889b6a8756cdb358c70d3 · 2026-07-04 09:55:37 -0700 · Steve Abrams
Files touched
Diff
commit 1909be7c89865b8d74a889b6a8756cdb358c70d3
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Jul 4 09:55:37 2026 -0700
etsy: auto-resolve digital-pattern taxonomy_id at wire-up (getSellerTaxonomyNodes) — removes the manual taxonomy step
---
scripts/etsy-list.mjs | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/scripts/etsy-list.mjs b/scripts/etsy-list.mjs
index 113546c..3b31039 100644
--- a/scripts/etsy-list.mjs
+++ b/scripts/etsy-list.mjs
@@ -127,12 +127,36 @@ async function form(body) {
return p;
}
-if (LIVE && missing.length === 0) {
+// Resolve the digital-pattern taxonomy_id from the live seller taxonomy (x-api-key only, no OAuth).
+// Prefers a "Digital Prints"/"Digital Patterns" node, else the closest "Patterns" under Craft Supplies.
+async function resolveTaxonomy(apiKey) {
+ const res = await fetch('https://openapi.etsy.com/v3/application/seller-taxonomy/nodes', { headers: { 'x-api-key': apiKey } });
+ if (!res.ok) throw new Error('seller-taxonomy: ' + res.status);
+ const { results } = await res.json();
+ const flat = [];
+ const walk = (n, trail) => { const p = [...trail, n.name]; flat.push({ id: n.id, path: p.join(' > ') }); (n.children || []).forEach((c) => walk(c, p)); };
+ (results || []).forEach((n) => walk(n, []));
+ const score = (p) => (/digital prints/i.test(p) ? 3 : /digital.*pattern|pattern.*digital/i.test(p) ? 2 : /pattern/i.test(p) ? 1 : 0);
+ const best = flat.filter((x) => score(x.path) > 0).sort((a, b) => score(b.path) - score(a.path))[0];
+ return best || null;
+}
+
+if (LIVE && missing.filter((m) => m !== 'taxonomyId').length === 0 && !CFG.taxonomyId) {
+ try {
+ const t = await resolveTaxonomy(CFG.apiKey);
+ if (t) { CFG.taxonomyId = String(t.id); console.log(`[etsy] resolved taxonomy_id ${t.id} — "${t.path}"`); }
+ else console.log('[etsy] could not auto-resolve taxonomy — set ETSY_TAXONOMY_ID manually.');
+ } catch (e) { console.log('[etsy] taxonomy resolve failed:', e.message); }
+}
+
+if (LIVE && CFG.apiKey && CFG.token && CFG.shopId && CFG.taxonomyId) {
+ const TAX = Number(CFG.taxonomyId);
const base = `https://openapi.etsy.com/v3/application/shops/${CFG.shopId}/listings`;
const headers = { 'x-api-key': CFG.apiKey, Authorization: `Bearer ${CFG.token}`, 'Content-Type': 'application/x-www-form-urlencoded' };
let ok = 0, fail = 0;
for (const item of plan) {
try {
+ item.body.taxonomy_id = TAX; // inject resolved id (build-time value may be a placeholder)
const res = await fetch(base, { method: 'POST', headers, body: await form(item.body) });
const j = await res.json();
if (!res.ok) { console.error(`[etsy] FAIL "${item.body.title}": ${JSON.stringify(j)}`); fail++; continue; }
← d0febc9 etsy: dry-run listing pipeline from kit CSV — 38 type=downlo
·
back to Pattern Vault
·
etsy: split compound tags into <=20-char atoms instead of dr 71cadbe →