{"openapi":"3.0.3","info":{"title":"MiniPim","description":"Headless Product Information Management — public API (v1). See [docs/DEVELOPERS.md](https://github.com/Epic-Design-Labs/minipim/blob/main/docs/DEVELOPERS.md) for the narrative integration guide (auth, pagination, locale/channel resolution, webhooks).","version":"0.6.0"},"components":{"securitySchemes":{"organizationHeader":{"type":"apiKey","name":"x-organization-id","in":"header","description":"Tenant context. Phase 0 only — replaced by JWT/API key when auth lands."}},"schemas":{}},"paths":{"/healthz":{"get":{"summary":"Liveness probe","tags":["health"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"service":{"type":"string"},"version":{"type":"string"}},"required":["status","service","version"],"additionalProperties":false}}}}}}},"/readyz":{"get":{"summary":"Readiness probe (DB connectivity)","tags":["health"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"],"additionalProperties":false}}}}}}},"/v1/changelog":{"get":{"summary":"Release changelog","tags":["health"],"description":"Structured release notes, newest first. The `version` of the first entry is the deployed version (also reported by /healthz).","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"string"},"entries":{"type":"array","items":{"type":"object","properties":{"version":{"type":"string"},"date":{"type":"string"},"title":{"type":"string"},"highlights":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["feature","fix","perf","docs"]},"text":{"type":"string"}},"required":["type","text"],"additionalProperties":false}}},"required":["version","date","title","highlights"],"additionalProperties":false}}},"required":["version","entries"],"additionalProperties":false}}}}}}},"/v1/organizations":{"post":{"summary":"Create an organization (tenant)","tags":["organizations"],"description":"Creates a new tenant and adds the authenticated caller as its admin member. Requires an existing auth context (you must already be in *some* org to create another). Blocked entirely in `single` tenant mode — the deployment ships one bootstrap org and that's the entire surface.\n\nTODO: a true \"first-org signup\" flow needs an auth provider that returns a user identity without an org context. Until then, the bootstrap org seeded at install time is everyone's entry point.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"slug":{"type":"string","minLength":1,"pattern":"^[a-z0-9-]+$"},"settings":{"type":"object","properties":{"default_locale":{"type":"string"},"default_channel":{"type":"string"},"default_currency":{"type":"string"}},"additionalProperties":false}},"required":["name","slug"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"settings":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","slug","settings","createdAt","updatedAt"],"additionalProperties":false}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/organizations/me":{"get":{"summary":"Get the current tenant organization","tags":["organizations"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"settings":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","slug","settings","createdAt","updatedAt"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"summary":"Update the current tenant organization","tags":["organizations"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"slug":{"type":"string","minLength":1,"pattern":"^[a-z0-9-]+$"},"settings":{"type":"object","properties":{"default_locale":{"type":"string"},"default_channel":{"type":"string"},"default_currency":{"type":"string"}},"additionalProperties":false}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"settings":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","slug","settings","createdAt","updatedAt"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/organizations/{id}":{"delete":{"summary":"Delete an organization (tenant) — irreversible","tags":["organizations"],"description":"Exports the tenant to storage, emits `organization.deleted`, then hard-cascade-deletes it. The caller must be an `admin` of the target org (the one they are currently in), and `confirm` must equal the org slug. Never calls out to connected platforms: the cascade is DB-local and emits no per-entity events, so no destructive webhooks are sent.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"type":"string","minLength":1}},"required":["confirm"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"exportKey":{"type":"string"},"deletedCounts":{"type":"object","additionalProperties":{"type":"number"}}},"required":["exportKey","deletedCounts"],"additionalProperties":false}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/attribute-groups":{"get":{"summary":"List attribute groups","tags":["attributes"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"position":{"type":"number"}},"required":["id","organizationId","createdAt","updatedAt","code","label","position"],"additionalProperties":false}}}}}}},"post":{"summary":"Create an attribute group","tags":["attributes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"object","additionalProperties":{"type":"string"}},"position":{"type":"integer","default":0}},"required":["code","label"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"position":{"type":"number"}},"required":["id","organizationId","createdAt","updatedAt","code","label","position"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/attributes":{"get":{"summary":"List attributes","tags":["attributes"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"type":{"type":"string","enum":["text","textarea","richtext","number","decimal","boolean","date","datetime","select","multiselect","reference","media","measurement","url","email","json","money","tags"]},"groupId":{"type":"string","format":"uuid","nullable":true},"isLocalizable":{"type":"boolean"},"isScopable":{"type":"boolean"},"isRequired":{"type":"boolean"},"isVariantAxis":{"type":"boolean"},"validation":{"type":"object","additionalProperties":{},"nullable":true},"defaultValue":{"nullable":true},"position":{"type":"number"},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","label","type","groupId","isLocalizable","isScopable","isRequired","isVariantAxis","validation","position","createdBy","updatedBy"],"additionalProperties":false}}}}}}},"post":{"summary":"Create an attribute","tags":["attributes"],"description":"Defines a typed property. Adding an attribute does not require a migration — the validator layer compiles a Zod schema from the family + definitions at runtime.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"object","additionalProperties":{"type":"string"}},"type":{"type":"string","enum":["text","textarea","richtext","number","decimal","boolean","date","datetime","select","multiselect","reference","media","measurement","url","email","json","money","tags"]},"groupId":{"type":"string","format":"uuid","nullable":true},"isLocalizable":{"type":"boolean","default":false},"isScopable":{"type":"boolean","default":false},"isRequired":{"type":"boolean","default":false},"isVariantAxis":{"type":"boolean","default":false},"validation":{"type":"object","additionalProperties":{},"nullable":true},"defaultValue":{"nullable":true},"position":{"type":"integer","default":0}},"required":["code","label","type"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"type":{"type":"string","enum":["text","textarea","richtext","number","decimal","boolean","date","datetime","select","multiselect","reference","media","measurement","url","email","json","money","tags"]},"groupId":{"type":"string","format":"uuid","nullable":true},"isLocalizable":{"type":"boolean"},"isScopable":{"type":"boolean"},"isRequired":{"type":"boolean"},"isVariantAxis":{"type":"boolean"},"validation":{"type":"object","additionalProperties":{},"nullable":true},"defaultValue":{"nullable":true},"position":{"type":"number"},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","label","type","groupId","isLocalizable","isScopable","isRequired","isVariantAxis","validation","position","createdBy","updatedBy"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/attributes/{id}":{"get":{"summary":"Get an attribute","tags":["attributes"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"type":{"type":"string","enum":["text","textarea","richtext","number","decimal","boolean","date","datetime","select","multiselect","reference","media","measurement","url","email","json","money","tags"]},"groupId":{"type":"string","format":"uuid","nullable":true},"isLocalizable":{"type":"boolean"},"isScopable":{"type":"boolean"},"isRequired":{"type":"boolean"},"isVariantAxis":{"type":"boolean"},"validation":{"type":"object","additionalProperties":{},"nullable":true},"defaultValue":{"nullable":true},"position":{"type":"number"},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","label","type","groupId","isLocalizable","isScopable","isRequired","isVariantAxis","validation","position","createdBy","updatedBy"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"summary":"Update an attribute","tags":["attributes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"object","additionalProperties":{"type":"string"}},"type":{"type":"string","enum":["text","textarea","richtext","number","decimal","boolean","date","datetime","select","multiselect","reference","media","measurement","url","email","json","money","tags"]},"groupId":{"type":"string","format":"uuid","nullable":true},"isLocalizable":{"type":"boolean","default":false},"isScopable":{"type":"boolean","default":false},"isRequired":{"type":"boolean","default":false},"isVariantAxis":{"type":"boolean","default":false},"validation":{"type":"object","additionalProperties":{},"nullable":true},"defaultValue":{"nullable":true},"position":{"type":"integer","default":0}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"type":{"type":"string","enum":["text","textarea","richtext","number","decimal","boolean","date","datetime","select","multiselect","reference","media","measurement","url","email","json","money","tags"]},"groupId":{"type":"string","format":"uuid","nullable":true},"isLocalizable":{"type":"boolean"},"isScopable":{"type":"boolean"},"isRequired":{"type":"boolean"},"isVariantAxis":{"type":"boolean"},"validation":{"type":"object","additionalProperties":{},"nullable":true},"defaultValue":{"nullable":true},"position":{"type":"number"},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","label","type","groupId","isLocalizable","isScopable","isRequired","isVariantAxis","validation","position","createdBy","updatedBy"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"delete":{"summary":"Delete an attribute","tags":["attributes"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/attribute-families":{"get":{"summary":"List attribute families","tags":["attributes"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"description":{"type":"string","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","label","description"],"additionalProperties":false}}}}}}},"post":{"summary":"Create an attribute family","tags":["attributes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"object","additionalProperties":{"type":"string"}},"description":{"type":"string"}},"required":["code","label"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"description":{"type":"string","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","label","description"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/attribute-families/{id}":{"get":{"summary":"Get an attribute family with its attached attributes","tags":["attributes"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"description":{"type":"string","nullable":true},"attributes":{"type":"array","items":{"type":"object","properties":{"familyId":{"type":"string","format":"uuid"},"attributeId":{"type":"string","format":"uuid"},"isRequiredOverride":{"type":"boolean","nullable":true},"position":{"type":"number"}},"required":["familyId","attributeId","isRequiredOverride","position"],"additionalProperties":false}}},"required":["id","organizationId","createdAt","updatedAt","code","label","description","attributes"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/attribute-families/{id}/attributes":{"post":{"summary":"Attach an attribute to a family","tags":["attributes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"attributeId":{"type":"string","format":"uuid"},"isRequiredOverride":{"type":"boolean","nullable":true},"position":{"type":"integer","default":0}},"required":["attributeId"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"familyId":{"type":"string","format":"uuid"},"attributeId":{"type":"string","format":"uuid"},"isRequiredOverride":{"type":"boolean","nullable":true},"position":{"type":"number"}},"required":["familyId","attributeId","isRequiredOverride","position"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/attribute-families/{id}/attributes/{attributeId}":{"delete":{"summary":"Detach an attribute from a family","tags":["attributes"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"attributeId","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}}}}},"/v1/products":{"get":{"summary":"List products with filtering, sort, and pagination","tags":["products"],"description":"Paginated read of the catalog. Add `?locale=` and `?channel=` to\nresolve attribute values for a specific market + storefront — without them\nthe response includes the raw multi-locale/channel value arrays.\n\n**Example request**\n```bash\ncurl 'https://api.minipim.com/v1/products?limit=2&sortBy=name&locale=en_US&channel=headless-main' \\\n  -H 'Authorization: Bearer pim_xxxxxxxxxxxx' \\\n  -H 'x-organization-id: 00000000-0000-0000-0000-000000000001'\n```\n\n**Example response**\n```json\n{\n  \"data\": [\n    {\n      \"id\": \"0b8c5d3a-7f4e-4d3c-9b1a-e1f2a3b4c5d6\",\n      \"name\": \"Eco Tote Bag\",\n      \"slug\": \"eco-tote-bag\",\n      \"status\": \"active\",\n      \"familyId\": \"5a2c1d8e-...\",\n      \"thumbnailUrl\": \"https://cdn.minipim.com/<orgId>/<mediaId>/eco-tote-hero.jpg\",\n      \"categoryIds\": [\"8d2a1f3c-...\", \"9e3b2c4d-...\"],\n      \"createdAt\": \"2026-05-30T14:22:11.000Z\",\n      \"updatedAt\": \"2026-06-02T09:10:55.000Z\"\n    }\n  ],\n  \"limit\": 50,\n  \"offset\": 0,\n  \"hasMore\": true\n}\n```\n\nIterate by reading `hasMore`. Pass `?withTotal=true` to add a `total`\nfield (costs one COUNT query). Default `limit` is 50, **max 200**.\nReconcile after downtime with `?updatedSince=<iso8601>`.","parameters":[{"schema":{"type":"string","enum":["draft","active","archived"]},"in":"query","name":"status","required":false},{"schema":{"type":"string"},"in":"query","name":"q","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"familyId","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"categoryId","required":false},{"schema":{"type":"string"},"in":"query","name":"brand","required":false},{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"in":"query","name":"tag","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"connectorId","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"updatedSince","required":false},{"schema":{"type":"string","enum":["name","slug","status","updatedAt","createdAt","relevance"],"default":"name"},"in":"query","name":"sortBy","required":false},{"schema":{"type":"string","enum":["asc","desc"],"default":"asc"},"in":"query","name":"sortDir","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0,"default":0},"in":"query","name":"offset","required":false},{"schema":{"type":"boolean"},"in":"query","name":"withTotal","required":false},{"schema":{"type":"string"},"in":"query","name":"locale","required":false},{"schema":{"type":"string"},"in":"query","name":"channel","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"familyId":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["draft","active","archived"]},"attributes":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"tags":{"type":"array","items":{"type":"string"}},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true},"thumbnailUrl":{"type":"string","nullable":true},"categoryIds":{"type":"array","items":{"type":"string","format":"uuid"}}},"required":["id","organizationId","createdAt","updatedAt","familyId","name","slug","status","attributes","tags","createdBy","updatedBy","thumbnailUrl","categoryIds"],"additionalProperties":false}},"limit":{"type":"integer"},"offset":{"type":"integer"},"hasMore":{"type":"boolean"},"total":{"type":"integer"}},"required":["data","limit","offset"],"additionalProperties":false}}}}}},"post":{"summary":"Create a product","tags":["products"],"description":"Validates the `attributes` payload against the family at write time. Emits `product.created`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"familyId":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1},"slug":{"type":"string"},"status":{"type":"string","enum":["draft","active","archived"],"default":"draft"},"attributes":{"type":"object","additionalProperties":{"type":"array","items":{"type":"object","properties":{"locale":{"type":"string","nullable":true},"channel":{"type":"string","nullable":true},"value":{}},"required":["locale","channel"],"additionalProperties":false}},"default":{}},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":80},"maxItems":50,"default":[]}},"required":["familyId","name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"familyId":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["draft","active","archived"]},"attributes":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"tags":{"type":"array","items":{"type":"string"}},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","familyId","name","slug","status","attributes","tags","createdBy","updatedBy"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/products/facets":{"get":{"summary":"Faceted counts for the products list","tags":["products"],"description":"Returns top-N counts per dimension (family, status, category, brand, connector). Each dimension is computed with all OTHER filters applied so the UI can show pivot-style counts.","parameters":[{"schema":{"type":"string","enum":["draft","active","archived"]},"in":"query","name":"status","required":false},{"schema":{"type":"string"},"in":"query","name":"q","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"familyId","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"categoryId","required":false},{"schema":{"type":"string"},"in":"query","name":"brand","required":false},{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"in":"query","name":"tag","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"connectorId","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"updatedSince","required":false},{"schema":{"type":"string","enum":["name","slug","status","updatedAt","createdAt","relevance"],"default":"name"},"in":"query","name":"sortBy","required":false},{"schema":{"type":"string","enum":["asc","desc"],"default":"asc"},"in":"query","name":"sortDir","required":false},{"schema":{"type":"string"},"in":"query","name":"locale","required":false},{"schema":{"type":"string"},"in":"query","name":"channel","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"integer"},"families":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"code":{"type":"string"},"label":{"type":"string","nullable":true},"count":{"type":"integer"}},"required":["id","code","label","count"],"additionalProperties":false}},"statuses":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"],"additionalProperties":false}},"categories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string","nullable":true},"count":{"type":"integer"}},"required":["id","slug","name","count"],"additionalProperties":false}},"brands":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"],"additionalProperties":false}},"tags":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"],"additionalProperties":false}},"connectors":{"type":"array","items":{"type":"object","properties":{"connectorId":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string"},"count":{"type":"integer"}},"required":["connectorId","name","type","count"],"additionalProperties":false}}},"required":["total","families","statuses","categories","brands","tags","connectors"],"additionalProperties":false}}}}}}},"/v1/products/tags":{"get":{"summary":"List all product tags in use, with counts","tags":["products"],"description":"Every distinct tag currently on at least one product, ordered by usage. Tags are free-form, normalized to lowercase slugs on write (`Featured` → `featured`). Filter products by tag with `GET /v1/products?tag=<tag>` (repeatable; multiple tags AND together).","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"],"additionalProperties":false}}}}}}}},"/v1/products/{id}":{"get":{"summary":"Get a product","tags":["products"],"description":"Fetch a single product by id, including its attribute payload,\nthumbnail, gallery, and category links. `attributes` is always the raw\n`{ code: [{ locale, channel, value }] }` shape (the editing surface).\n\nPass `?locale=` and/or `?channel=` to additionally receive\n`resolvedAttributes` — a flat `{ code: value }` map for that\n(locale, channel) request. Fallback order: exact match →\n`(locale, null)` → `(null, channel)` → `(null, null)`. Missing\nattributes are simply absent from the resolved map (no nulls).\n\n`modifierCount` and `hasRequiredModifiers` summarize the product's\norder-line options (engraving, add-ons, print choices, …). A PDP with\n`hasRequiredModifiers: true` must not let a customer check out without\nresolving them — fetch the full list from\n`GET /v1/products/{id}/modifiers`. Modifiers do NOT create SKUs; see\nDEVELOPERS.md → \"Sellable optionality\" for how they relate to variants.\n\n**Example response (with ?locale=en_US&channel=headless-main)**\n```json\n{\n  \"id\": \"0b8c5d3a-...\",\n  \"name\": \"Eco Tote Bag\",\n  \"slug\": \"eco-tote-bag\",\n  \"status\": \"active\",\n  \"familyId\": \"5a2c1d8e-...\",\n  \"attributes\": {\n    \"description\": [\n      { \"locale\": \"en_US\", \"channel\": null,            \"value\": \"Default copy...\" },\n      { \"locale\": \"en_US\", \"channel\": \"headless-main\", \"value\": \"Override for the headless site...\" }\n    ],\n    \"price\":  [{ \"locale\": null, \"channel\": null, \"value\": { \"amount_cents\": 1899, \"currency\": \"USD\" } }],\n    \"weight\": [{ \"locale\": null, \"channel\": null, \"value\": { \"amount\": 0.4, \"unit\": \"lb\" } }]\n  },\n  \"resolvedAttributes\": {\n    \"description\": \"Override for the headless site...\",\n    \"price\":  { \"amount_cents\": 1899, \"currency\": \"USD\" },\n    \"weight\": { \"amount\": 0.4, \"unit\": \"lb\" }\n  },\n  \"thumbnailUrl\": \"https://cdn.minipim.com/<orgId>/<mediaId>/eco-tote-hero.jpg\",\n  \"categoryIds\": [\"8d2a1f3c-...\", \"9e3b2c4d-...\"],\n  \"media\": [\n    {\n      \"id\": \"11aa22bb-...\",\n      \"url\": \"https://cdn.minipim.com/<orgId>/11aa22bb-.../hero.jpg\",\n      \"role\": \"hero\", \"position\": 0, \"altText\": { \"en_US\": \"Front view\" }\n    }\n  ],\n  \"modifierCount\": 2,\n  \"hasRequiredModifiers\": true,\n  \"createdAt\": \"...\",\n  \"updatedAt\": \"...\"\n}\n```\n\nReturns 404 with `{ \"error\": { \"code\": \"not_found\", ... } }` when the\nproduct doesn't exist (or isn't visible to this tenant — RLS makes those\nindistinguishable on purpose).","parameters":[{"schema":{"type":"string"},"in":"query","name":"locale","required":false},{"schema":{"type":"string"},"in":"query","name":"channel","required":false},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"familyId":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["draft","active","archived"]},"attributes":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"tags":{"type":"array","items":{"type":"string"}},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true},"thumbnailUrl":{"type":"string","nullable":true},"categoryIds":{"type":"array","items":{"type":"string","format":"uuid"}},"media":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","nullable":true},"storageKey":{"type":"string"},"filename":{"type":"string"},"mimeType":{"type":"string"},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"role":{"type":"string"},"position":{"type":"integer"},"altText":{"type":"object","additionalProperties":{"type":"string"},"nullable":true}},"required":["id","url","storageKey","filename","mimeType","width","height","role","position"],"additionalProperties":false}},"modifierCount":{"type":"integer"},"hasRequiredModifiers":{"type":"boolean"},"resolvedAttributes":{"type":"object","additionalProperties":{}}},"required":["id","organizationId","createdAt","updatedAt","familyId","name","slug","status","attributes","tags","createdBy","updatedBy","thumbnailUrl","categoryIds","media","modifierCount","hasRequiredModifiers"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"summary":"Update a product (partial)","tags":["products"],"description":"Partial update with PATCH semantics. Scalar fields (`name`, `slug`, `status`) replace if present, else preserve.\n\n`attributes` merges at the attribute-code granularity:\n- code present with values → replaces that code's entire record list\n- code present with `[]` → clears that code\n- code absent → preserved as-is\n\nThe validator runs against the *merged* payload, so required-attribute rules see the complete picture.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"slug":{"type":"string"},"status":{"type":"string","enum":["draft","active","archived"]},"attributes":{"type":"object","additionalProperties":{"type":"array","items":{"type":"object","properties":{"locale":{"type":"string","nullable":true},"channel":{"type":"string","nullable":true},"value":{}},"required":["locale","channel"],"additionalProperties":false}}},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":80},"maxItems":50}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"familyId":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["draft","active","archived"]},"attributes":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"tags":{"type":"array","items":{"type":"string"}},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","familyId","name","slug","status","attributes","tags","createdBy","updatedBy"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"delete":{"summary":"Delete a product","tags":["products"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/products/{id}/completeness":{"get":{"summary":"Completeness scores per channel","tags":["products"],"description":"For each channel on the org, compute how ready this product is for syndication. Required attributes per channel come from `channel.settings.required_attributes` plus the baseline `attribute.isRequired` (with family override). Localizable attributes are counted per locale.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"channelCode":{"type":"string"},"total":{"type":"integer"},"filled":{"type":"integer"},"score":{"type":"integer"},"missing":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"locale":{"type":"string","nullable":true},"label":{"type":"string"},"variantsMissing":{"type":"integer"},"variantsTotal":{"type":"integer"}},"required":["code","locale"],"additionalProperties":false}}},"required":["channelCode","total","filled","score","missing"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/products/bulk":{"post":{"summary":"Bulk create products (per-row results)","tags":["products"],"description":"A single failure does not sink the rest of the batch. Each row reports ok/error independently.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"products":{"type":"array","items":{},"maxItems":500}},"required":["products"],"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"index":{"type":"integer"},"id":{"type":"string","format":"uuid"}},"required":["ok","index","id"],"additionalProperties":false},{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"index":{"type":"integer"},"error":{"type":"string"}},"required":["ok","index","error"],"additionalProperties":false}]}}},"required":["results"],"additionalProperties":false}}}}}}},"/v1/products/bulk-move-family":{"post":{"summary":"Bulk-reassign products to a different family","tags":["products"],"description":"Per-row results. In `preserve` mode, a row fails if the destination family does not include every attribute code currently on the product. In `drop_unsupported` mode, unsupported codes are removed from `attributes` and the row proceeds. Required-attribute validation runs on the merged payload (post-drop), so a move can also fail if the destination family requires attributes the product is missing.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"productIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":500},"familyId":{"type":"string","format":"uuid"},"mode":{"type":"string","enum":["preserve","drop_unsupported"],"default":"preserve"}},"required":["productIds","familyId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"ok":{"type":"boolean"},"droppedCodes":{"type":"array","items":{"type":"string"}},"error":{"type":"string"}},"required":["productId","ok"],"additionalProperties":false}}},"required":["results"],"additionalProperties":false}}}}}}},"/v1/products/bulk-set-status":{"post":{"summary":"Bulk-set status (draft / active / archived) on a set of products","tags":["products"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"productIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":500},"status":{"type":"string","enum":["draft","active","archived"]}},"required":["productIds","status"],"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"ok":{"type":"boolean"},"error":{"type":"string"}},"required":["productId","ok"],"additionalProperties":false}}},"required":["results"],"additionalProperties":false}}}}}}},"/v1/products/bulk-delete":{"post":{"summary":"Bulk-delete products","tags":["products"],"description":"Per-row results. Failures are reported individually; the rest still delete.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"productIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":500}},"required":["productIds"],"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"ok":{"type":"boolean"},"error":{"type":"string"}},"required":["productId","ok"],"additionalProperties":false}}},"required":["results"],"additionalProperties":false}}}}}}},"/v1/products/bulk-set-attribute":{"post":{"summary":"Bulk-set a single attribute value across many products","tags":["products"],"description":"Writes the same `(code, locale, channel) → value` tuple onto each product, merging into the existing payload (other codes preserved). Each product is validated against its own family — a row fails if the attribute is not in that product's family or fails type/options checks.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"productIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":500},"code":{"type":"string","minLength":1},"locale":{"type":"string","nullable":true},"channel":{"type":"string","nullable":true},"value":{},"clear":{"type":"boolean","default":false}},"required":["productIds","code","locale","channel"],"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"ok":{"type":"boolean"},"error":{"type":"string"}},"required":["productId","ok"],"additionalProperties":false}}},"required":["results"],"additionalProperties":false}}}}}}},"/v1/products/bulk-set-categories":{"post":{"summary":"Bulk-modify category assignments across products","tags":["products"],"description":"`mode=add` inserts (idempotent), `mode=remove` deletes the listed assignments, `mode=replace` wipes existing assignments and inserts the new list.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"productIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":500},"mode":{"type":"string","enum":["add","remove","replace"]},"categoryIds":{"type":"array","items":{"type":"string","format":"uuid"}}},"required":["productIds","mode","categoryIds"],"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"ok":{"type":"boolean"},"error":{"type":"string"}},"required":["productId","ok"],"additionalProperties":false}}},"required":["results"],"additionalProperties":false}}}}}}},"/v1/products/bulk-set-tags":{"post":{"summary":"Bulk-modify tags across products","tags":["products"],"description":"`mode=add` unions the listed tags into each product, `mode=remove` strips them, `mode=replace` overwrites the full list. Tags are normalized (slugified + deduped) exactly like single-product writes.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"productIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":500},"mode":{"type":"string","enum":["add","remove","replace"]},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":80},"maxItems":50}},"required":["productIds","mode","tags"],"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"ok":{"type":"boolean"},"error":{"type":"string"}},"required":["productId","ok"],"additionalProperties":false}}},"required":["results"],"additionalProperties":false}}}}}}},"/v1/products/{id}/variants":{"get":{"summary":"List variants of a product","tags":["products"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"productId":{"type":"string","format":"uuid"},"sku":{"type":"string"},"position":{"type":"number"},"attributes":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"status":{"type":"string","enum":["draft","active","archived"]},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","productId","sku","position","attributes","status","createdBy","updatedBy"],"additionalProperties":false}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"post":{"summary":"Create a variant under a product","tags":["products"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sku":{"type":"string","minLength":1},"position":{"type":"integer","default":0},"status":{"type":"string","enum":["draft","active","archived"],"default":"draft"},"attributes":{"type":"object","additionalProperties":{"type":"array","items":{"type":"object","properties":{"locale":{"type":"string","nullable":true},"channel":{"type":"string","nullable":true},"value":{}},"required":["locale","channel"],"additionalProperties":false}},"default":{}}},"required":["sku"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"productId":{"type":"string","format":"uuid"},"sku":{"type":"string"},"position":{"type":"number"},"attributes":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"status":{"type":"string","enum":["draft","active","archived"]},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","productId","sku","position","attributes","status","createdBy","updatedBy"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/variants/{id}":{"get":{"summary":"Get a variant by id","tags":["products"],"description":"Direct lookup — cart/checkout flows can resolve a\nvariant from a stored line-item id without already knowing the parent\nproduct. The returned row includes `productId`; fetch the parent with\n`GET /v1/products/{productId}` if you need product-level fields.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"productId":{"type":"string","format":"uuid"},"sku":{"type":"string"},"position":{"type":"number"},"attributes":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"status":{"type":"string","enum":["draft","active","archived"]},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","productId","sku","position","attributes","status","createdBy","updatedBy"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"summary":"Update a variant (partial)","tags":["products"],"description":"Partial update with PATCH semantics. Scalar fields (`sku`, `position`, `status`) replace if present, else preserve.\n\n`attributes` merges at the attribute-code granularity:\n- code present with values → replaces that code's entire record list\n- code present with `[]` → clears that code\n- code absent → preserved as-is\n\nValidation runs against the *merged* payload, but required-attribute enforcement is relaxed for variants (per the variant-validation rule); product-level writes still enforce required.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sku":{"type":"string","minLength":1},"position":{"type":"integer"},"status":{"type":"string","enum":["draft","active","archived"]},"attributes":{"type":"object","additionalProperties":{"type":"array","items":{"type":"object","properties":{"locale":{"type":"string","nullable":true},"channel":{"type":"string","nullable":true},"value":{}},"required":["locale","channel"],"additionalProperties":false}}}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"productId":{"type":"string","format":"uuid"},"sku":{"type":"string"},"position":{"type":"number"},"attributes":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"status":{"type":"string","enum":["draft","active","archived"]},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","productId","sku","position","attributes","status","createdBy","updatedBy"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"delete":{"summary":"Delete a variant","tags":["products"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/products/{id}/variants/matrix":{"post":{"summary":"Generate a variant matrix from variant-axis attribute values","tags":["products"],"description":"Cartesian product across the supplied axes. Each axis code must be a variant-axis attribute on the product family. SKU collisions abort the whole batch (409).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"axes":{"type":"object","additionalProperties":{"type":"array","items":{},"minItems":1}},"skuTemplate":{"type":"string"},"status":{"type":"string","enum":["draft","active","archived"],"default":"draft"},"startPosition":{"type":"integer","minimum":0,"default":0}},"required":["axes"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"productId":{"type":"string","format":"uuid"},"sku":{"type":"string"},"position":{"type":"number"},"attributes":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"status":{"type":"string","enum":["draft","active","archived"]},"createdBy":{"type":"string","format":"uuid","nullable":true},"updatedBy":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","productId","sku","position","attributes","status","createdBy","updatedBy"],"additionalProperties":false}}},"required":["created"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/variants/{variantId}/identifiers":{"get":{"summary":"List identifiers attached to a variant","tags":["products"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"variantId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"variantId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["sku","upc","gtin8","gtin12","gtin13","gtin14","ean","mpn","asin","isbn","custom"]},"customLabel":{"type":"string","nullable":true},"value":{"type":"string"}},"required":["id","organizationId","createdAt","updatedAt","variantId","type","customLabel","value"],"additionalProperties":false}}}}}}},"post":{"summary":"Attach an identifier (SKU/UPC/GTIN/etc.) to a variant","tags":["products"],"description":"GTIN-typed values are validated by check digit and length on write.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["sku","upc","gtin8","gtin12","gtin13","gtin14","ean","mpn","asin","isbn","custom"]},"customLabel":{"type":"string"},"value":{"type":"string","minLength":1}},"required":["type","value"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"variantId","required":true}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"variantId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["sku","upc","gtin8","gtin12","gtin13","gtin14","ean","mpn","asin","isbn","custom"]},"customLabel":{"type":"string","nullable":true},"value":{"type":"string"}},"required":["id","organizationId","createdAt","updatedAt","variantId","type","customLabel","value"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"put":{"summary":"Replace the full identifier set for a variant","tags":["products"],"description":"Wipes the variant's current identifiers and inserts the provided list. Pass an empty array to clear all. Each item is validated like `POST /identifiers`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"identifiers":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["sku","upc","gtin8","gtin12","gtin13","gtin14","ean","mpn","asin","isbn","custom"]},"customLabel":{"type":"string"},"value":{"type":"string","minLength":1}},"required":["type","value"],"additionalProperties":false}}},"required":["identifiers"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"variantId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"variantId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["sku","upc","gtin8","gtin12","gtin13","gtin14","ean","mpn","asin","isbn","custom"]},"customLabel":{"type":"string","nullable":true},"value":{"type":"string"}},"required":["id","organizationId","createdAt","updatedAt","variantId","type","customLabel","value"],"additionalProperties":false}}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/identifiers/{id}":{"delete":{"summary":"Detach an identifier","tags":["products"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/channels":{"get":{"summary":"List channels","tags":["taxonomy"],"description":"Channels are the surfaces your catalog publishes to —\nstorefronts, marketplaces, ad platforms. Used in attribute-value resolution\ntogether with locale: `(locale, channel)` keys each value. `settings` is\nfree-form per-channel config; `settings.required_attributes` drives the\nper-channel completeness score on products.\n\n**Example response**\n```json\n[\n  {\n    \"id\": \"3ac5ab02-93fd-49b7-91a3-681fc36eabdd\",\n    \"code\": \"headless-main\",\n    \"name\": \"Headless Storefront\",\n    \"locales\": [\"en_US\"],\n    \"settings\": null,\n    \"createdAt\": \"2026-06-02T22:28:01.926Z\",\n    \"updatedAt\": \"2026-06-02T22:28:01.926Z\"\n  },\n  {\n    \"id\": \"f1c2b3a4-...\",\n    \"code\": \"bc_pdo6b28kp8_default\",\n    \"name\": \"BigCommerce (default)\",\n    \"locales\": [\"en_US\"],\n    \"settings\": {\n      \"bc_channel_id\": 1,\n      \"bc_store_hash\": \"pdo6b28kp8\",\n      \"required_attributes\": [\"description\", \"price\", \"brand\", \"weight\"]\n    },\n    \"createdAt\": \"...\",\n    \"updatedAt\": \"...\"\n  }\n]\n```\n\nChannels seeded by connectors carry `bc_*` / `shopify_*` keys in\n`settings` so they can be identified and scoped-wiped on connector\nuninstall — leave those alone unless you know what you're doing.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"name":{"type":"string"},"locales":{"type":"array","items":{"type":"string"}},"settings":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","name","locales","settings"],"additionalProperties":false}}}}}}},"post":{"summary":"Create a channel","tags":["taxonomy"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"locales":{"type":"array","items":{"type":"string"},"default":[]},"settings":{"type":"object","additionalProperties":{}}},"required":["code","name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"name":{"type":"string"},"locales":{"type":"array","items":{"type":"string"}},"settings":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","name","locales","settings"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/channels/{id}":{"patch":{"summary":"Update a channel (settings, locales, name)","tags":["taxonomy"],"description":"Partial update. `settings` replaces the entire object — to clear, pass `null`. Use `settings.required_attributes` to drive per-channel completeness scoring.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"locales":{"type":"array","items":{"type":"string"}},"settings":{"type":"object","additionalProperties":{},"nullable":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"name":{"type":"string"},"locales":{"type":"array","items":{"type":"string"}},"settings":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","name","locales","settings"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/locales":{"get":{"summary":"List locales","tags":["taxonomy"],"description":"Locales are language/region codes (IETF\n`language_REGION`) that scope attribute values. Used with channels to\nkey attribute values: `(locale, channel)`. Exactly one locale is\nmarked `isDefault` per org — that's the fallback when a fetch doesn't\nspecify `?locale=`.\n\n**Example response**\n```json\n[\n  { \"id\": \"1f8d9e3a-...\", \"code\": \"en_US\", \"label\": \"English (US)\", \"isDefault\": true,  \"createdAt\": \"...\", \"updatedAt\": \"...\" },\n  { \"id\": \"2a9c8d4b-...\", \"code\": \"fr_FR\", \"label\": \"Français (France)\", \"isDefault\": false, \"createdAt\": \"...\", \"updatedAt\": \"...\" }\n]\n```\n\nWhen passing `?locale=fr_FR` to product reads, MiniPim falls back\nthrough `(fr_FR, channel)` → `(fr_FR, null)` → `(en_US, channel)` →\n`(en_US, null)` until it finds a value. See\n[docs/DEVELOPERS.md](https://github.com/Epic-Design-Labs/minipim/blob/main/docs/DEVELOPERS.md#locale--channel-resolution)\nfor the full resolution order.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"type":"string"},"isDefault":{"type":"boolean"}},"required":["id","organizationId","createdAt","updatedAt","code","label","isDefault"],"additionalProperties":false}}}}}}},"post":{"summary":"Create a locale","tags":["taxonomy"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":2},"label":{"type":"string","minLength":1},"isDefault":{"type":"boolean","default":false}},"required":["code","label"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"type":"string"},"isDefault":{"type":"boolean"}},"required":["id","organizationId","createdAt","updatedAt","code","label","isDefault"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/categories":{"get":{"summary":"List categories","tags":["taxonomy"],"description":"Returns the full category tree, flat, ordered by\n`position`. Build the hierarchy client-side by joining on `parentId` —\nroots have `parentId: null`. `name` is a localized label map keyed by\nlocale code.\n\n**Example response**\n```json\n[\n  {\n    \"id\": \"8d2a1f3c-...\",\n    \"parentId\": null,\n    \"name\": { \"en_US\": \"Banner Stands\" },\n    \"slug\": \"banner-stands\",\n    \"position\": 0,\n    \"createdAt\": \"...\",\n    \"updatedAt\": \"...\"\n  },\n  {\n    \"id\": \"9e3b2c4d-...\",\n    \"parentId\": \"8d2a1f3c-...\",\n    \"name\": { \"en_US\": \"24\\\" - 36\\\" Banner Stand Displays\" },\n    \"slug\": \"24-36-banner-stand-displays\",\n    \"position\": 0,\n    \"createdAt\": \"...\",\n    \"updatedAt\": \"...\"\n  }\n]\n```\n\nTo get a single category's full ancestor path, walk `parentId` upwards\nclient-side, or hit `GET /v1/categories/{id}` which returns the same row\nshape with the resolved path included in `path`.\n\nReturns the full tree by default. `?limit=` / `?offset=` are supported\nfor large taxonomies (ordered by `position`); omit them to get everything.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":1000},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0,"default":0},"in":"query","name":"offset","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"parentId":{"type":"string","format":"uuid","nullable":true},"name":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"slug":{"type":"string"},"position":{"type":"number"},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","parentId","name","slug","position","channelId"],"additionalProperties":false}}}}}}},"post":{"summary":"Create a category","tags":["taxonomy"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"parentId":{"type":"string","format":"uuid","nullable":true},"name":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string","minLength":1},"position":{"type":"integer","default":0},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["name","slug"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"parentId":{"type":"string","format":"uuid","nullable":true},"name":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"slug":{"type":"string"},"position":{"type":"number"},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","parentId","name","slug","position","channelId"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/categories/{id}":{"get":{"summary":"Get a category","tags":["taxonomy"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"parentId":{"type":"string","format":"uuid","nullable":true},"name":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"slug":{"type":"string"},"position":{"type":"number"},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","parentId","name","slug","position","channelId"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"summary":"Update a category","tags":["taxonomy"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"parentId":{"type":"string","format":"uuid","nullable":true},"name":{"type":"object","additionalProperties":{"type":"string"}},"slug":{"type":"string","minLength":1},"position":{"type":"integer"},"channelId":{"type":"string","format":"uuid","nullable":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"parentId":{"type":"string","format":"uuid","nullable":true},"name":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"slug":{"type":"string"},"position":{"type":"number"},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","parentId","name","slug","position","channelId"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"delete":{"summary":"Delete a category","tags":["taxonomy"],"description":"Children re-parent to the deleted category's parent (or root) via the schema's ON DELETE SET NULL.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/products/{id}/categories":{"get":{"summary":"List categories assigned to a product","tags":["taxonomy"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"parentId":{"type":"string","format":"uuid","nullable":true},"name":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"slug":{"type":"string"},"position":{"type":"number"},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","parentId","name","slug","position","channelId"],"additionalProperties":false}}}}}}},"put":{"summary":"Replace a product's category assignments","tags":["taxonomy"],"description":"Wipes the product's current associations and writes the provided list in order. Empty array clears all assignments.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"categoryIds":{"type":"array","items":{"type":"string","format":"uuid"}}},"required":["categoryIds"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"assigned":{"type":"integer"}},"required":["assigned"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/modifiers":{"get":{"summary":"List modifier definitions","tags":["modifiers"],"description":"Modifiers are order-line options that do NOT produce new SKUs — engraving, gift wrap, monogram text, etc. The commerce engine consumes them at purchase time.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"type":{"type":"string","enum":["text","textarea","select","multiselect","number","boolean","file"]},"validation":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"config":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","label","type","validation","config"],"additionalProperties":false}}}}}}},"post":{"summary":"Create a modifier definition","tags":["modifiers"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"object","additionalProperties":{"type":"string"}},"type":{"type":"string","enum":["text","textarea","select","multiselect","number","boolean","file"]},"validation":{"type":"object","additionalProperties":{},"nullable":true},"config":{"type":"object","additionalProperties":{},"nullable":true}},"required":["code","label","type"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"type":{"type":"string","enum":["text","textarea","select","multiselect","number","boolean","file"]},"validation":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"config":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","label","type","validation","config"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/modifiers/{id}":{"get":{"summary":"Get a modifier definition","tags":["modifiers"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"type":{"type":"string","enum":["text","textarea","select","multiselect","number","boolean","file"]},"validation":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"config":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","label","type","validation","config"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"summary":"Update a modifier definition","tags":["modifiers"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"object","additionalProperties":{"type":"string"}},"type":{"type":"string","enum":["text","textarea","select","multiselect","number","boolean","file"]},"validation":{"type":"object","additionalProperties":{},"nullable":true},"config":{"type":"object","additionalProperties":{},"nullable":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"code":{"type":"string"},"label":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"type":{"type":"string","enum":["text","textarea","select","multiselect","number","boolean","file"]},"validation":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"config":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true}},"required":["id","organizationId","createdAt","updatedAt","code","label","type","validation","config"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"delete":{"summary":"Delete a modifier definition","tags":["modifiers"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/products/{id}/modifiers":{"get":{"summary":"List modifiers attached to a product","tags":["modifiers"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"modifierId":{"type":"string","format":"uuid"},"position":{"type":"number"},"isRequired":{"type":"boolean"}},"required":["productId","modifierId","position","isRequired"],"additionalProperties":false}}}}}}},"post":{"summary":"Attach a modifier to a product","tags":["modifiers"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"modifierId":{"type":"string","format":"uuid"},"position":{"type":"integer","default":0},"isRequired":{"type":"boolean","default":false}},"required":["modifierId"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"productId":{"type":"string","format":"uuid"},"modifierId":{"type":"string","format":"uuid"},"position":{"type":"number"},"isRequired":{"type":"boolean"}},"required":["productId","modifierId","position","isRequired"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/products/{id}/modifiers/{modifierId}":{"delete":{"summary":"Detach a modifier from a product","tags":["modifiers"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"modifierId","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}}}}},"/v1/products/{id}/media":{"get":{"summary":"List media attached to a product","tags":["media"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["image","video","document","model_3d"]},"storageKey":{"type":"string"},"filename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"number"},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"metadata":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"association":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"mediaId":{"type":"string","format":"uuid"},"entityType":{"type":"string","enum":["product","variant"]},"entityId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["hero","gallery","thumbnail","technical","lifestyle","swatch"]},"position":{"type":"number"},"altText":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","mediaId","entityType","entityId","role","position","altText","channelId"],"additionalProperties":false},"url":{"type":"string","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","type","storageKey","filename","mimeType","sizeBytes","width","height","metadata","association","url"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/variants/{id}/media":{"get":{"summary":"List media attached to a variant","tags":["media"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["image","video","document","model_3d"]},"storageKey":{"type":"string"},"filename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"number"},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"metadata":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"association":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"mediaId":{"type":"string","format":"uuid"},"entityType":{"type":"string","enum":["product","variant"]},"entityId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["hero","gallery","thumbnail","technical","lifestyle","swatch"]},"position":{"type":"number"},"altText":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","mediaId","entityType","entityId","role","position","altText","channelId"],"additionalProperties":false},"url":{"type":"string","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","type","storageKey","filename","mimeType","sizeBytes","width","height","metadata","association","url"],"additionalProperties":false}}},"required":["data"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/media":{"post":{"summary":"Upload media (multipart)","tags":["media"],"description":"Uploads a single file to object storage and creates a `media` row. If `entityType`+`entityId` are present in the multipart fields, also creates an association in the same transaction.","responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["image","video","document","model_3d"]},"storageKey":{"type":"string"},"filename":{"type":"string"},"mimeType":{"type":"string"},"sizeBytes":{"type":"number"},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"metadata":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"association":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"mediaId":{"type":"string","format":"uuid"},"entityType":{"type":"string","enum":["product","variant"]},"entityId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["hero","gallery","thumbnail","technical","lifestyle","swatch"]},"position":{"type":"number"},"altText":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","mediaId","entityType","entityId","role","position","altText","channelId"],"additionalProperties":false},"url":{"type":"string"}},"required":["id","organizationId","createdAt","updatedAt","type","storageKey","filename","mimeType","sizeBytes","width","height","metadata","url"],"additionalProperties":false}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/media/associations":{"post":{"summary":"Attach a media to a product or variant","tags":["media"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mediaId":{"type":"string","format":"uuid"},"entityType":{"type":"string","enum":["product","variant"]},"entityId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["hero","gallery","thumbnail","technical","lifestyle","swatch"],"default":"gallery"},"position":{"type":"integer","minimum":0,"default":0},"altText":{"type":"object","additionalProperties":{"type":"string"}},"channelId":{"type":"string","format":"uuid"}},"required":["mediaId","entityType","entityId"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"mediaId":{"type":"string","format":"uuid"},"entityType":{"type":"string","enum":["product","variant"]},"entityId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["hero","gallery","thumbnail","technical","lifestyle","swatch"]},"position":{"type":"number"},"altText":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","mediaId","entityType","entityId","role","position","altText","channelId"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/media/associations/{id}":{"patch":{"summary":"Update a media association","tags":["media"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["hero","gallery","thumbnail","technical","lifestyle","swatch"]},"position":{"type":"integer","minimum":0},"altText":{"type":"object","additionalProperties":{"type":"string"},"nullable":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"mediaId":{"type":"string","format":"uuid"},"entityType":{"type":"string","enum":["product","variant"]},"entityId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["hero","gallery","thumbnail","technical","lifestyle","swatch"]},"position":{"type":"number"},"altText":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"channelId":{"type":"string","format":"uuid","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","mediaId","entityType","entityId","role","position","altText","channelId"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"delete":{"summary":"Detach media from an entity","tags":["media"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/media/{id}":{"delete":{"summary":"Delete media (object + row)","tags":["media"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/me":{"get":{"summary":"Identity of the authenticated principal","tags":["auth"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"externalProvider":{"type":"string"},"externalSubject":{"type":"string"},"email":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"avatarUrl":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","externalProvider","externalSubject","email","name","avatarUrl","createdAt","updatedAt"],"additionalProperties":false},"organizationId":{"type":"string","format":"uuid"},"organizationName":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}},"source":{"type":"string"},"deployment":{"type":"object","properties":{"tenantMode":{"type":"string","enum":["single","multi"]},"bootstrapOrgId":{"type":"string","format":"uuid"}},"required":["tenantMode","bootstrapOrgId"],"additionalProperties":false}},"required":["user","organizationId","organizationName","roles","source","deployment"],"additionalProperties":false}}}}}}},"/v1/me/orgs":{"get":{"summary":"List organizations the current user belongs to","tags":["auth"],"description":"Drives the admin's org switcher. Returns each org's id, name, slug, and the roles this user holds in it. In single-tenant mode this is always one row (the bootstrap org).","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}},"isCurrent":{"type":"boolean"}},"required":["organizationId","name","slug","roles","isCurrent"],"additionalProperties":false}}}}}}}},"/v1/members":{"get":{"summary":"List members of the current organization","tags":["auth"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"},"roles":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"externalProvider":{"type":"string"},"externalSubject":{"type":"string"},"email":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"avatarUrl":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","externalProvider","externalSubject","email","name","avatarUrl","createdAt","updatedAt"],"additionalProperties":false}},"required":["organizationId","userId","roles","createdAt","updatedAt","user"],"additionalProperties":false}}}}}}}},"/v1/members/{id}":{"patch":{"summary":"Update a member's roles","tags":["auth"],"description":"The `:id` here is the member's `user_id`. Replaces the entire `roles` array.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"roles":{"type":"array","items":{"type":"string"},"minItems":0}},"required":["roles"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"},"roles":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"externalProvider":{"type":"string"},"externalSubject":{"type":"string"},"email":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"avatarUrl":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","externalProvider","externalSubject","email","name","avatarUrl","createdAt","updatedAt"],"additionalProperties":false}},"required":["organizationId","userId","roles","createdAt","updatedAt","user"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/invitations":{"get":{"summary":"List invitations for the current organization","tags":["auth"],"description":"Returns pending invites by default; accepted/revoked/expired ones are included so admins can audit history.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"token":{"type":"string"},"email":{"type":"string","nullable":true},"roles":{"type":"array","items":{"type":"string"}},"invitedBy":{"type":"string","format":"uuid","nullable":true},"expiresAt":{"type":"string","format":"date-time","nullable":true},"acceptedAt":{"type":"string","format":"date-time","nullable":true},"acceptedByUserId":{"type":"string","format":"uuid","nullable":true},"revokedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}},"required":["id","organizationId","token","email","roles","invitedBy","expiresAt","acceptedAt","acceptedByUserId","revokedAt","createdAt"],"additionalProperties":false}}}}}}},"post":{"summary":"Create an invitation","tags":["auth"],"description":"Returns the invite row including the `token`. Build the accept URL on the client (e.g. `https://admin.example.com/accept-invite?token=…`) and share it however you want — email, Slack, sticky note.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"roles":{"type":"array","items":{"type":"string"},"default":[]},"expiresInDays":{"type":"integer","minimum":1,"maximum":365}},"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"token":{"type":"string"},"email":{"type":"string","nullable":true},"roles":{"type":"array","items":{"type":"string"}},"invitedBy":{"type":"string","format":"uuid","nullable":true},"expiresAt":{"type":"string","format":"date-time","nullable":true},"acceptedAt":{"type":"string","format":"date-time","nullable":true},"acceptedByUserId":{"type":"string","format":"uuid","nullable":true},"revokedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}},"required":["id","organizationId","token","email","roles","invitedBy","expiresAt","acceptedAt","acceptedByUserId","revokedAt","createdAt"],"additionalProperties":false}}}}}}},"/v1/invitations/{id}/revoke":{"post":{"summary":"Revoke an unaccepted invitation","tags":["auth"],"description":"Sets `revoked_at`. Already-accepted invites are no-ops.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"token":{"type":"string"},"email":{"type":"string","nullable":true},"roles":{"type":"array","items":{"type":"string"}},"invitedBy":{"type":"string","format":"uuid","nullable":true},"expiresAt":{"type":"string","format":"date-time","nullable":true},"acceptedAt":{"type":"string","format":"date-time","nullable":true},"acceptedByUserId":{"type":"string","format":"uuid","nullable":true},"revokedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}},"required":["id","organizationId","token","email","roles","invitedBy","expiresAt","acceptedAt","acceptedByUserId","revokedAt","createdAt"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/invitations/{token}/accept":{"post":{"summary":"Accept an invitation token","tags":["auth"],"description":"Adds the authenticated user to the invite's organization with the invite's roles. Idempotent: re-accepting from the same user is a no-op.","parameters":[{"schema":{"type":"string","minLength":8},"in":"path","name":"token","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"type":"string","format":"uuid"},"roles":{"type":"array","items":{"type":"string"}}},"required":["organizationId","roles"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/api-keys":{"get":{"summary":"List API keys for the current org","tags":["auth"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdBy":{"type":"string","format":"uuid","nullable":true},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":"string","format":"date-time","nullable":true},"expiresAt":{"type":"string","format":"date-time","nullable":true},"revokedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}},"required":["id","organizationId","createdBy","name","prefix","scopes","lastUsedAt","expiresAt","revokedAt","createdAt"],"additionalProperties":false}}}}}}},"post":{"summary":"Create an API key (plaintext shown once)","tags":["auth"],"description":"The returned `key` is the only time the plaintext is exposed. Store it securely — only the sha256 hash is persisted server-side.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"scopes":{"type":"array","items":{"type":"string"},"default":["read","write"]},"expiresAt":{"type":"string","format":"date-time"}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdBy":{"type":"string","format":"uuid","nullable":true},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":"string","format":"date-time","nullable":true},"expiresAt":{"type":"string","format":"date-time","nullable":true},"revokedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"key":{"type":"string"}},"required":["id","organizationId","createdBy","name","prefix","scopes","lastUsedAt","expiresAt","revokedAt","createdAt","key"],"additionalProperties":false}}}}}}},"/v1/api-keys/{id}":{"patch":{"summary":"Rename an API key","tags":["auth"],"description":"Updates only the display name; scopes/expiry are immutable post-create.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1}},"required":["name"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdBy":{"type":"string","format":"uuid","nullable":true},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":"string","format":"date-time","nullable":true},"expiresAt":{"type":"string","format":"date-time","nullable":true},"revokedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}},"required":["id","organizationId","createdBy","name","prefix","scopes","lastUsedAt","expiresAt","revokedAt","createdAt"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/api-keys/{id}/revoke":{"post":{"summary":"Revoke an API key","tags":["auth"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdBy":{"type":"string","format":"uuid","nullable":true},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":"string","format":"date-time","nullable":true},"expiresAt":{"type":"string","format":"date-time","nullable":true},"revokedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}},"required":["id","organizationId","createdBy","name","prefix","scopes","lastUsedAt","expiresAt","revokedAt","createdAt"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/connectors/available":{"get":{"summary":"List connector types available in this deployment","tags":["connectors"],"description":"OSS deployments list only `availability: oss` types; hosted lists both.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"availability":{"type":"string","enum":["oss","hosted"]},"capabilities":{"type":"array","items":{"type":"object","properties":{"entity":{"type":"string","enum":["product","variant","media","category"]},"direction":{"type":"string","enum":["push","pull","bidirectional"]}},"required":["entity","direction"],"additionalProperties":false}}},"required":["type","availability","capabilities"],"additionalProperties":false}}}}}}}},"/v1/connectors":{"get":{"summary":"List installed connector instances for the current org","tags":["connectors"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"type":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["active","paused","error"]},"availability":{"type":"string","enum":["oss","hosted"]},"config":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"secretsRef":{"type":"string","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","type","name","status","availability","config","secretsRef"],"additionalProperties":false}}}}}}},"post":{"summary":"Install a connector instance","tags":["connectors"],"description":"Creates a `connectors` row, calls the implementation's `install()`, then `configure()`. Fails atomically — if configure rejects (bad credentials), the row is removed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"config":{"type":"object","additionalProperties":{},"default":{}},"secretsRef":{"type":"string"}},"required":["type","name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"type":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["active","paused","error"]},"availability":{"type":"string","enum":["oss","hosted"]},"config":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"secretsRef":{"type":"string","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","type","name","status","availability","config","secretsRef"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/connectors/{id}":{"get":{"summary":"Get an installed connector","tags":["connectors"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"type":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["active","paused","error"]},"availability":{"type":"string","enum":["oss","hosted"]},"config":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"secretsRef":{"type":"string","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","type","name","status","availability","config","secretsRef"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"patch":{"summary":"Update connector config / name / secretsRef","tags":["connectors"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"config":{"type":"object","additionalProperties":{}},"secretsRef":{"type":"string","nullable":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"type":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["active","paused","error"]},"availability":{"type":"string","enum":["oss","hosted"]},"config":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"secretsRef":{"type":"string","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","type","name","status","availability","config","secretsRef"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}},"delete":{"summary":"Uninstall a connector","tags":["connectors"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/connectors/{id}/sync":{"post":{"summary":"Trigger a sync (pull / push / both)","tags":["connectors"],"description":"Asynchronous: returns the sync record immediately with status `running`; the work continues in the background. Poll `GET /v1/connectors/{id}/syncs` for the terminal status (`completed`/`partial`/`failed`) and stats. Pulls download media binaries and routinely take minutes — running in-request exceeded proxy timeouts, which severed responses mid-sync and invited retry double-pulls. Caveat: a server restart mid-sync leaves the row `running` forever; a BullMQ worker will make this durable later.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"direction":{"type":"string","enum":["pull","push","both"],"default":"pull"},"dryRun":{"type":"boolean","default":false},"since":{"type":"string","format":"date-time"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"connectorId":{"type":"string","format":"uuid"},"direction":{"type":"string","enum":["push","pull","bidirectional"]},"trigger":{"type":"string","enum":["manual","scheduled","event"]},"status":{"type":"string","enum":["queued","running","completed","failed","partial"]},"stats":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"error":{"type":"string","nullable":true},"startedAt":{"type":"string","format":"date-time","nullable":true},"completedAt":{"type":"string","format":"date-time","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","connectorId","direction","trigger","status","stats","error","startedAt","completedAt"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/catalog/wipe":{"post":{"summary":"Delete all catalog data for the current organization","tags":["connectors"],"description":"Destructive. Removes products, variants, identifiers, media, categories, modifiers, attributes, families, groups, channels and locales for this org. Connectors, sync history, users, members, API keys, and audit log are preserved. Body must include `{ confirm: \"WIPE\" }` to proceed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"type":"string","enum":["WIPE"]}},"required":["confirm"],"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"wiped":{"type":"object","properties":{"products":{"type":"integer"},"categories":{"type":"integer"},"modifiers":{"type":"integer"},"media":{"type":"integer"},"attributes":{"type":"integer"},"attributeFamilies":{"type":"integer"},"attributeGroups":{"type":"integer"},"channels":{"type":"integer"},"locales":{"type":"integer"}},"required":["products","categories","modifiers","media","attributes","attributeFamilies","attributeGroups","channels","locales"],"additionalProperties":false}},"required":["wiped"],"additionalProperties":false}}}}}}},"/v1/{entityType}/{entityId}/connector-metadata":{"get":{"summary":"Read connector metadata for an entity","tags":["connectors"],"description":"Returns a flat `{ key: value }` map of all connector_metadata rows attached to this product / variant across all connectors. Used by the admin to surface things like `bc_channels` on the product detail page.","parameters":[{"schema":{"type":"string","enum":["products","variants"]},"in":"path","name":"entityType","required":true},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"entityId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}}}}},"/v1/oauth-apps/shopify":{"get":{"summary":"Read Shopify App OAuth config (non-secret fields)","tags":["connectors"],"description":"Returns whether Shopify App OAuth is configured + the public client id and scopes. NEVER exposes the client secret. Source = env > app_settings > unset.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"configured":{"type":"boolean"},"source":{"type":"string","enum":["env","db","unset"]},"client_id":{"type":"string","nullable":true},"scopes":{"type":"string","nullable":true},"callback_url":{"type":"string","format":"uri"}},"required":["configured","source","client_id","scopes","callback_url"],"additionalProperties":false}}}}}},"put":{"summary":"Save Shopify App OAuth credentials","tags":["connectors"],"description":"Stores client_id + base64-encoded client_secret + scopes in app_settings. Takes effect immediately (no server restart). Env vars (if set) still win.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string","minLength":1},"client_secret":{"type":"string","minLength":1},"scopes":{"type":"string","default":"read_products,write_products,read_product_listings"}},"required":["client_id","client_secret"],"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"source":{"type":"string","enum":["env","db"]}},"required":["ok","source"],"additionalProperties":false}}}}}},"delete":{"summary":"Clear stored Shopify App OAuth credentials","tags":["connectors"],"description":"Removes the DB row. Env vars (if set) still take effect.","responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}}}}},"/v1/connectors/shopify/oauth/start":{"post":{"summary":"Begin Shopify App OAuth install","tags":["connectors"],"description":"Returns a URL the browser should redirect to (Shopify's authorize page). After the merchant authorizes, Shopify redirects back to the callback, which creates the connector row and stores the access token.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"shop_domain":{"allOf":[{"type":"string"},{"type":"string","pattern":"^[a-z0-9-]+\\.myshopify\\.com$"}]},"name":{"type":"string","minLength":1,"default":"Shopify"}},"required":["shop_domain"],"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"authorize_url":{"type":"string","format":"uri"}},"required":["authorize_url"],"additionalProperties":false}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/connectors/shopify/oauth/callback":{"get":{"summary":"Shopify OAuth callback","tags":["connectors"],"description":"Validates HMAC + state, exchanges code for access token, creates the connector, redirects to admin.","parameters":[{"schema":{"type":"string"},"in":"query","name":"code","required":true},{"schema":{"type":"string"},"in":"query","name":"shop","required":true},{"schema":{"type":"string"},"in":"query","name":"state","required":true},{"schema":{"type":"string"},"in":"query","name":"hmac","required":true},{"schema":{"type":"string"},"in":"query","name":"timestamp","required":false},{"schema":{"type":"string"},"in":"query","name":"host","required":false}],"responses":{"302":{"description":"Default Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/connectors/{id}/syncs":{"get":{"summary":"Sync history for a connector","tags":["connectors"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"connectorId":{"type":"string","format":"uuid"},"direction":{"type":"string","enum":["push","pull","bidirectional"]},"trigger":{"type":"string","enum":["manual","scheduled","event"]},"status":{"type":"string","enum":["queued","running","completed","failed","partial"]},"stats":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}]},"error":{"type":"string","nullable":true},"startedAt":{"type":"string","format":"date-time","nullable":true},"completedAt":{"type":"string","format":"date-time","nullable":true}},"required":["id","organizationId","createdAt","updatedAt","connectorId","direction","trigger","status","stats","error","startedAt","completedAt"],"additionalProperties":false}}}}}}}},"/v1/connectors/{id}/deliveries":{"get":{"summary":"Recent outbound webhook deliveries for a connector","tags":["connectors"],"description":"Last 50 delivery attempts (event name, HTTP status, ok, error) for push connectors that POST events to a destination (e.g. headless). Answers \"did any events actually arrive?\" without grepping receiver logs.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"eventName":{"type":"string"},"status":{"type":"integer","nullable":true},"ok":{"type":"boolean"},"error":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}},"required":["id","eventName","status","ok","error","createdAt"],"additionalProperties":false}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/connectors/{id}/webhooks/test":{"post":{"summary":"Send a signed test event to the connector destination","tags":["connectors"],"description":"Delivers a synthetic `webhook.test` event (HMAC-signed like any real delivery) so integrators can verify endpoint wiring without mutating catalog data. Returns the delivery outcome and logs it like any other.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"integer","nullable":true},"error":{"type":"string"}},"required":["ok","status"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/connectors/{id}/webhooks/enable":{"post":{"summary":"Enable inbound webhooks for this connector","tags":["connectors"],"description":"Generates a random per-connector secret. If the connector implements `subscribeWebhooks` AND a public URL is configured, also registers the subscription with the upstream platform. Otherwise returns the URL for the user to register manually.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"secret":{"type":"string"},"subscribed":{"type":"boolean"},"note":{"type":"string"},"subscriptionIds":{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"}]}}},"required":["url","secret","subscribed"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/connectors/{id}/webhooks/disable":{"post":{"summary":"Disable inbound webhooks for this connector","tags":["connectors"],"description":"Removes any platform-side subscriptions we registered and clears the local webhook secret. Best-effort on the platform side — we log and continue if a subscription fails to delete.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/connectors/{id}/webhooks/{secret}":{"post":{"summary":"Inbound webhook receiver (platform → MiniPim)","tags":["connectors"],"description":"Accepts a JSON payload from the external platform (e.g. BigCommerce store/product/updated). The connector's `handleWebhook` decides what to do; typically: refetch the affected product and run it through the upsert pipeline.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","minLength":8},"in":"path","name":"secret","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"result":{"type":"object","additionalProperties":{}}},"required":["ok"],"additionalProperties":false}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/products/{id}/relationships":{"get":{"summary":"List a product’s relationships","tags":["products"],"description":"Outbound by default (`source = :id`). Use direction=inbound or both to include rows where this product is the target.","parameters":[{"schema":{"type":"string","enum":["cross_sell","up_sell","related","accessory","replacement","bundle_component"]},"in":"query","name":"type","required":false},{"schema":{"type":"string","enum":["outbound","inbound","both"],"default":"outbound"},"in":"query","name":"direction","required":false},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"sourceProductId":{"type":"string","format":"uuid"},"targetProductId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["cross_sell","up_sell","related","accessory","replacement","bundle_component"]},"position":{"type":"number"},"metadata":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true}},"required":["id","organizationId","createdAt","updatedAt","sourceProductId","targetProductId","type","position","metadata"],"additionalProperties":false}}}}}}},"post":{"summary":"Create a relationship from this product to another","tags":["products"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"targetProductId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["cross_sell","up_sell","related","accessory","replacement","bundle_component"]},"position":{"type":"integer","default":0},"metadata":{"type":"object","additionalProperties":{},"nullable":true}},"required":["targetProductId","type"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"sourceProductId":{"type":"string","format":"uuid"},"targetProductId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["cross_sell","up_sell","related","accessory","replacement","bundle_component"]},"position":{"type":"number"},"metadata":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true}},"required":["id","organizationId","createdAt","updatedAt","sourceProductId","targetProductId","type","position","metadata"],"additionalProperties":false}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/relationships/{id}":{"delete":{"summary":"Delete a relationship","tags":["products"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Default Response","content":{"application/json":{"schema":{"enum":["null"],"nullable":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}}}}}}},"/v1/audit-log":{"get":{"summary":"Query the audit log","tags":["audit"],"description":"Returns audit entries newest-first. Filter by entity_type, entity_id, action, and `since`.","parameters":[{"schema":{"type":"string"},"in":"query","name":"entity_type","required":false},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"entity_id","required":false},{"schema":{"type":"string","enum":["create","update","delete"]},"in":"query","name":"action","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"since","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0,"default":0},"in":"query","name":"offset","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organizationId":{"type":"string","format":"uuid"},"entityType":{"type":"string"},"entityId":{"type":"string","format":"uuid"},"action":{"type":"string","enum":["create","update","delete"]},"actorId":{"type":"string","format":"uuid","nullable":true},"actorType":{"type":"string","enum":["user","api_key","connector","system"]},"changes":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"enum":["null"],"nullable":true}]},{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"nullable":true},"createdAt":{"type":"string","format":"date-time"}},"required":["id","organizationId","entityType","entityId","action","actorId","actorType","changes","createdAt"],"additionalProperties":false}},"limit":{"type":"integer"},"offset":{"type":"integer"},"hasMore":{"type":"boolean"},"total":{"type":"integer"}},"required":["data","limit","offset"],"additionalProperties":false}}}}}}}},"servers":[{"url":"https://api.minipim.com","description":"this deployment"}],"security":[{"organizationHeader":[]}],"tags":[{"name":"health","description":"Liveness + readiness probes"},{"name":"organizations","description":"Tenant management"},{"name":"attributes","description":"Attribute schema — groups, definitions, families"},{"name":"products","description":"Products, variants, identifiers"},{"name":"taxonomy","description":"Channels, locales, categories"},{"name":"modifiers","description":"Order-line options (engraving, gift wrap, etc.) — no new SKUs"},{"name":"media","description":"Media (images, video) attached to products / variants"},{"name":"auth","description":"API keys + authenticated principal"},{"name":"connectors","description":"Inbound + outbound platform connectors"},{"name":"audit","description":"Mutation history per entity"}]}