# ApparelHub (npm · @apparelhub/mcp-server)

Run a custom-merch store from an agent: design, build products, list on every channel, fulfill.

- Trust score: 63/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-29

## Components

- remote · `mcp.apparelhub.ai`: 36/100, [markdown](https://verifymcp.io/servers/apparelhub-ai-apparelhub-mcp/mcp.md), [page](https://verifymcp.io/servers/apparelhub-ai-apparelhub-mcp/mcp)
- npm · `@apparelhub/mcp-server`: 63/100 (this document), [markdown](https://verifymcp.io/servers/apparelhub-ai-apparelhub-mcp/apparelhub-mcp-server.md), [page](https://verifymcp.io/servers/apparelhub-ai-apparelhub-mcp/apparelhub-mcp-server)

## Channel facts

- Registry: `npm`
- Package: `@apparelhub/mcp-server`
- Version: `0.15.2`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-29.

- **Supply Chain Security**: 48/100
  - Malware scan not yet available for this package.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 31 of 92 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 97/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to ApparelHub-AI/apparelhub-mcp).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 0 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 68/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 23020 tokens (~187/item across 123 items; 123 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 90/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 71% of tool parameters carry a description.
- **Tool Safety**: 98/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 9 of 10 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "remove_order_item" implies "remove" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - An AI judge read all 124 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the ApparelHub MCP server?

ApparelHub runs locally as an npm package, launched with npx -y @apparelhub/mcp-server. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add apparelhub-ai-apparelhub-mcp -- npx -y @apparelhub/mcp-server
```

### Cursor

```json
{
  "mcpServers": {
    "apparelhub-ai-apparelhub-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@apparelhub/mcp-server"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "apparelhub-ai-apparelhub-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@apparelhub/mcp-server"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add apparelhub-ai-apparelhub-mcp -- npx -y @apparelhub/mcp-server
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "apparelhub-ai-apparelhub-mcp": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "@apparelhub/mcp-server"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add apparelhub-ai-apparelhub-mcp --command npx --arg -y --arg @apparelhub/mcp-server
```

### Hermes

```yaml
mcp_servers:
  apparelhub-ai-apparelhub-mcp:
    command: "npx"
    args: ["-y", "@apparelhub/mcp-server"]
```

### Netclaw

```json
{
  "McpServers": {
    "apparelhub-ai-apparelhub-mcp": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "@apparelhub/mcp-server"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add apparelhub-ai-apparelhub-mcp -t stdio -c npx -a -y @apparelhub/mcp-server
```

### Other

```json
{
  "mcpServers": {
    "apparelhub-ai-apparelhub-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@apparelhub/mcp-server"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-29 (score 63)

First indexed and scored.

## MCP tools (123)

### `check_setup_readiness` (~162 tokens)

What this account already has, what it still needs, and the single next action to take. Returns ready_to_design / ready_to_fulfill / ready_to_sell, a per-store breakdown, and an ordered next_steps list. Start here for any first-time setup, and call it again after each connection to confirm the state actually changed. Read-only, makes no provider calls, and is safe to poll.

[#01f20c]

Input parameters:

- `workspace` (string): Workspace uuid the store lives in (agency accounts) — use the store's workspace.uuid from list_my_stores. Omit only for single-workspace accounts; omitting it on a multi-workspace account targets the…

### `list_connectable_providers` (~165 tokens)

Fulfillment providers and sales channels this account may connect, each marked with how it connects: connect_mode "in_chat" means you can complete it here by asking for a credential, "browser" means you must dispatch an authorization link with start_channel_connect and poll. Also returns where the merchant generates the credential, when there is one. Use this before asking a user for anything, so you ask for the right thing.

[#21af2f]

Input parameters:

- `workspace` (string): Workspace uuid the store lives in (agency accounts) — use the store's workspace.uuid from list_my_stores. Omit only for single-workspace accounts; omitting it on a multi-workspace account targets the…

### `connect_fulfillment_provider` (~267 tokens)

Connect an API-token fulfillment provider (Printify, Gelato) to a store, entirely in chat. Validates the token first, so a bad token fails before anything is stored. If the token maps to more than one shop the result asks you to pick one and lists them — call again with shop_id set. For Printful use start_channel_connect instead: it needs a browser. Never repeat the token back to the user.

[#98598c]

Input parameters:

- `api_token` (string, required): The merchant's provider API token. Get the generation URL from list_connectable_providers (credential_url). Treat as a secret: do not echo it.
- `provider_uuid` (string, required): Provider uuid (from list_connectable_providers).
- `shop_id` (string): Which shop to connect, when the token maps to several. Omit on the first call.
- `store_uuid` (string, required): Store uuid (from list_my_stores or create_store).
- `workspace` (string): Workspace uuid the store lives in (agency accounts) — use the store's workspace.uuid from list_my_stores. Omit only for single-workspace accounts; omitting it on a multi-workspace account targets the…

### `connect_sales_channel` (~202 tokens)

Connect an API-key sales channel (WooCommerce, Wix) to a store, entirely in chat. For Shopify and TikTok Shop use start_channel_connect instead: they need a browser. Credentials are write-only and are never returned.

[#c9093d]

Input parameters:

- `credentials` (object, required): Channel credentials, e.g. WooCommerce { store_url, consumer_key, consumer_secret }; Wix { api_key, site_id }. Treat as secrets: do not echo them.
- `provider_uuid` (string, required): Provider uuid (from list_connectable_providers).
- `store_uuid` (string, required): Store uuid (from list_my_stores or create_store).
- `workspace` (string): Workspace uuid the store lives in (agency accounts) — use the store's workspace.uuid from list_my_stores. Omit only for single-workspace accounts; omitting it on a multi-workspace account targets the…

### `start_channel_connect` (~466 tokens)

Begin a browser-based connection (Printful, Shopify, TikTok Shop, Fourthwall). Shopify additionally requires shop_url, the merchant myshopify domain — ask for it before calling. Returns an authorization URL to give the user. THE CONNECTION IS NOT FINISHED WHEN THIS RETURNS. You must keep polling check_connection_status (passing the same provider_uuid) until it reports connected, then tell the user. The browser tab where they authorize is NOT this conversation and cannot report back to you, so polling is the only way you or they will learn it worked. Poll every few seconds, up to about two minutes, and if it has not landed by then ask whether they finished authorizing rather than giving up silently. If they need to create an upstream account first, let them, then call this again for a fresh link.

[#6d4006]

Input parameters:

- `callback_url` (string): OMIT THIS. The platform fills in the callback registered with the provider, and for Shopify that registered URL is the only one that works — anything else is refused, either by us or by Shopify with…
- `kind` (string, required): Which family this provider belongs to (from list_connectable_providers.family).
- `provider_uuid` (string, required): Provider uuid (from list_connectable_providers).
- `shop_url` (string): Required for Shopify only: the merchant's myshopify domain, e.g. your-store.myshopify.com. Ask the user for it; it is the domain in their Shopify admin URL, not their custom storefront domain. Omit f…
- `store_name` (string): Name for a NEW store, when connecting fulfillment without an existing store_uuid.
- `store_uuid` (string): Store to attach to. Required for sales_channel. For fulfillment, omit it together with store_name to create a new store.
- `workspace` (string): Workspace uuid the store lives in (agency accounts) — use the store's workspace.uuid from list_my_stores. Omit only for single-workspace accounts; omitting it on a multi-workspace account targets the…

### `check_connection_status` (~282 tokens)

Poll whether a dispatched connection has completed. Call this repeatedly after start_channel_connect while the user authorizes in their browser, and announce the result when it lands: they cannot see this conversation from the tab they authorized in, so if you do not tell them, nobody does. Read-only, makes no provider call, and is safe to poll every few seconds. connected true means say so and continue setup. connected false means keep waiting. needs_reconnect means retrying will never work and you must dispatch a fresh link with start_channel_connect.

[#807361]

Input parameters:

- `provider_uuid` (string): The provider you are waiting for — pass the same provider_uuid you gave start_channel_connect. REQUIRED in practice when waiting on a sales channel (Shopify, TikTok Shop): without it this answers onl…
- `store_uuid` (string): Narrow the answer to one store. Omit to get the whole account.
- `workspace` (string): Workspace uuid the store lives in (agency accounts) — use the store's workspace.uuid from list_my_stores. Omit only for single-workspace accounts; omitting it on a multi-workspace account targets the…

### `list_my_workspaces` (~103 tokens)

List the workspaces this account can act in, each with its uuid. Agency / multi-brand accounts have more than one (e.g. a workspace per client); a single account just has Default. The store / product / order / design tools operate on the Default workspace unless you pass workspace=<uuid>. Use this FIRST to resolve a workspace by name (e.g. a client's name) to the uuid those tools need. Read-only.

[#358d40]

### `list_my_stores` (~72 tokens)

List the merchant's ApparelHub stores, each with its fulfillment providers (Printful/Printify) and connected sales channels (Shopify/WooCommerce/Wix). Read-only.

[#739377]

Input parameters:

- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `list_my_designs` (~321 tokens)

List the merchant's generated design images (newest first). Read-only. Use these design_uuids with the design/product tools. Pass on_products=false to find orphan designs (designs not used by any live product), the supported way to audit a workspace for unused designs before archiving them. Pass archived=true to list already-archived designs. A design with no full_url carries processing_status: "pending"/"processing" means it is still being made and is worth polling, while "failed" means it gave up and processing_error says why. Branch on processing_error_code rather than matching the message text, and do not retry a design whose failure is a content block — it will fail the same way every time.

[#58124e]

Input parameters:

- `archived` (boolean): true returns archived designs instead of active ones (default false).
- `limit` (integer): Max results (default 20).
- `on_products` (boolean): false returns only designs NOT used by any live product (orphans, safe to archive). true returns only designs in use. Omit for no filter.
- `search` (string): Match title/prompt where supported.
- `sort` (string): Sort order (default newest).
- `source` (string): Filter by AI source name, e.g. "Nano Banana". Comma-separated for several; case-insensitive. An unrecognised name is rejected with the list of valid sources, so a result set that comes back is genuin…
- `workspace` (string): Workspace uuid (agency accounts).

### `list_my_products` (~153 tokens)

List the merchant's products with their fulfillment and sales-channel sync status. Each channel entry also carries `health` — what the channel last said about the listing. A channel can remove or deactivate a listing at any time, so check `health`, not just `sync_status`: a product can read 'Synced' historically and still be gone. Pass store_uuid to scope to one store; omit for all products. Read-only.

[#06afd6]

Input parameters:

- `limit` (integer)
- `search` (string)
- `status` (string)
- `store_uuid` (string): Scope to one store (omit for all products).
- `sync_state` (string)
- `workspace` (string)

### `list_my_orders` (~70 tokens)

List the merchant's recent orders across channels. Read-only.

[#c5f34c]

Input parameters:

- `limit` (integer)
- `since` (string): ISO date lower bound where supported.
- `status` (string)
- `store_uuid` (string)
- `workspace` (string)

### `get_order_details` (~59 tokens)

Full detail for one order: line items, payment + fulfillment status, and shipments/tracking. Read-only.

[#c40849]

Input parameters:

- `order_uuid` (string, required): The order uuid (from list_my_orders).
- `workspace` (string)

### `browse_catalog` (~355 tokens)

Browse ONE fulfillment provider's catalog for garments to print on. `category` is resolved against THAT provider's own taxonomy (providers use different vocabularies for the same idea) and an unknown category is rejected with the valid list rather than quietly returning everything. `keyword` matches product names across the whole catalog. ALWAYS read `warnings` in the response: they tell you when your results are narrower than you asked for -- e.g. a category that only exists inside one department. Each garment carries `decoration_method` / `accepts_photoreal` (`accepts_photoreal` absent means the provider publishes no signal -- unchecked, NOT unsuitable). This searches a SINGLE provider: to ask what the whole account can do, or before concluding a garment cannot take a design, use find_garments. Read-only.

[#5e449d]

Input parameters:

- `category` (string): e.g. "t-shirts", "hoodies", "mugs".
- `has_aop` (boolean): Filter to all-over-print garments. All-over print is the weakest part of the decoration signal (not every provider declares the technique, so some are recognised by name) and this searches ONE provid…
- `keyword` (string)
- `page` (integer)
- `per_page` (integer)
- `provider` (string, required): The fulfillment provider to browse, by name (case-insensitive). Must be a provider this account has access to — call list_catalog_providers to see valid values (the set is account-specific). An unrec…
- `workspace` (string)

### `get_garment_details` (~133 tokens)

Full detail for one garment: the variant matrix (colors/sizes/costs), print templates, ApparelHub pricing floor, and quality tier. Read-only.

[#16a079]

Input parameters:

- `product_ref_id` (string, required): The garment ref id from browse_catalog (a string).
- `provider` (string, required): The fulfillment provider to browse, by name (case-insensitive). Must be a provider this account has access to — call list_catalog_providers to see valid values (the set is account-specific). An unrec…
- `workspace` (string)

### `find_garments` (~447 tokens)

Search EVERY fulfillment provider on the account at once for garments matching a capability. USE THIS BEFORE TELLING A USER AN ITEM CANNOT BE BUILT. A capability limit is almost always scoped to one provider, not to the category of garment: one provider carrying only embroidered headwear says nothing about another's printed caps. browse_catalog answers 'what does THIS provider carry'; this answers 'what on this ACCOUNT can take this design'. Provider scope defaults to every provider available — pass `providers` only to deliberately narrow it. Returns a compact ranked shortlist (confirmed capability first), plus `providers_searched` so you can state your coverage honestly rather than implying you checked everything. An empty result means nothing matched THESE filters on THESE providers; it is not proof the garment does not exist, and the warnings say so. Read-only.

[#e5ad81]

Input parameters:

- `accepts_photoreal` (boolean): true for photographic / gradient-heavy / fine-detail artwork; false to find garments you can embroider. This is the filter that answers "can this design go on this thing".
- `category` (string): Garment kind, e.g. "hat", "t-shirt", "mug". Matched against product names across each provider's whole catalog, including the words providers actually use ("hat" also finds cap / beanie / snapback /…
- `decoration_method` (array): Any match qualifies. "print" means a print process the provider does not name more precisely.
- `include_unknown` (boolean): Keep garments whose decoration method the provider never published. Default true: unclassified is not the same as unsuitable, and excluding them hides real options.
- `keyword` (string): Extra substring match on name/brand.
- `limit` (integer): Default 20.
- `providers` (array): Provider NAMES to restrict to. OMIT to search every provider on the account — that is the default and the recommended usage.
- `verify` (boolean): Confirm low-confidence matches with a per-garment lookup. Defaults on when filtering by capability. Bounded, so a very broad search may leave some unverified.
- `workspace` (string)

### `recommend_garment` (~105 tokens)

Recommend a garment type for a design/use-case, encoding ApparelHub's garment trade-offs (BC 3001 vs Comfort Colors, budget vs premium, pricing floors). Returns a pick + rationale + alternatives. Advisory / knowledge-based.

[#66d03b]

Input parameters:

- `budget_tier` (string)
- `design_uuid` (string): Optional design for context. Design-content-based ranking is a future enhancement; not required today.
- `target_audience` (string)

### `list_catalog_providers` (~88 tokens)

List the fulfillment providers this account can browse catalogs from. Use this to discover valid `provider` values for browse_catalog / get_garment_details — the set is account-specific and auth-gated on the platform (a provider only appears if this account is entitled to it), so never assume a fixed list. Read-only.

[#f0ed53]

Input parameters:

- `workspace` (string)

### `generate_image` (~325 tokens)

Generate a design image (split primitive of design_apparel). Returns the raw generated image; follow with process_transparency for apparel that needs a transparent background. Rate-limit errors are classified (model_rate_limited = one model's provider vs platform_rate_limited = this key's ApparelHub throttle vs request_not_sent = the call never reached ApparelHub), and fallback_trail shows any model substitutions.

[#e8fffe]

Input parameters:

- `augment_prompt_for_transparency` (boolean): Add the solid-green-background hint so the background can be keyed out (default true).
- `no_fallback` (boolean): Disable the model-fallback ladder. By default a rate-limited/transient model transparently retries with a different model (see fallback_trail); set true to fail on the chosen source alone.
- `prompt` (string, required)
- `size` (string): Output shape. 1024x1024 = square; 1024x1792 = tall/portrait (phone cases, posters, banners); 1792x1024 = wide/landscape (mugs, laptop sleeves, wide banners). Pick to match the product's print area —…
- `source` (string): Explicit model name, or omit to auto-pick (Nano Banana; OpenAI for abstract).
- `style` (string)
- `workspace` (string)

### `process_transparency` (~265 tokens)

Key a solid background out of a generated image to true RGBA transparency (flood-fill + enclosed-region sweep + tight crop) and upload the result. Runs server-side (Python + Pillow). If the generator produced a tinted/muted green instead of pure #00FF00, it auto-recovers by re-keying in green-dominance mode (safe for art with no bright-green/lime elements). Returns a NEW image_uuid plus keying_mode.

[#3cd7e7]

Input parameters:

- `background_mode` (string): How to detect the background. auto (default): box-key a pure-green screen, else auto-recover in dominance mode for a tinted/muted green. box: strict pure-#00FF00 keying (best for colorful designs wit…
- `force` (boolean): Bypass the pure-green safety check and box-key anyway. Use only when you have visually confirmed the palette has no colors near the green background.
- `image_url` (string): The image URL, if known (else resolved from the uuid).
- `image_uuid` (string, required)
- `workspace` (string)

### `verify_design_text` (~117 tokens)

Read the text in a design with local OCR (tesseract) when available, so the agent can confirm spelling. Advisory: pass expected_text to get a match verdict, otherwise the detected text is returned for visual review. has_text is null when OCR is unavailable, meaning UNKNOWN — not "no text"; read the design image yourself in that case.

[#57c0af]

Input parameters:

- `expected_text` (string)
- `image_url` (string)
- `image_uuid` (string, required)
- `workspace` (string)

### `design_apparel` (~266 tokens)

End-to-end apparel design with the platform lessons baked in: solid-green-background prompt, transparency keying, and (optionally) a local text check. Returns ready-to-use design(s). Streams progress. Set needs_transparency=false for all-over-print products. Rate-limit errors are classified (model_rate_limited = one model's provider vs platform_rate_limited = this key's ApparelHub throttle vs request_not_sent = the call never reached ApparelHub), and each design's fallback_trail shows any model substitutions. This GENERATES new artwork — when the merchant already owns the file (a logo, a brand mark, a cleared cover), use upload_design instead and do not regenerate their mark.

[#d84efc]

Input parameters:

- `count` (integer)
- `garment_type` (string): Hints source selection.
- `needs_transparency` (boolean)
- `no_fallback` (boolean): Disable the model-fallback ladder. By default a rate-limited/transient model transparently retries with a different model (per-design fallback_trail); set true to fail on the chosen source alone.
- `prompt` (string, required)
- `source` (string)
- `style` (string)
- `verify_text` (boolean)
- `workspace` (string)

### `iterate_design` (~182 tokens)

Generate a variation of an existing design via img2img (e.g. "make the cactus blue"). Almost every source supports editing; only Google Imagen 4 is text-to-image-only (rejected). Multi-reference edits (several source images) work on Seedream, Flux 2 Pro, and Wan; slow-model edits return 202 and are polled automatically.

[#63592e]

Input parameters:

- `change_description` (string, required)
- `no_fallback` (boolean): Disable the model-fallback ladder. By default a rate-limited/transient editing model transparently retries with another edit-capable model (see fallback_trail); set true to fail on the chosen source…
- `preserve` (array)
- `source` (string): Editing source (default Nano Banana).
- `source_design_uuid` (string, required)
- `workspace` (string)

### `fit_aspect` (~338 tokens)

Fit an EXISTING design image to a target aspect ratio without generating a new one. mode="pad" letterboxes it onto a background (keeps the whole design, nothing cropped); mode="crop" center-crops (trims the edges to fill the shape). QUOTA-FREE: this reshapes an existing image and does NOT consume an image-generation credit. Use to adapt a square design to a product's print area (e.g. a tall 9:16 for a phone case or poster, a wide 16:9 for a mug or banner). Returns a NEW design (image uuid + url). Note: for an AI-generated EXTENSION of the borders (outpainting) instead of a flat pad/crop, generate a new image with generate_image at the target size — that DOES use the image-generation quota.

[#9ca805]

Input parameters:

- `aspect` (required): Target aspect ratio as "W:H". Common: 9:16 tall (phone cases, posters), 16:9 wide (mugs, banners), 1:1 square, 4:5 portrait.
- `background` (string): Fill color for the padded bars as #RRGGBB (pad mode only; ignored for crop). Defaults to transparent/white on the platform when omitted.
- `image_uuid` (string, required): The uuid of an existing design to reshape (from generate_image / list_my_designs).
- `mode` (string, required): pad (default): letterbox onto a background, keeping the whole design (nothing lost). crop: center-crop to fill the shape, trimming the edges.
- `workspace` (string)

### `archive_design` (~109 tokens)

Archive a design so it stops showing in the default gallery listing. Reversible with restore_design, and safe: it never touches products that already use the design. This is the right way to retire an unwanted or orphan design. Prefer it over delete_design unless the design must be removed permanently. Find orphan designs first with list_my_designs(on_products=false).

[#16a475]

Input parameters:

- `design_uuid` (string, required): The design uuid to archive.
- `workspace` (string): Workspace uuid (agency accounts).

### `restore_design` (~68 tokens)

Restore a previously archived design so it appears in the default gallery listing again. List archived designs with list_my_designs(archived=true).

[#f0d74b]

Input parameters:

- `design_uuid` (string, required): The design uuid to restore.
- `workspace` (string): Workspace uuid (agency accounts).

### `delete_design` (~89 tokens)

Permanently delete a design and its stored files. Irreversible. Refused with design_in_use if any live product still uses the design, in which case archive_design is the safe alternative. Use archive_design unless the design genuinely must be erased.

[#02e216]

Input parameters:

- `design_uuid` (string, required): The design uuid to delete permanently.
- `workspace` (string): Workspace uuid (agency accounts).

### `upload_design` (~637 tokens)

Upload artwork the merchant ALREADY OWNS and turn it into a design_uuid usable by create_product / ship_product. This is the way to build products from a client's own files — a logo, a brand mark, a cleared cover, a photograph — instead of generating something new. If a client says their mark must not be redrawn, use this; never regenerate or approximate a mark to work around a missing file.

Three ways to supply the file, pick the cheapest one available:
1\. `source_url` — an https URL the server can fetch. One call, no context cost. Best when the asset is already hosted or reachable by link (the link must not require sign-in).
2\. no source at all — returns a presigned `upload_url` you PUT the bytes to yourself, then call this tool again with the returned `image_uuid` to finish. No context cost, full resolution, and the right choice whenever you can make an HTTP request (curl, fetch, requests).
3\. `image_base64` — inline bytes. Works anywhere, but costs roughly 350k tokens per megabyte of file, so reserve it for small files when neither of the above is possible.

Accepts PNG, JPEG, WEBP and SVG. SVG is the BEST input for a logo or mark: it is rendered server-side at print resolution, so it stays crisp at any size. Two things must be true of the SVG first — text converted to outlines, and any linked image embedded — otherwise the upload is refused with instructions rather than silently losing that part of the artwork. For pixel art, or any hard-edge raster mark that must stay crisp, pass upscale="pixel" so a small file is enlarged without being smoothed.

[#85542c]

Input parameters:

- `content_type` (string): Declared MIME type. Detected from the file when the bytes are supplied, so it is only needed for the presigned mode (defaults to image/png). Use image/svg+xml to upload vector.
- `filename` (string): Original filename. Used for the default title.
- `image_base64` (string): Base64-encoded file bytes (a data: URI is accepted). Expensive in context — prefer source_url or the presigned mode. Capped at 4MB decoded.
- `image_uuid` (string): Finish a presigned upload: pass the image_uuid from a previous upload_design call after you have PUT the bytes. Also use this to resume polling if processing was still running.
- `source_url` (string): Public https URL of the artwork. The server fetches it. Must not require sign-in.
- `title` (string): Display title for the design. Defaults to the filename stem.
- `upscale` (string): How to resample if the file is below the 512px print minimum. "pixel" = nearest-neighbour, keeps pixel art and hard-edge marks crisp. "smooth" = for photographic art. "auto" (default) detects. Pass "…
- `workspace` (string): Workspace uuid (agency accounts).

### `ship_product` (~582 tokens)

End-to-end pipeline in ONE call: take a design, generate + verify a mockup (one per imported color, so every color variant has a matching mockup), create the product with the correct field names, add all variants, associate with a store, sync to fulfillment, then (optionally) sync to sales channels as DRAFT. Handles EMBROIDERY garments automatically WHEN the garment is actually embroidered: routes the design to the real embroidery placement and attaches thread colors (derived from the design, or pass thread_colors). Headwear is NOT inherently embroidered -- some providers carry printed (DTF) caps that take photoreal art as-is, so check `accepts_photoreal` on the garment rather than assuming, and call find_garments before concluding a design cannot go on a hat. Face goods (canvas, posters, backpacks, bags, socks, towels, blankets, pillows, cases...) default to print_style "fill": the design is recomposed onto an aesthetically matching background and printed edge-to-edge, so no green-screen background or contrasting borders reach the product. Enforces pricing floors and guards the AQUA-vs-Navy variant trap. Streams progress. PREFER this over chaining create_product + add_variants + sync_to_fulfillment + sync_to_channel yourself — especially for AUTOMATED or SCHEDULED runs — because it guarantees the correct order (store association + fulfillment sync BEFORE any channel sync). Use the split primitives only when you deliberately need a partial/interactive flow.

[#232f74]

Input parameters:

- `design_url` (string): The design URL, if known (else resolved).
- `design_uuid` (string, required): The design to print. Comes from generate_image / design_apparel, OR from upload_design when the merchant already owns the artwork (a logo, a brand mark, a cleared cover). Never regenerate a mark you…
- `garment` (object, required)
- `generate_mockup` (boolean): Default true.
- `pricing` (object, required)
- `print_style` (string): How the design sits on the print face. "fill": recompose onto a matching background and print edge-to-edge (default for face goods like canvas/backpacks/bags/socks/towels/blankets/pillows/cases). "pl…
- `product_meta` (object, required)
- `store_uuid` (string): Omit to create a standalone (unassociated) product.
- `sync_to_channels` (array)
- `thread_colors` (array): EMBROIDERY garments only: explicit Printful thread palette colors. Omit to auto-derive from the design (mapped to the fixed 15-color palette).
- `variants` (array, required)
- `workspace` (string)

### `create_product` (~457 tokens)

Create a STANDALONE product from a design (split primitive) — it is NOT placed on any store yet. Applies the correct field names + pricing floor, routes EMBROIDERY garments (caps/beanies) to their real embroidery placement with Printful thread colors (derived or explicit), and defaults face goods (canvas/backpacks/bags/socks/towels/blankets/pillows/cases...) to print_style "fill" (design recomposed onto a matching background, printed edge-to-edge). Set generate_mockup: true to render a garment mockup as the display image (it auto-derives representative variants from the catalog, so you do NOT need mockup_variant_ids) — otherwise the raw design is used as the display image. To get it onto a store and listed, the required sequence is: add_variants -> sync_to_fulfillment(product_uuid, store_uuid) [associates it with the store + syncs to Printful/Printify] -> sync_to_channel [sales channel]. To run that whole pipeline in one call instead, use ship_product.

[#e8704a]

Input parameters:

- `design_url` (string)
- `design_uuid` (string, required): The design to print. Comes from generate_image / design_apparel, OR from upload_design when the merchant already owns the artwork (a logo, a brand mark, a cleared cover). Never regenerate a mark you…
- `garment` (object, required)
- `generate_mockup` (boolean)
- `mockup_variant_ids` (array): Representative variant ids for the mockup preview (numeric on Printful/Printify, string productUids on Gelato).
- `pricing` (object, required)
- `print_style` (string): How the design sits on the print face. "fill": recompose onto a matching background and print edge-to-edge (default for face goods). "placed": transparency preserved (default for apparel and embroide…
- `product_meta` (object, required)
- `thread_colors` (array): EMBROIDERY garments only: explicit Printful thread palette colors. Omit to auto-derive from the design.
- `workspace` (string)

### `add_variants` (~112 tokens)

Add variants to an existing product (split primitive). Resolves provider_variant_ids by color+size from the product's provider options (or pass them explicitly). Warns on the AQUA-vs-Navy trap. Variants must exist before syncing.

[#c9cff4]

Input parameters:

- `product_ref_id` (string): Enables the AQUA-vs-Navy guard for BC 3001 ("71").
- `product_uuid` (string, required)
- `variants` (array, required)
- `workspace` (string)

### `sync_to_fulfillment` (~108 tokens)

Associate a product with a store AND sync it to that store's fulfillment provider (Printful/Printify). This is the REQUIRED step before sync_to_channel: it both puts the product on the store (a product from create_product is standalone) and creates the manufacturing path the sales-channel listing binds to. Run it after the product has variants.

[#58f859]

Input parameters:

- `product_uuid` (string, required)
- `store_uuid` (string, required)
- `workspace` (string)

### `sync_to_channel` (~193 tokens)

Sync one product to a sales channel (WooCommerce/Shopify/Wix) as a listing. PREREQUISITE: the product must first be associated with the store AND synced to its fulfillment provider — call sync_to_fulfillment(product_uuid, store_uuid) FIRST (it does the store association too). If that prerequisite is missing, this tool now AUTO-HEALS it (associate + fulfillment-sync, then retries once) instead of failing with "product not associated with store" — but the clean, explicit order is sync_to_fulfillment then sync_to_channel, and ship_product does the whole pipeline in one call. Defaults to DRAFT — only push live when the user explicitly asks.

[#32962d]

Input parameters:

- `integration_uuid` (string, required)
- `product_uuid` (string, required)
- `state` (string)
- `store_uuid` (string, required)
- `workspace` (string)

### `update_product` (~157 tokens)

Update a product (name, description, price). For a price change that must propagate to synced channels, prefer cascade_price_change. Optionally set tiktok_listing to enrich the TikTok Shop listing (SEO search terms, product highlights, brand, packaging, TikTok-only title/description, and category_id) — applied when the product is synced to a TikTok channel; ignored by other channels. For channel-defined ATTRIBUTES (Material, Style, Washing Instructions...) use set_listing_attributes instead: it validates against the listing category's real schema and tells you which values the channel refused, which this tool cannot.

[#c20857]

Input parameters:

- `changes` (object, required)
- `product_uuid` (string, required)
- `workspace` (string)

### `set_product_images` (~553 tokens)

Set an existing product's listing images: attach an uploaded photo or a generated lifestyle shot, reorder them, and choose the cover. Use this AFTER the product exists — create_product / ship_product pick the initial mockup themselves.

⚠️ THE LIST REPLACES, IT DOES NOT MERGE. What you send becomes the whole gallery, in the order given. To add one image, READ the current list first and send it back with the new entry in it — sending the new entry alone deletes every other image. Pass `images: null` to reset the gallery back to the product's provider mockups.

⚠️ ORDER IS FUNCTIONAL, NOT COSMETIC. Channels cap how many images a listing may carry and TRUNCATE IN GALLERY ORDER, so position decides what actually ships: TikTok Shop takes 9, Wix 15, Shopify and WooCommerce are unlimited. On a capped channel an image in position 10 is not a lower-priority image, it is an absent one. Put the images that must survive first. The platform stores at most 20.

Each entry carries provenance. `source` says where the file came from (mockup / upload / ai_mockup / print_file / unknown). `ai_generated` is SEPARATE and tri-state on purpose: an uploaded photo may itself have been AI-generated and the platform cannot detect that, so only you can say. Set it truthfully — true, false, or leave it unset when you genuinely do not know. Do not guess it from `source`.

\`cover` sets the display image independently of order, so the cover need not be first. A cover that is not in the gallery is added to it. Replace the gallery without naming a cover and the cover follows to the new first image.

CONCURRENCY: this reads the product first and passes its version back with the write, so a change someone else made in between is REFUSED rather than silently overwritten. On a conflict the tool re-reads and returns `conflict: true` with the current images — it does NOT retry, because the list you built was based on a gallery that no longer exists. Rebuild from `current_images` and call again.

[#781240]

Input parameters:

- `cover` (string): URL of the image to show as the listing cover. Independent of gallery order. Added to the gallery if it is not already in it.
- `images`: The COMPLETE ordered gallery, replacing whatever is there. Null resets to the product's provider mockups. Omit to change only the cover.
- `product_uuid` (string, required): The product whose listing images to set.
- `workspace` (string): Workspace uuid (agency accounts).

### `generate_listing_image` (~593 tokens)

Generate listing photography for an existing product — an on-model shot, a detail crop, a flat lay, or the product in a real setting.

HOW IT WORKS: this EDITS the product's own rendered mockup. It is not text-to-image, and that is the point — the photo shows the actual colourway and the actual printed design, so it depicts the product a shopper will receive.

⚠️ A PRODUCT WITH NO MOCKUP IS REFUSED, not silently generated from scratch. A from-scratch product photo invents a product that does not exist and publishes it as photography of one that does — a listing-takedown and chargeback risk, not merely a quality problem. On `product_has_no_mockup`, render a mockup preview first (ship_product / create_product do this) and call again. Raw print artwork does not count as a mockup.

\`guidance` is EXTRA wording folded in on top of the chosen preset — it does NOT replace it, and it cannot override the constraint that keeps the garment, colour and artwork unchanged. Use it for setting or mood ("outdoors at golden hour"), not to restate the product.

COST: this spends an image generation from the account's quota, like any other. Four styles across thirty products is 120 generations — more than some plans allow in total. Check the plan before looping over a catalogue.

By default the image is generated and RETURNED, not attached: putting a machine-made photo on a live storefront is a separate decision from making one. Pass `attach: true` to append it to the gallery — appended, so existing images are kept, unlike set_product_images which replaces the whole gallery.

[#b24313]

Input parameters:

- `attach` (boolean): Append the result to the product's listing gallery (default false). Existing images are kept. Leave false to review the image before it reaches a storefront.
- `guidance` (string): Optional extra direction layered on top of the preset (setting, mood, lighting). Truncated at 500 characters by the platform.
- `product_uuid` (string, required): The product to photograph. Its existing mockup is what gets edited.
- `set_as_cover` (boolean): When attaching, also make it the listing cover. Ignored unless `attach` is true.
- `source_image_url` (string): Which of the product's existing listing images to edit. Must be one of them and must not be raw print artwork. Defaults to the product's best mockup — usually leave unset.
- `style` (string, required): Which preset to use. `on_model` = worn by a person; `detail` = close crop showing fabric and print texture; `flat_lay` = styled flat, shot from above; `lifestyle` = the product in a real setting. The…
- `workspace` (string): Workspace uuid (agency accounts).

### `unsync_from_channel` (~169 tokens)

Remove a product from ONE sales channel, leaving every other channel and the fulfillment provider untouched. The product stays in ApparelHub; only that channel listing goes away. Use this to delist from a single channel — do NOT hand-roll it against the raw unsync endpoint: that endpoint is product-level and defaults to detaching fulfillment AND cascading to every channel, so getting the parameters slightly wrong unsyncs far more than you asked for. To remove a product from EVERYTHING, use archive_product instead.

[#bd6407]

Input parameters:

- `integration_uuid` (string, required): The sales-channel integration to remove the listing from. Required: without it the platform would cascade to fulfillment and every other channel.
- `product_uuid` (string, required)
- `store_uuid` (string, required)
- `workspace` (string)

### `delete_product` (~101 tokens)

Delete (default) or archive a product. Hard delete cascades to variants; if the product is synced to channels, unsync it first to avoid orphan listings — unsync_from_channel for one channel, archive_product for all of them. (sync_to_channel cannot unsync; it only syncs.)

[#020483]

Input parameters:

- `archive_only` (boolean): Default false (hard delete).
- `product_uuid` (string, required)
- `workspace` (string)

### `diagnose_tiktok_listings` (~627 tokens)

Diagnose TikTok Shop listing quality and optionally apply TikTok's own recommendations. TikTok grades each listing POOR/FAIR/GOOD and a low grade suppresses reach. Returns, per listing: the current tier, the machine-readable issues behind it (code + how_to_solve + the tier that ONE fix unlocks), and TikTok's recommended search terms / titles / descriptions. READ-ONLY unless you pass `apply`. `apply:['search_terms']` is the safe default action — search terms are hidden listing metadata. Passing 'title' or 'description' replaces merchant-visible copy with machine-generated text, so ask the user first; those land on a TikTok-ONLY override and never rewrite the shared product record (which would also change the Shopify/WooCommerce/Wix listings). Use `dry_run` to preview. IMPORTANT — TikTok often flags a title WITHOUT offering a replacement, so `apply:["title"]` returns no_recommendation. That is not a dead end: each listing also carries `requirements` (the computed target, e.g. 40-150 chars — TikTok's own length rules contradict each other and this is the intersection), `building_blocks` (the product's real garment/colors/sizes, so you write from facts rather than inventing them), and `candidates.title` (ready-to-use options, shortest first, each already validated against the requirements). Offer the candidates to the user, or write your own title to the requirements and set it via update_product tiktok_listing.title. Check `issues[].fixable_by` before acting: `photography` means the listing needs new imagery, not better writing — report it rather than trying to write around it. ⚠️ `diagnosable` means "TikTok returned a diagnosis", NOT "this listing is live". TikTok also answers for deactivated and deleted listings, so a catalog can come back entirely diagnosable:true while a third of it is no longer for sale. Read `listing_health` for liveness: "Removed" is gone, "Needs Attention" is present but not visible to buyers, and null means we have never checked — which is NO…

Input parameters:

- `apply` (array): Omit for a read-only diagnosis. Provide the fields to overwrite with TikTok's recommendations.
- `dry_run` (boolean): With `apply`: report what would change without writing or syncing.
- `integration_uuid` (string): Only needed when the store has more than one connected TikTok Shop integration.
- `product_uuids` (array): Limit to these products. Omit to cover every listing synced to TikTok.
- `store_uuid` (string, required)
- `workspace` (string)

### `analyze_what_works` (~86 tokens)

Surface insights from the merchant's own products + orders: best sellers, top channel, average order value. Read-only. Own-account signal (cross-merchant intelligence is a future feature).

[#f6f4f3]

Input parameters:

- `scope` (string)
- `store_uuid` (string)
- `time_window` (string)
- `workspace` (string)

### `auto_optimize_listings` (~168 tokens)

Propose (and, with dry_run=false, apply) optimizations across listings. Uses the sales channel's own demand data, so a listing that people SEE but do not buy is flagged for a listing fix rather than archived — that listing is proven demand with broken conversion, and archiving it destroys the best opportunity in the catalogue. Only a listing the channel reports as genuinely inert is ever archived. Where no demand data is available the proposal is "review" and NOTHING is applied. DEFAULTS TO DRY-RUN; applying only ever archives (never deletes, never goes live).

[#170c11]

Input parameters:

- `dry_run` (boolean): Default true — preview only.
- `scope` (string)
- `store_uuid` (string)
- `workspace` (string)

### `cascade_price_change` (~117 tokens)

Change a product price once and propagate it: the platform cascades to all variants, and (when store_uuid is given) this re-syncs each connected channel so the price is consistent everywhere. Avoids the "changed on one channel, forgot the others" footgun.

[#b8aca8]

Input parameters:

- `also_update_channels` (boolean): Default true.
- `new_price` (number, required)
- `product_uuid` (string, required)
- `store_uuid` (string): Required to re-sync channels.
- `workspace` (string)

### `set_prices_by_margin` (~204 tokens)

Set each variant's price to hit a target profit margin off its OWN cost: price = cost / (1 - margin). Reads per-variant production cost (populated after the fulfillment sync), applies a per-variant price, then re-syncs connected channels. Use this instead of one flat price when costs tier by size (larger sizes cost more, so a single price gives a different margin per size — and can go negative on the biggest). Requires store_uuid (cost lives on the store-products list, not product detail).

[#54cd6b]

Input parameters:

- `also_update_channels` (boolean): Default true.
- `margin` (number, required): Target profit margin as a fraction of the selling price, e.g. 0.15 = 15%.
- `product_uuid` (string, required)
- `store_uuid` (string, required): The store the product is in — needed to read per-variant cost and to re-sync channels.
- `workspace` (string)

### `recover_from_outage` (~91 tokens)

Find products in a failed sync state (fulfillment or channel) and, with dry_run=false + a store_uuid, retry the syncs. DEFAULTS TO DRY-RUN (diagnose only).

[#57cf54]

Input parameters:

- `dry_run` (boolean): Default true — diagnose only.
- `scope` (string)
- `store_uuid` (string)
- `workspace` (string)

### `verify_design_quality` (~97 tokens)

Local QC gate for a design: transparency correctness (alpha, clean corners, white premultiply), resolution, and detected text. Returns a 0-100 score + issues. Needs local Python + Pillow.

[#8e74ec]

Input parameters:

- `design_uuid` (string, required)
- `image_url` (string)
- `needs_transparency` (boolean): Default true; set false for all-over-print.
- `workspace` (string)

### `check_design_compliance` (~118 tokens)

Advisory pre-flight for IP / trademark / prohibited-content risk. Scans the prompt/name and any detected text against common protected marks. NOT legal advice, and NOT an image-content trademark check.

[#fba5cb]

Input parameters:

- `design_uuid` (string)
- `image_url` (string)
- `name` (string): The intended product name (scanned).
- `prompt` (string): The prompt that produced the design (scanned for risk terms).
- `target_channels` (array)
- `workspace` (string)

### `verify_mockup_quality` (~186 tokens)

QC gate for a rendered product MOCKUP (verify_design_quality checks the design; this checks the render on the garment). Deterministically catches three defects that have actually shipped: an un-keyed chroma-green background printed onto the product, an empty render, and a render too small to judge. It does NOT decide whether the design is upright, clipped, seam-split, or whether every face is printed: those need looking at the image, and a pixel statistic that guessed would be confidently wrong on exactly those cases. It returns a fixed visual_checklist for you to answer by VIEWING the render, so grading is consistent across callers. Treat a clean result as "no hard defect found", not "the mockup is good" until you have answered the checklist.

[#feb72a]

Input parameters:

- `preview_url` (string, required): URL of the rendered mockup to grade.

### `approve_order` (~112 tokens)

Approve an order that is awaiting approval, releasing it for fulfillment. For sales-channel (webhook) orders this also auto-submits the order to the fulfillment provider (Printful/Printify). Use when an order is held for review and the user wants to let it proceed.

[#9b6ea5]

Input parameters:

- `order_uuid` (string, required): The order uuid (from list_my_orders / get_order_details).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for Default.

### `unapprove_order` (~99 tokens)

Revert an approved order back to pending so it can be reviewed / re-approved. Only works if the order has NOT yet been submitted to the fulfillment provider. Use to undo an approve_order that was done too early.

[#1d050e]

Input parameters:

- `order_uuid` (string, required): The order uuid (from list_my_orders / get_order_details).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for Default.

### `hold_order` (~109 tokens)

Put an order on hold with an optional reason, pausing it before it is submitted to fulfillment. Use when the user wants to stop an order from proceeding (e.g. to double-check the design or address). Release it later with approve_order.

[#9a2fd5]

Input parameters:

- `order_uuid` (string, required): The order uuid to hold.
- `reason` (string): Why the order is being held (defaults to "Manual hold").
- `workspace` (string): Workspace uuid (agency accounts).

### `cancel_order` (~113 tokens)

Cancel an order. Cancels it locally and, where possible, cancels the draft/order at the fulfillment provider (Printful/Printify). This does NOT refund the customer on the sales channel — the channel is the source of payment. Destructive: only cancel when the user explicitly asks.

[#9e800b]

Input parameters:

- `order_uuid` (string, required): The order uuid (from list_my_orders / get_order_details).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for Default.

### `confirm_order` (~110 tokens)

Confirm a DRAFT order to send it into production at the fulfillment provider. Only works for orders in "draft" status that have already been submitted to a provider (have a provider order id). Use after submit_order_to_fulfillment on a "prepare, then I confirm" store.

[#ebe558]

Input parameters:

- `order_uuid` (string, required): The order uuid (from list_my_orders / get_order_details).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for Default.

### `submit_order_to_fulfillment` (~116 tokens)

Manually submit an order to its fulfillment provider (Printful/Printify) as a DRAFT. For sales-channel orders this auto-fetches the recipient from the channel. Use to un-stick a paid order that never got submitted; confirm it afterward with confirm_order if the store requires confirmation.

[#437d68]

Input parameters:

- `order_uuid` (string, required): The order uuid (from list_my_orders / get_order_details).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for Default.

### `add_order_item` (~320 tokens)

Add an item (e.g. another variant of the same product) to a DRAFT order, before it is confirmed to production. Optionally set custom_price (the new item's per-unit retail price) and/or shipping_cost (the order-level retail shipping the customer pays — NOT the provider's cost); omit them and the variant price / existing shipping are kept. Only works while the order is in "draft" status. Printful and Gelato edit the existing provider draft IN PLACE; Printify has no edit API, so it CANCELS + RE-CREATES the order — the result then carries edit_method="recreated" and a new fulfillment_external_id. Nothing is charged on a draft, so re-creation is safe. Returns 409 "order_not_editable" if the order was already confirmed/submitted to production.

[#f416d1]

Input parameters:

- `custom_price` (number): Per-unit retail price for the new item. Omit to use the variant's own price.
- `order_uuid` (string, required): The DRAFT order uuid (from list_my_orders / get_order_details).
- `quantity` (integer): Quantity to add (default 1).
- `shipping_cost` (number): Order-level retail shipping price (what the customer pays, not the provider cost). Omit to keep the order's current shipping.
- `variant_uuid` (string, required): The product variant to add (e.g. another color/size of the same product).
- `workspace` (string): Workspace uuid (agency accounts). Omit for Default.

### `remove_order_item` (~134 tokens)

Remove a line item from a DRAFT order (the order must keep at least one item). Same provider semantics as add_order_item: Printful/Gelato edit in place, Printify cancels + re-creates. Only works while the order is a draft. Get the order_item_id from get_order_details (each item carries an id).

[#b20b6e]

Input parameters:

- `order_item_id` (integer, required): The order item id to remove (from get_order_details items[].id).
- `order_uuid` (string, required): The DRAFT order uuid.
- `workspace` (string): Workspace uuid (agency accounts).

### `check_order_status` (~105 tokens)

Poll the fulfillment provider for the latest status of an order and update it locally (including any design-approval holds). Read-mostly refresh — safe to call repeatedly. Use to see whether an order has shipped or is on hold at the provider.

[#81b1a2]

Input parameters:

- `order_uuid` (string, required): The order uuid (from list_my_orders / get_order_details).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for Default.

### `reconcile_order` (~121 tokens)

Reconcile a sales-channel order with the channel it came from: pull payment / cancellation FROM the channel and push fulfillment status + tracking TO it. Only sales-channel orders can be reconciled (native orders return reconcilable=false). Use to re-sync an order that drifted (e.g. tracking not relayed to the storefront).

[#ac6b88]

Input parameters:

- `order_uuid` (string, required): The order uuid (from list_my_orders / get_order_details).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for Default.

### `list_order_holds` (~117 tokens)

List the design-approval holds on an order (active and released). Set refresh=true to also poll the fulfillment provider for newly-discovered holds. Read-only. Use to see why an order is stuck at the provider and get the hold_uuid for approve_order_hold / request_hold_changes.

[#2c7422]

Input parameters:

- `order_uuid` (string, required): The order uuid to list holds for.
- `refresh` (boolean): Also poll the provider for new holds (default false).
- `workspace` (string): Workspace uuid (agency accounts).

### `approve_order_hold` (~125 tokens)

Approve a design-approval hold on an order so the provider can proceed. If the provider can't flip the hold via its API (Printful today), the result is deferred with a dashboard_url to finish the approval manually — the hold stays active until the provider's release fires. Get the hold_uuid from list_order_holds.

[#7b5295]

Input parameters:

- `hold_uuid` (string, required): The hold uuid (from list_order_holds).
- `order_uuid` (string, required): The order uuid the hold belongs to.
- `workspace` (string): Workspace uuid (agency accounts).

### `request_hold_changes` (~173 tokens)

Request design changes on a held shipment instead of approving it. change_kind is 'minor' (notes REQUIRED — describe the edit) or 'full_replacement' (re-do the design). If the provider can't action it via API (Printful today), the result is deferred with a dashboard_url. Get the hold_uuid from list_order_holds.

[#acd99f]

Input parameters:

- `change_kind` (string, required): 'minor' = tweak the current design (notes required); 'full_replacement' = new design.
- `hold_uuid` (string, required): The hold uuid (from list_order_holds).
- `notes` (string): What to change. Required when change_kind='minor'.
- `order_uuid` (string, required): The order uuid the hold belongs to.
- `workspace` (string): Workspace uuid (agency accounts).

### `report_fulfillment_issue` (~285 tokens)

Report a post-sale fulfillment issue (defect) on an order: the item does not match the approved mockup, poor print quality, damaged in transit, wrong/missing item, late or lost. Creates a tracked issue and computes the provider report window (30 days from delivery). Follow up with check_fulfillment_issue for the provider-ready problem report and resolve_fulfillment_issue to file/close it or create a replacement order.

[#dc88bc]

Input parameters:

- `category` (string, required): What went wrong (e.g. mockup_mismatch = print does not match the approved mockup).
- `description` (string, required): What happened, in the words the provider report should carry.
- `items` (array): The affected line items. Omit to report the issue against the order as a whole.
- `order_uuid` (string, required): The order the issue is on (from list_my_orders / get_order_details).
- `resolution_requested` (string): What to ask the provider for (default 'reprint'). Providers typically resolve as a free reprint or a wallet refund.
- `shipment_ref` (string): The shipment reference the issue belongs to (multi-shipment orders).
- `title` (string): Short title (defaults to the category label).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `list_fulfillment_issues` (~168 tokens)

List fulfillment issues. With order_uuid: that order's issues plus its report-window eligibility. Without: the workspace-wide issues inbox, filterable by status ('open_any' = open + filed upstream) and store, with limit/offset paging. Read-only.

[#533758]

Input parameters:

- `limit` (integer): Inbox page size (default 50).
- `offset` (integer): Inbox page offset.
- `order_uuid` (string): Scope to one order (the inbox filters below apply only without it).
- `status` (string): Inbox filter; 'open_any' = open + submitted_upstream.
- `store` (string): Inbox filter: a store uuid.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `check_fulfillment_issue` (~143 tokens)

Fetch one fulfillment issue in full (affected items, evidence attachments, provider claim tracking, resolution) and, by default, the provider-ready problem report: a copy-paste summary_text plus the provider dashboard deep-link where the report must be filed (Printful/Printify accept problem reports only in their own dashboards). Read-only.

[#2f6bdb]

Input parameters:

- `include_report` (boolean): Also build the provider-ready problem report (default true).
- `issue_uuid` (string, required): The issue uuid (from list_fulfillment_issues).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `resolve_fulfillment_issue` (~247 tokens)

Progress a fulfillment issue. action='submit_upstream' records that the problem report was filed with the provider (optionally with their claim reference) and returns the dashboard link + summary. action='resolve' closes it with a resolution_type (reprint, refund_wallet, refund_customer, replacement_order, other, none). action='create_replacement' builds a one-click zero-charge replacement (reship) draft order from the affected items; if it cannot be built automatically (no recipient on the provider record, an unlinked variant, or a replacement already exists) the error says what to do instead.

[#a819f9]

Input parameters:

- `action` (string, required): Which lifecycle step to take.
- `issue_uuid` (string, required): The issue uuid (from list_fulfillment_issues).
- `notes` (string): Resolution notes (for action='resolve').
- `provider_claim_ref` (string): The provider's claim/case reference (for action='submit_upstream').
- `resolution_type` (string): How the issue was resolved. REQUIRED when action='resolve'.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `analytics_summary` (~196 tokens)

Headline order/merch KPIs for a date range (gross revenue, orders, units, AOV, COGS, gross profit, margin, cancel/refund/hold rates, fulfillment velocity) plus prior-period deltas. Defaults to the last 30 days. Requires an Advanced Analytics plan (Professional or Enterprise). Read-only.

[#6b72a2]

Input parameters:

- `currency` (string): Reporting currency (e.g. "USD"). Currencies are segmented, never summed.
- `end` (string): End date (YYYY-MM-DD). Omit to default to today (UTC).
- `start` (string): Start date (YYYY-MM-DD). Omit to default to 30 days before end.
- `store` (string): Store uuid to narrow to one store. Omit for all accessible stores.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `analytics_timeseries` (~197 tokens)

KPI trend series over a date range, bucketed by day, week, or month (zero-filled). Each bucket carries gross revenue, gross profit, COGS, order count, units, AOV, average margin, and margin coverage. Requires an Advanced Analytics plan. Read-only.

[#48246d]

Input parameters:

- `currency` (string): Reporting currency (e.g. "USD"). Currencies are segmented, never summed.
- `end` (string): End date (YYYY-MM-DD). Omit to default to today (UTC).
- `interval` (string): Bucket granularity (default day).
- `start` (string): Start date (YYYY-MM-DD). Omit to default to 30 days before end.
- `store` (string): Store uuid to narrow to one store. Omit for all accessible stores.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `analytics_breakdown` (~220 tokens)

Aggregate KPIs broken down by one dimension: product_type, sales_channel, fulfillment_provider, product, variant, or hold_reason. Rows are sorted for display; overflow past the limit folds into an "(everything else)" row so totals still reconcile. Requires an Advanced Analytics plan. Read-only.

[#127113]

Input parameters:

- `currency` (string): Reporting currency (e.g. "USD"). Currencies are segmented, never summed.
- `dimension` (string, required): The dimension to break down by (required).
- `end` (string): End date (YYYY-MM-DD). Omit to default to today (UTC).
- `limit` (integer): Max rows before folding the rest into "(everything else)" (default 50).
- `start` (string): Start date (YYYY-MM-DD). Omit to default to 30 days before end.
- `store` (string): Store uuid to narrow to one store. Omit for all accessible stores.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `analytics_ops` (~171 tokens)

Operational health for a date range: fulfillment velocity (payment→submit→ship→deliver averages), order counts, and cancellation / refund / hold rates, plus a hold-reason breakdown. Requires an Advanced Analytics plan. Read-only.

[#ed8ddd]

Input parameters:

- `currency` (string): Reporting currency (e.g. "USD"). Currencies are segmented, never summed.
- `end` (string): End date (YYYY-MM-DD). Omit to default to today (UTC).
- `start` (string): Start date (YYYY-MM-DD). Omit to default to 30 days before end.
- `store` (string): Store uuid to narrow to one store. Omit for all accessible stores.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `analytics_portfolio` (~169 tokens)

Cross-client portfolio: per-workspace (per-client) KPIs plus rolled-up totals — the agency view. Groups store rollups by each store's current workspace over every workspace you can view analytics in. Requires an agency (Enterprise) account with Advanced Analytics; other accounts get a feature_unavailable error. Read-only.

[#65a008]

Input parameters:

- `currency` (string): Reporting currency (e.g. "USD"). Currencies are segmented, never summed.
- `end` (string): End date (YYYY-MM-DD). Omit to default to today (UTC).
- `start` (string): Start date (YYYY-MM-DD). Omit to default to 30 days before end.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `describe_listing_attributes` (~679 tokens)

Discover the channel-defined listing fields you can set — TikTok product attributes, eBay item specifics, WooCommerce product attributes — and what is currently set. READ-ONLY. Call this BEFORE set_listing_attributes or set_channel_settings: the field names and their allowed values are defined by the channel, so guessing them gets the value dropped.

Pass `product_uuid` for one listing, or `integration_uuid` alone for the shop-wide settings (compliance answers, the shipping template, and a fallback size chart).

BRAND and the per-listing SIZE CHART are per-PRODUCT, not shop-wide — both describe the blank, so a shop selling two blanks needs two values, and a shop-wide size chart would replace the accurate per-garment one on every other listing at once. Ask for them with `product_uuid`.

Each field carries `value_type`, `cardinality` (single vs multi), `free_text` (whether a value outside the list is accepted) and `requirement`. Those are separate on purpose: most fields are enumerated AND accept free text, so neither flag alone tells you what is legal. `requirement: "conditional"` means the field only becomes required once `required_when` holds — typically after you answer a related question one particular way.

\`values` is what is LIVE ON THE CHANNEL, which is not the same as what was last written from here: platform auto-fills and merchant edits made directly in the channel's own admin show up here too. That drift is usually the most useful thing in the response.

\`unset_required` lists fields that are required and empty. Those are NOT filled in for you, deliberately — several are legal attestations. Left unset, the channel picks its own default or grades the listing down, so they are worth resolving with the merchant.

⚠️ CHECK `resolved_for.resolution` when it is present. `explicit_override` means the merchant chose the category. `keyword_match` means it was GUESSED from the product name, and a wrong guess means these fields belong to a different kind of product…

Input parameters:

- `include_values` (string): 'all', or a comma-separated list of field keys, to inline allowed values that are elided by default.
- `integration_uuid` (string): Which connected sales channel. Required when `product_uuid` is omitted; otherwise only needed if the store has more than one channel connected.
- `product_uuid` (string): The listing to inspect. Omit for the shop-wide settings.
- `store_uuid` (string, required)
- `workspace` (string)

### `set_listing_attributes` (~564 tokens)

Set channel-defined listing attributes on ONE product (Material, Style, Washing Instructions and similar). Call describe_listing_attributes first to learn the field keys and their allowed values.

A PARTIAL WRITE SUCCEEDS. Send four values with one bad and the three good ones are stored while the bad one is reported — you do not have to get them all right at once. A value the channel refuses comes back in `rejected` with a machine-readable `reason` and the allowed values echoed, so you can correct it in one more turn rather than guessing. Rejections are never dropped silently.

⛔ NEVER INVENT A VALUE. Relay what the merchant told you. If you cannot get a value from them, leave it UNSET and say so — an unset field is honest, an invented one is not. Do not infer it from the product type, do not copy it from another shop, and do not pick the nearest allowed value because it looks close.

📏 SIZE CHART. US apparel is graded down without one. A chart is normally rendered automatically from the fulfillment provider's real measurements, so most listings need nothing. When one IS flagged, prefer `size_chart_measurements` (an object — call import_size_measurements to fill it from the provider) over `size_chart_template_id`: the template id can only come from a human in the channel's own admin, because the channel publishes no way to list, verify or correct one.

⛔ NEVER INVENT MEASUREMENTS. They are what a buyer reads before choosing a size. Do not derive a table from the garment type, do not copy one from a similar product, and do not fill a gap with a plausible number. A malformed table is refused whole, with a reason — nothing is half-applied. A missing cell is fine and renders blank; an invented one means somebody receives a garment that does not fit.

Setting a value does NOT change the live listing on its own — the channel is updated on the next sync. Pass `sync: true` to push it immediately, or run sync_to_channel afterwards.

[#ef218b]

Input parameters:

- `integration_uuid` (string): Only needed when the store has more than one connected channel.
- `product_uuid` (string, required)
- `remove` (array): Field keys to clear.
- `store_uuid` (string, required)
- `sync` (boolean): Push the listing to the channel immediately after storing.
- `values` (object, required): field key -> value. Use an array for a field whose `cardinality` is "multi", and an object for one whose `value_type` is "object" (build it from that field's `channel_ref.object_schema`). Values are…
- `workspace` (string)

### `set_channel_settings` (~539 tokens)

Set SHOP-WIDE listing settings for one connected sales channel: product compliance attestations, the shipping template, and a fallback size chart. These apply to every listing on that channel, so they are set once rather than per product. Call describe_listing_attributes with `integration_uuid` (and no `product_uuid`) first to see which settings this channel defines and what each one accepts.

⚠️ BRAND and the per-listing SIZE CHART are NOT here — they are per-product (use set_listing_attributes), because both describe the blank rather than the shop. `default_size_measurements` is the one size-chart setting that is shop-wide, and only as a FALLBACK for listings with no provider measurements of their own. Set it only when the whole catalogue is ONE blank: with a mixed catalogue it would be applied to garments it does not describe.

⛔ SOME OF THESE ARE LEGAL ATTESTATIONS. Product-compliance answers (for example California Proposition 65 questions) are statements the MERCHANT makes about their goods, and they carry legal weight. ⛔ NEVER INVENT A VALUE. Relay what the merchant told you. If you cannot get a value from them, leave it UNSET and say so — an unset field is honest, an invented one is not. Do not infer it from the product type, do not copy it from another shop, and do not pick the nearest allowed value because it looks close. In particular: do not answer "No" because it is usually "No", and do not reason from the product being printed apparel — Proposition 65 covers clothing, and some inks and finishes do contain listed chemicals. Ask the merchant, relay their answer, and if they do not have one, leave it unset and tell them it is outstanding.

Answering one of these questions "Yes" can make a follow-up field required — naming the specific chemicals, from a list of hundreds. That follow-up appears in `unset_required` and is never filled in for the merchant.

A value the channel refuses comes back in `rejected` with a machine-readable `reason` and the allowed…

Input parameters:

- `integration_uuid` (string, required)
- `remove` (array): Setting keys to clear.
- `store_uuid` (string, required)
- `values` (object, required): setting key -> value, exactly as the merchant supplied it. Use an object for a setting whose `value_type` is "object".
- `workspace` (string)

### `import_size_measurements` (~361 tokens)

Get the blank's real per-size measurements from its fulfillment provider, in the exact shape `size_chart_measurements` takes. READ-ONLY. Use this instead of asking a merchant to type a size chart, and never instead of asking them when it comes back unavailable.

It imports nothing by itself — adopting a set of measurements is the merchant's decision. Show them the table, let them correct it, then write it back with set_listing_attributes as `size_chart_measurements`.

\`available: false` is an ANSWER, not a failure. Branch on `reason`:
• `provider_publishes_no_size_guide` — this provider has no size-guide API at all (Printify and Gelato), so no product of theirs will ever import. Permanent: ask the merchant for the blank manufacturer's own numbers.
• `no_size_guide_for_this_blank` — the provider does publish guides, just not for this item. Normal for non-apparel.
• `provider_lookup_unavailable` — transient. Retry.
• `product_has_no_fulfillment_provider` — nothing to import from.

⚠️ TELL THE MERCHANT WHERE THE NUMBERS CAME FROM. `source` names the provider and the catalog item. These are measurements a buyer makes a purchase decision on, published in the merchant's name — present them as the provider's figures for a specific blank, not as something you know.

\`notes`, when present, lists what was adjusted on the way through (a provider sometimes files a measurement under a size outside its own size list). Pass those on rather than dropping them.

[#19618b]

Input parameters:

- `product_uuid` (string, required)
- `store_uuid` (string, required)
- `workspace` (string)

### `channel_performance` (~476 tokens)

What the sales channel reports about each of your listings: impressions, clicks, click-through rate and units sold, plus a state telling you what to do about it. Use this to find listings people SEE but do not BUY — the order-based analytics tools cannot show you those, because to them a listing with 5,000 views and no sales looks identical to one nobody has ever seen. States: winner (scale it), conversion_blocked (lots of views, few clicks — the listing card is losing them), pdp_blocked (they click but do not buy — the product page is losing them), starved (too few views to judge; needs discovery, NOT a rewrite), dead (no activity at all; the only state safe to archive), no_channel_data (synced to the channel, but the channel has never reported it — usually means it is not actually live; check the listing before anything else), insufficient_data (not enough signal, or this channel does not report it). READ `summary.shop` FIRST. If it says no_channel_traffic, the whole shop is barely being served and no per-listing state means anything yet — the problem is distribution, and editing titles or images cannot fix a listing nobody is shown. Each row says which channel and store it came from — always check that before comparing two rows, since a channel product id is only unique within its own channel. ALWAYS check the coverage block before treating a missing metric as zero. Read-only.

[#73dd2d]

Input parameters:

- `end` (string): End date (YYYY-MM-DD), channel-local. Defaults to yesterday.
- `limit` (integer): Cap listings returned.
- `provider` (string): Only listings from this sales channel, by name (e.g. "TikTok Shop"). Case-insensitive. channels_present lists the channels that actually have data.
- `start` (string): Start date (YYYY-MM-DD), in the sales channel's own local dates. Defaults to 28 days back.
- `state` (string): Filter to one state, e.g. "conversion_blocked" to list only proven-demand listings that are failing to convert.
- `store` (string): Only listings from this store uuid.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `channel_opportunities` (~275 tokens)

The listings wasting the most demand: proven traffic, broken conversion, ranked by how many people saw them and did not buy. This is the natural starting point for an optimisation pass — fix these before touching anything else, because the demand is already there and only the listing is in the way. Also returns per-state counts and, separately, the listings that are genuinely inert (state "dead") and therefore safe to archive. Nothing else is safe to archive. READ `shop` BEFORE acting on anything else here. If the shop as a whole is getting almost no views, safe_to_archive will be empty and top_opportunities will be thin — not because the listings are fine, but because nothing has been seen enough to judge. That is a distribution problem and no listing edit will move it. Read-only.

[#7c8c30]

Input parameters:

- `end` (string): End date (YYYY-MM-DD), channel-local. Defaults to yesterday.
- `provider` (string): Narrow to one sales channel, by name.
- `start` (string): Start date (YYYY-MM-DD), in the sales channel's own local dates. Defaults to 28 days back.
- `store` (string): Narrow to one store uuid.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `channel_coverage` (~87 tokens)

Which of your connected sales channels report performance data, and which metrics each one supplies. Check this before concluding a listing has no traffic: a channel that reports nothing looks identical to a channel reporting zeros unless you look here. Also flags shops that must be RECONNECTED before performance data can flow. Read-only.

[#df2820]

Input parameters:

- `workspace` (string): Workspace uuid to scope to.

### `listing_changes` (~375 tokens)

What has been changed on your listings, and whether it worked. The other half of channel_performance: that says what to fix, this says whether the last fix landed.

Every shopper-visible change — title, description, images, price, search terms, variants, availability — is recorded automatically when it is made, along with the signal state that prompted it. Once the channel has finalised enough days either side, a verdict is computed on the ONE metric that change should have moved (a title is judged on click-through, not revenue).

⛔ `unmeasurable` IS THE DEFAULT VERDICT, NOT AN ERROR, and it does not mean the change had no effect. It means the data cannot support a conclusion — most often because the shop is not getting enough views for any single edit to register, in which case the answer is distribution and not more editing. Read `verdict_reason` before saying anything about a change: no_shop_traffic, window_not_final, metric_not_reported, no_baseline.

\`confounded` means two changes landed close enough together that neither owns the result. Do not attribute it to whichever was most recent.

Read-only. Verdicts settle when read, so a window that closed since you last looked is already answered.

[#a02f68]

Input parameters:

- `days` (integer): How far back to look. Default 90.
- `kind` (string): Limit to one kind of change, e.g. "title" or "price".
- `product` (string): Limit to one product uuid — that listing's change history.
- `store` (string): Limit to one store uuid.
- `verdict` (string): Limit to one verdict, e.g. "improved" or "worsened".
- `workspace` (string): Workspace uuid to scope to.

### `list_collections` (~54 tokens)

List a store's product collections (categories/groups), each with its product count and per-channel sync status. Read-only.

[#1d7020]

Input parameters:

- `store_uuid` (string, required)
- `workspace` (string)

### `get_collection` (~56 tokens)

Get a single collection by uuid, including its member products and per-channel sync status. Read-only.

[#71cbae]

Input parameters:

- `collection_uuid` (string, required)
- `store_uuid` (string, required)
- `workspace` (string)

### `create_collection` (~92 tokens)

Create a new (empty) collection in a store. Provide a name (sent to the platform as the collection title) and an optional description. Add products with add_products_to_collection, then sync_collection to push it to a sales channel.

[#ff2c5e]

Input parameters:

- `description` (string)
- `name` (string, required)
- `store_uuid` (string, required)
- `workspace` (string)

### `update_collection` (~83 tokens)

Update a collection's name and/or description. A name change is sent to the platform as the collection title. Editing a synced collection marks it for re-sync.

[#1d73cf]

Input parameters:

- `collection_uuid` (string, required)
- `description` (string)
- `name` (string)
- `store_uuid` (string, required)
- `workspace` (string)

### `delete_collection` (~69 tokens)

Delete a collection. If it is synced to any sales channel, the platform unsyncs it there first. The member products are NOT deleted, only the grouping.

[#afdbfe]

Input parameters:

- `collection_uuid` (string, required)
- `store_uuid` (string, required)
- `workspace` (string)

### `add_products_to_collection` (~88 tokens)

Add one or more products (by uuid) to a collection. The products must already be associated with the store. If the collection is synced to a channel, the products are added there too.

[#e8080a]

Input parameters:

- `collection_uuid` (string, required)
- `product_uuids` (array, required)
- `store_uuid` (string, required)
- `workspace` (string)

### `remove_product_from_collection` (~79 tokens)

Remove a single product from a collection (the product itself is not deleted). If the collection is synced to a channel, the product is removed there too.

[#c2fc5a]

Input parameters:

- `collection_uuid` (string, required)
- `product_uuid` (string, required)
- `store_uuid` (string, required)
- `workspace` (string)

### `sync_collection` (~116 tokens)

Sync a collection to a sales channel (creates/updates the channel-side category and places all products in it that are already synced there). integration_uuid selects which channel; not all channels support collections (e.g. TikTok Shop), which returns a clear "collections_unsupported" error.

[#7f246a]

Input parameters:

- `collection_uuid` (string, required)
- `integration_uuid` (string, required): The sales-channel integration to sync this collection to (required by the platform).
- `store_uuid` (string, required)
- `workspace` (string)

### `copy_product_to_workspace` (~168 tokens)

Copy a product into another workspace (agency accounts). Non-destructive: the original is untouched and the copy lands as an unsynced DRAFT (no store mapping, fresh variants). Use list_my_workspaces to get the destination workspace uuid. If the product lives in a non-Default workspace, pass source_workspace too.

[#d046a8]

Input parameters:

- `destination_workspace` (string, required): Destination workspace uuid to copy/move into. Get it from list_my_workspaces (resolve a client/brand name to its uuid).
- `product_uuid` (string, required): The product to transfer.
- `source_workspace` (string): The workspace the asset currently lives in. Omit only if it is in your Default workspace; otherwise you must pass it (the platform scopes reads to a single workspace).

### `move_product_to_workspace` (~179 tokens)

Move a product to another workspace (agency accounts) by re-stamping its workspace. Fails with a 409 (blocking list) if the product is mapped to a store or has orders — copy it instead in that case (check first with check_product_move). Use list_my_workspaces for the destination uuid; pass source_workspace if the product is not in your Default workspace.

[#f36e77]

Input parameters:

- `destination_workspace` (string, required): Destination workspace uuid to copy/move into. Get it from list_my_workspaces (resolve a client/brand name to its uuid).
- `product_uuid` (string, required): The product to transfer.
- `source_workspace` (string): The workspace the asset currently lives in. Omit only if it is in your Default workspace; otherwise you must pass it (the platform scopes reads to a single workspace).

### `check_product_move` (~127 tokens)

Dry run: report whether a product can be MOVED to another workspace, without changing anything. Returns {eligible, blockers} — a non-empty blockers list (e.g. asset_in_use, asset_has_orders, forbidden_source/destination) means move would fail, so copy instead. Read-only.

[#81f916]

Input parameters:

- `destination_workspace` (string, required): Destination workspace uuid (from list_my_workspaces).
- `product_uuid` (string, required): The product to check.
- `source_workspace` (string): The product's current workspace uuid; omit only if it is in your Default workspace.

### `copy_design_to_workspace` (~155 tokens)

Copy a generated design image into another workspace (agency accounts). Non-destructive: the original stays put and the copy gets its own duplicated image file. Use list_my_workspaces for the destination uuid; pass source_workspace if the design is not in your Default workspace.

[#3090dc]

Input parameters:

- `design_uuid` (string, required): The design to transfer.
- `destination_workspace` (string, required): Destination workspace uuid to copy/move into. Get it from list_my_workspaces (resolve a client/brand name to its uuid).
- `source_workspace` (string): The workspace the asset currently lives in. Omit only if it is in your Default workspace; otherwise you must pass it (the platform scopes reads to a single workspace).

### `move_design_to_workspace` (~180 tokens)

Move a generated design image to another workspace (agency accounts). Fails with a 409 (blocking list) if a product that uses the design is mapped to a store or has orders — copy it instead in that case (check first with check_design_move). Use list_my_workspaces for the destination uuid; pass source_workspace if the design is not in your Default workspace.

[#8b8b8a]

Input parameters:

- `design_uuid` (string, required): The design to transfer.
- `destination_workspace` (string, required): Destination workspace uuid to copy/move into. Get it from list_my_workspaces (resolve a client/brand name to its uuid).
- `source_workspace` (string): The workspace the asset currently lives in. Omit only if it is in your Default workspace; otherwise you must pass it (the platform scopes reads to a single workspace).

### `check_design_move` (~128 tokens)

Dry run: report whether a generated design can be MOVED to another workspace, without changing anything. Returns {eligible, blockers} — a non-empty blockers list (a product using the design is in use, or forbidden_source/destination) means move would fail, so copy instead. Read-only.

[#1e988c]

Input parameters:

- `design_uuid` (string, required): The design to check.
- `destination_workspace` (string, required): Destination workspace uuid (from list_my_workspaces).
- `source_workspace` (string): The design's current workspace uuid; omit only if it is in your Default workspace.

### `create_workspace` (~77 tokens)

Create a new workspace in the account (agency / Enterprise). Name must be unique within the account. Needs an account-wide key; a tier without the agency feature gets feature_unavailable. Returns the new workspace uuid.

[#51f6a7]

Input parameters:

- `name` (string, required): Workspace name (unique within the account, max 128 chars).

### `update_workspace` (~94 tokens)

Rename a workspace or archive/unarchive it (agency / Enterprise). The Default workspace cannot be archived. Needs an account-wide key.

[#e8ebf4]

Input parameters:

- `archived` (boolean): Archive (true) or unarchive (false). Ignored for the Default workspace.
- `name` (string): New name (max 128 chars).
- `workspace_uuid` (string, required): Workspace uuid (from list_my_workspaces).

### `check_workspace_deletion` (~69 tokens)

Dry run: preview deleting a workspace (agency / Enterprise) — the stores that would move to the Default workspace and the members whose assignment would be revoked. Changes nothing. Read-only.

[#bcfd4e]

Input parameters:

- `workspace_uuid` (string, required): Workspace uuid (from list_my_workspaces).

### `delete_workspace` (~73 tokens)

Delete a workspace (agency / Enterprise). Its stores are reassigned to the Default workspace and member assignments revoked first. The Default workspace cannot be deleted. Preview with check_workspace_deletion. Needs an account-wide key.

[#c566a1]

Input parameters:

- `workspace_uuid` (string, required): Workspace uuid (from list_my_workspaces).

### `assign_workspace_member` (~130 tokens)

Assign an account member to a workspace with a role, or update their existing role (agency / Enterprise). The target must already be a member of the account (invite_member first). Needs an account-wide key.

[#5ec6df]

Input parameters:

- `role` (string, required): Workspace role: director (full control), creator (design/build), merchandiser (price/publish), operator (post-sale), viewer (read-only).
- `user_public_id` (string, required): The member's user public_id (from list_account_members).
- `workspace_uuid` (string, required): Workspace uuid (from list_my_workspaces).

### `unassign_workspace_member` (~80 tokens)

Revoke a member's assignment to a workspace (agency / Enterprise). The account owner cannot be unassigned from the Default workspace. Needs an account-wide key.

[#7d3ad6]

Input parameters:

- `user_public_id` (string, required): The member's user public_id.
- `workspace_uuid` (string, required): Workspace uuid (from list_my_workspaces).

### `move_store_to_workspace` (~85 tokens)

Move a store into one of the account's workspaces (agency / Enterprise). This changes who can access the store, so it needs account owner/admin + an account-wide key.

[#9dde84]

Input parameters:

- `store_uuid` (string, required): The store to move (from list_my_stores).
- `workspace_uuid` (string, required): Destination workspace uuid (in the same account).

### `get_account_overview` (~52 tokens)

Account name, your role, whether the agency feature is enabled, and seat accounting (used / included / billable). Agency / Enterprise; needs an account-wide key. Read-only.

[#bf5472]

### `get_role_matrix` (~49 tokens)

The workspace roles and the role → capability matrix, so you can pick a role before assigning a member. Agency / Enterprise; needs an account-wide key. Read-only.

[#894f5c]

### `list_account_members` (~140 tokens)

List account members and their per-workspace assignments (agency / Enterprise). Filterable + paginated. Needs an account-wide key. Read-only.

[#b3bd65]

Input parameters:

- `account_role` (string): Filter by account role.
- `in_workspace` (string): Only members assigned to this workspace uuid.
- `page` (integer): Page number (default 1).
- `per_page` (integer): Page size (default 50, max 100).
- `q` (string): Free-text match on email/username.
- `workspace_role` (string): Members holding this workspace role (combine with in_workspace for "role in that workspace").

### `remove_member` (~70 tokens)

Remove a member from the account entirely (agency / Enterprise): all their workspace assignments are revoked and seat billing synced. The account owner cannot be removed. Needs an account-wide key.

[#e5169b]

Input parameters:

- `user_public_id` (string, required): The member's user public_id (from list_account_members).

### `invite_member` (~116 tokens)

Invite someone to the account by email, optionally pre-assigning a workspace + role (agency / Enterprise). An existing ApparelHub user is auto-added immediately; a new email gets a pending invite. Needs an account-wide key.

[#d3c424]

Input parameters:

- `account_role` (string): Account role (default member).
- `email` (string, required): Email to invite.
- `role` (string): Workspace role (required when workspace_uuid is set).
- `workspace_uuid` (string): Optional: pre-assign to this workspace uuid.

### `list_invites` (~48 tokens)

List the account’s pending invites, each with the target workspace name and a copyable accept URL (agency / Enterprise). Needs an account-wide key. Read-only.

[#0cf62c]

### `revoke_invite` (~57 tokens)

Revoke a pending invite so its token can no longer be used (agency / Enterprise). Needs an account-wide key.

[#8a3ee8]

Input parameters:

- `invite_uuid` (string, required): The pending invite uuid (from list_invites).

### `resend_invite` (~61 tokens)

Re-send a pending invite’s email with the SAME token and extend its TTL 14 days (agency / Enterprise). Needs an account-wide key.

[#4dd71c]

Input parameters:

- `invite_uuid` (string, required): The pending invite uuid (from list_invites).

### `accept_invite` (~79 tokens)

Accept a pending invite by token. The authenticated key-holder’s email must match the invite. Works on any tier and with a workspace-scoped key (the invitee side). Returns the account + workspace you were added to.

[#e7e5ee]

Input parameters:

- `token` (string, required): The invite token (from the invite email / accept URL).

### `get_store_settings` (~109 tokens)

Read a store's fulfillment workflow + notification settings: fulfillment_mode (auto/confirm/review), approval_authority (human/agent/rules), the margin / high-value / first-time-customer hold guardrails, auto-reconcile, and payment settings. Read-only.

[#3b3cd0]

Input parameters:

- `store_uuid` (string, required): The store uuid (from list_my_stores).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `update_store_settings` (~518 tokens)

Update a store's fulfillment workflow / notification settings. Only the fields you pass are changed. fulfillment_mode: "auto" (auto-pilot: paid -> draft -> auto-confirm -> production), "confirm" (auto-draft, you confirm each order), "review" (held before submission for approval). The hold_* guardrails escalate an otherwise-auto/confirm order to a pre-submission review. Set hold_orders_above_amount / hold_below_margin_pct to null to disable that guardrail. hold_channel_risk_review is ON by default and OVERRIDES fulfillment_mode (including "auto"): an order the sales channel is reviewing is never sent to fulfillment while it may still be voided.

[#fe3014]

Input parameters:

- `approval_authority` (string): Who decides when a review is required: human (UI queue), agent (API/callback), rules (auto unless a guardrail trips).
- `auto_fulfill_on_payment` (boolean): Auto-submit to the provider once payment clears.
- `auto_reconcile_orders` (boolean): Periodically re-sync open sales-channel orders with their channel (manual reconcile always works regardless).
- `fulfillment_mode` (string): Automation level: auto (auto-pilot), confirm (you confirm each), review (approve before submit).
- `hold_below_margin_pct` (number|null): Auto-hold orders below this profit-margin percent. null disables the guardrail.
- `hold_channel_risk_review` (boolean): ON by default. Hold an order the SALES CHANNEL has flagged as under its own risk review, so it is never sent to fulfillment while the channel may still void it. Unlike the other guardrails this one O…
- `hold_first_time_customer` (boolean): Auto-hold the first order from a new customer.
- `hold_on_negative_margin` (boolean): Auto-hold orders that would lose money.
- `hold_orders_above_amount` (number|null): Auto-hold orders whose total exceeds this amount. null disables the guardrail.
- `notify_on_new_order` (boolean): Send a notification when a new order arrives.
- `notify_on_shipment` (boolean): Send a notification when an order ships.
- `require_payment_before_fulfill` (boolean): Block fulfillment until the order is paid.
- `store_uuid` (string, required): The store uuid to update.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `create_store` (~129 tokens)

Create a new ApparelHub store. Only a name is required. The store starts CLOSED — connect a fulfillment provider (Printful/Printify), then call activate_store to make it ACTIVE. In an agency account pass workspace=<uuid> to create it in a specific client workspace.

[#9d5cbc]

Input parameters:

- `description` (string): Optional store description.
- `logo` (string): Optional logo image URL.
- `name` (string, required): Store name (must be unique within the account).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `archive_store` (~131 tokens)

Archive a store (use instead of delete for stores with order history — order records are kept for accounting, but the store is hidden from the default listing and stops ingesting new orders). Restore it later with unarchive_store. Set disconnect_provider=true to also disconnect every connected fulfillment provider and remove its stored credentials.

[#b85c96]

Input parameters:

- `disconnect_provider` (boolean): Also disconnect connected fulfillment providers and remove their credentials (default false).
- `store_uuid` (string, required): The store uuid to archive.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `unarchive_store` (~87 tokens)

Restore an archived store. It comes back as CLOSED (or ACTIVE if a fulfillment provider is still connected); if it landed CLOSED, connect a provider and call activate_store to reopen it.

[#468f04]

Input parameters:

- `store_uuid` (string, required): The store uuid to unarchive.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `activate_store` (~97 tokens)

Activate a store so it can list products and ingest orders. Requires at least one fulfillment provider (e.g. Printful) to be connected first — otherwise this fails. Use after create_store or unarchive_store once a provider is connected.

[#87c31f]

Input parameters:

- `store_uuid` (string, required): The store uuid to activate.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `record_order_payment` (~185 tokens)

Record a manual payment on an order that is awaiting payment (payment_status="pending"). Use payment_method="sales_channel" for an order already paid on its storefront (Shopify/WooCommerce/Wix — the channel is the source of payment), or "stripe" for an order taken through ApparelHub's own card flow. This marks the order paid; it does not charge a card.

[#81d42b]

Input parameters:

- `amount` (number): Optional amount for the caller's intent; the recorded amount comes from the order total.
- `order_uuid` (string, required): The order uuid (from list_my_orders).
- `payment_method` (string, required): e.g. "sales_channel" (paid on the storefront) or "stripe" (ApparelHub card flow).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `mark_order_no_payment` (~95 tokens)

Mark an order as having no payment expected (e.g. a free / comp / sample order). Sets its payment status to "no payment". Use when an order should proceed without a recorded payment.

[#23c7f4]

Input parameters:

- `order_uuid` (string, required): The order uuid to mark as no-payment.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `set_order_payment_method` (~115 tokens)

Change the recorded payment method on an order that already has a payment recorded (e.g. correct "stripe" to "sales_channel"). This is a bookkeeping label change; it does not move any money.

[#588a7f]

Input parameters:

- `order_uuid` (string, required): The order uuid to update.
- `payment_method` (string, required): The new payment method label (e.g. "sales_channel", "stripe").
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `sync_orders` (~91 tokens)

Pull the latest orders from connected fulfillment providers. Pass store_uuid to sync one store; omit it to sync all of your stores. Use to refresh orders that have not come through yet.

[#64e731]

Input parameters:

- `store_uuid` (string): Scope the sync to one store (omit for all stores).
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `estimate_order_costs` (~237 tokens)

Estimate production + shipping + tax + total for an order WITHOUT creating it (read-only against the fulfillment provider, no order placed). Give the store, the recipient, and the variants + quantities. Use to preview landed cost before placing an order. AVAILABLE FOR PRINTFUL AND GELATO ONLY: Printify offers no pre-order estimate, so a Printify-fulfilled store returns a refusal rather than a number — do not retry it, and do not present a cross-provider landed-cost comparison that silently omits Printify. Both country_code AND address1 are required; the platform rejects the request without a street address. The variants must already be synced to the fulfillment provider.

[#a2bd48]

Input parameters:

- `currency` (string): Currency code (defaults to USD).
- `items` (array, required): Line items to price.
- `recipient` (object, required): Ship-to details. country_code and address1 are both required; city/state/zip improve accuracy.
- `store_uuid` (string, required): The store the order would be placed in.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `get_orders_summary` (~76 tokens)

Aggregated stats for the orders dashboard: counts of orders pending approval / awaiting payment / in fulfillment / shipped today, plus today's revenue and profit, and a per-store breakdown. Read-only.

[#88d8a4]

Input parameters:

- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `list_pending_fulfillments` (~86 tokens)

List orders in a store that have pending fulfillment data needing attention (used by the reconciliation view). Read-only. Use to find orders that stalled before reaching the provider.

[#80ba1e]

Input parameters:

- `store_uuid` (string, required): The store uuid to check.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `archive_product` (~112 tokens)

Archive a product: unsync it from every connected sales channel and its fulfillment provider, then hide it. Fails (returns blocking_orders) if any pending order still references its variants — cancel or fulfill those first. Restore it later with restore_product. Use archive rather than delete_product when a product has order history.

[#4b1462]

Input parameters:

- `product_uuid` (string, required): The product uuid to archive.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `restore_product` (~91 tokens)

Restore a previously archived product (sets it back to active). It is not re-synced to any sales channel automatically — sync it again afterward if you want it live. Use to undo archive_product.

[#8d20f7]

Input parameters:

- `product_uuid` (string, required): The product uuid to restore.
- `workspace` (string): Workspace uuid to scope to (agency accounts). Omit for the Default workspace.

### `get_api_reference` (~170 tokens)

Discover the full ApparelHub agent API: returns a compact index of every endpoint (path, methods, summary) from the live OpenAPI spec. Use this when no dedicated tool covers what you need, then call it with api_request. Read-only.

Also returns `connector`, which reports what THIS server actually serves: its version, and the name of every tool. **If a capability seems missing, check that first.** A tool listed in `connector.tool_names` that you cannot call means your own tool list is stale, not that the tool is unbuilt — say so and tell the user to reconnect, rather than reporting the feature as missing.

[#5dc40c]

Input parameters:

- `filter` (string): Only return endpoints whose path contains this substring (e.g. "orders", "collections").

### `api_request` (~192 tokens)

Escape hatch: make an authenticated request to any ApparelHub agent API endpoint under /agents/v1, as the connected account. PREFER a dedicated tool when one exists (they return clean, guarded results) — use this only for capabilities no tool covers. Call get_api_reference first to find the right path. `path` is relative (e.g. "orders", "store/<uuid>/settings"); no full URLs. Scoped to the account's own permissions.

[#48e53d]

Input parameters:

- `body` (object): JSON request body (for POST/PUT/PATCH).
- `method` (string, required): HTTP method.
- `path` (string, required): Relative path under /agents/v1, e.g. "orders" or "product/<uuid>/archive". No host, no "..".
- `query` (object): Query-string parameters.
- `workspace` (string): Workspace uuid to scope to (agency accounts).

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/apparelhub-ai-apparelhub-mcp/apparelhub-mcp-server#diagnostics

## Score history

- 2026-09-29: 63

## Common questions

### What is the ApparelHub MCP server?

ApparelHub is an MCP server listed in the public MCP registry as io.github.ApparelHub-AI/apparelhub-mcp. Run a custom-merch store from an agent: design, build products, list on every channel, fulfill. This page covers its npm package (@apparelhub/mcp-server).

### Is the ApparelHub MCP server safe to use?

ApparelHub scores 63 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 29 September 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the ApparelHub MCP server expose?

ApparelHub exposes 123 tools: check_setup_readiness, list_connectable_providers, connect_fulfillment_provider, connect_sales_channel, start_channel_connect, and 118 more. Their descriptions and schemas cost roughly 22,846 tokens of context every time the server is loaded.

### Is the ApparelHub MCP server still maintained?

ApparelHub is still listed as active in the MCP registry. We last reached this channel on 29 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the ApparelHub MCP server under?

ApparelHub declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- npm package: https://www.npmjs.com/package/@apparelhub/mcp-server
- Socket report: https://socket.dev/npm/package/@apparelhub/mcp-server
- Repository: https://github.com/ApparelHub-AI/apparelhub-mcp
- Website: https://apparelhub.ai/agents
- Changelog RSS feed: https://verifymcp.io/servers/apparelhub-ai-apparelhub-mcp/apparelhub-mcp-server.xml
- Changelog JSON feed: https://verifymcp.io/servers/apparelhub-ai-apparelhub-mcp/apparelhub-mcp-server.json
- HTML version of this page: https://verifymcp.io/servers/apparelhub-ai-apparelhub-mcp/apparelhub-mcp-server
