{"count":29,"docs":[{"slug":"blog/dry-run-preview","title":"Dry-run: preview a write before it happens","tags":["blog","feature","safety"],"body":"\n# Dry-run: preview a write before it happens\n\nWrite tools accept `dryRun: true`. The preview says it was not executed. When the tool has a GraphQL document, the preview includes it. A dry-run of `publish_product_to_channel` reads the current product and publication from Shopify and does not publish. A dry-run of `github_create_theme_pr` does not call GitHub and does not open a pull request. A dry-run of `upload_product_media` does not call Shopify and does not upload. On the Free plan a curated preview is allowed and the write is not run. Custom-tool previews require Pro.\n\n## Why it matters\n\nAgents are great at intent, imperfect at details. Dry-run catches the gap:\n\n```json\n{\n  \"tool\": \"create_discount\",\n  \"dryRun\": true,\n  \"input\": { \"title\": \"WEEKEND20\", \"type\": \"code\", \"valueType\": \"percentage\", \"value\": 20 }\n}\n```\n\nThe response shows the discount document it would create, so the agent (or you) can verify before committing.\n\n## Dry-run + confirmation gate\n\nThe two features stack:\n\n1. Agent calls the tool with `dryRun: true` and sees the preview. The write is not sent.\n2. Agent calls it for real → if that tool's confirmation setting is on, or the action is one of the four that always pause, Shopigent returns `confirmation_pending`.\n3. You approve in your AI client or on the Confirmations page. The mutation runs and is stored in the **audit log**.\n\nTools with confirmation turned off skip step 2. `create_discount` is one of those by default.\n"},{"slug":"blog/feature-read-tools-content","title":"Manage collections, blogs, articles, and pages — now with read tools","tags":["blog","feature","content","collections"],"body":"\n# Manage collections, blogs, articles, and pages — now with read tools\n\nShopigent has always supported **write tools** for collections, blogs, articles, and pages (create, update, delete). What was missing was the ability to *explore* them first — to list what exists, inspect a specific item, and decide what to act on.\n\nNow every content category has both **read and write** tools.\n\n## New read tools\n\n### Collections (5 tools)\n\nCollections organize your products. The new read tools let you browse and inspect them:\n\n```\nList all my collections and show me which ones have\nthe most products.\n```\n\n```\nGet the \"Winter Sale\" collection — I need to see\nits current products and description.\n```\n\n| Tool | What it does |\n|------|-------------|\n| `list_collections` | Lists all collections (manual + automated) with id, title, handle, sort order |\n| `get_collection_by_id` | Fetches a single collection by GID, including description, image, product count |\n\n### Blogs (5 tools)\n\nBlogs hold your articles. Read tools let you discover them:\n\n```\nList all my blogs and show me which ones have the\nmost recent articles.\n```\n\n| Tool | What it does |\n|------|-------------|\n| `list_blogs` | Lists all blogs with id, title, handle, comment policy |\n| `get_blog_by_id` | Fetches a single blog by GID, including its articles and metadata |\n\n### Articles (5 tools)\n\nArticles are individual blog posts. Now you can read them:\n\n```\nList all articles in the \"News\" blog, ordered by date.\n```\n\n```\nGet the article about the summer sale — I need to\ncheck its publish date.\n```\n\n| Tool | What it does |\n|------|-------------|\n| `list_articles` | Lists all blog articles for a blog, with optional filters |\n| `get_article_by_id` | Fetches a single article by GID, including body, summary, tags, author |\n\n### Pages (5 tools)\n\nPages are static content (About, Contact, etc.):\n\n```\nList all my pages and tell me which ones are unpublished.\n```\n\n```\nGet the \"About Us\" page and show me its current content.\n```\n\n| Tool | What it does |\n|------|-------------|\n| `list_pages` | Lists all pages with id, title, handle, and status |\n| `get_page_by_id` | Fetches a single page by GID, including body and SEO metadata |\n\n## Why this matters\n\nBefore these read tools, an agent could only *write* content — but couldn't discover what exists. The workflow was:\n\n**Before:** \"Delete the About page\" → \"I don't know which page that is.\"\n**After:** \"List my pages. Find 'About'. Delete it.\"\n\nIt's a small change that makes the agent dramatically more useful — especially for repetitive tasks like auditing content, cleaning up old blogs, or restructuring collections.\n\n## Full category breakdown\n\nThe current seeded merchant catalog is organized into **nine categories**:\n\n| Category | Tools |\n|----------|-------|\n| Products | 31 |\n| Content | 16 |\n| Metaobjects & Custom Data | 15 |\n| Theme Pull Requests | 4 |\n| Orders | 6 |\n| Customers | 5 |\n| Discounts | 4 |\n| Fulfillments | 4 |\n| Inventory | 2 |\n\n**Total: 87 distinct seeded merchant tools** across nine categories. Runtime\nconnection identity and capability utilities are excluded from this count.\nTheme work is GitHub-only: Shopigent reads explicit repository files and creates\na confirmed pull request; the merchant merges it, then Shopify's native GitHub\nintegration synchronizes the connected branch to the merchant-selected theme.\nShopigent never merges, deploys, publishes, or directly edits Shopify theme code."},{"slug":"blog/feature-search-products","title":"Find any product instantly with full-text search","tags":["blog","feature","products"],"body":"\n# Find any product instantly with full-text search\n\nShopigent's `search_products` tool gives your AI agent the ability to search across your entire product catalog by keyword — just like the Shopify admin search, but accessible from any MCP client.\n\n## What it is\n\n`search_products` is a new read tool that performs full-text search across product titles, descriptions, and tags. It returns matching products with relevance scores, so the most relevant results appear first.\n\n```\nSearch my store for \"wool beanie\" — show me the top 5 matches.\n```\n\n## Why it's different from list_products\n\n`list_products` returns products with optional filters (status, collection, etc.). `search_products` is built for *discovery* — when you don't know exactly what you're looking for but have a keyword in mind.\n\n| Tool | Best for |\n|------|----------|\n| `list_products` | Browsing a category, paginated listing, status filters |\n| `search_products` | Keyword search, finding a specific product by name or description |\n\n## Example prompts\n\n### Find products by material\n\n```\nSearch for products containing \"organic cotton\" in their description.\nList the top 3 with their prices and inventory.\n```\n\n### Find products by tag or keyword\n\n```\nSearch for \"clearance\" across all product names and tags.\nShow me how many are still in stock.\n```\n\n### Product research\n\n```\nSearch for \"running\" in my store and tell me which products\nneed better meta descriptions based on their search ranking.\n```\n\n### Compare with list_products\n\n```\nSearch for \"winter\" products. Then for each result, check\nif the inventory is below 10 and add it to a restock list.\n```\n\n## How it works with the tool grid\n\n`search_products` is part of the **Products** category (15 tools). Enable it in Curated Tools alongside `list_products`, `get_product_by_id`, and the product write tools.\n\nAvailable on all plans (Free, Growth, Pro)."},{"slug":"blog/shopigent-vs-raw-api-key","title":"Shopigent vs a raw Admin API key: why curated tools win","tags":["blog","comparison","security"],"body":"\n# Shopigent vs a raw Admin API key\n\nTechnically, you don't need Shopigent to connect an LLM to Shopify. You can create a custom app, grab an Admin API token, and hand it to your agent. Merchants ask us why they shouldn't. Fair question — here's the honest comparison.\n\n## What a raw token gives you\n\n- **Everything, forever.** Whatever scopes the token has, the agent has — 24/7, no questions asked.\n- **No record.** Shopify logs API calls internally, but *you* have no readable trail of what your agent did or why.\n- **No brakes.** A hallucinated ID in a `productDelete` mutation executes just as happily as a correct one.\n- **Raw GraphQL complexity.** The agent burns tokens (and makes mistakes) constructing mutations, cursors, and error handling from scratch.\n\n## What curated tools change\n\n| | Raw Admin token | Shopigent |\n|---|---|---|\n| Access control | All-or-nothing scopes | Per-tool and per-area switches. One connection key per store |\n| Destructive actions | Execute immediately | Pause when that tool's confirmation is on. Four actions always pause |\n| Visibility | None | Audit log (input, result, latency), with a redacted detail view |\n| Agent ergonomics | Raw GraphQL | 87 curated merchant tools |\n| Failure mode | The mutation runs | Invalid input is rejected. A gated write pauses. A write with confirmation off can still run |\n| Revocation | Rotate the token and redeploy | Regenerate connection key, or uninstall |\n\n## The deeper point\n\nAgents don't fail like humans. They fail *confidently*, at machine speed, in bulk. The right response isn't \"never connect an agent\" — it's **structure the access so mistakes are cheap**. Validated inputs catch many errors. A confirmation setting catches the writes you leave gated. It does not pause every write. The audit log records the call either way.\n\nThat's the entire product, honestly. Fewer capabilities exposed, with receipts.\n\n*On Free, reads run and a curated write preview is not executed. Custom tools are not included.*\n"},{"slug":"blog/use-case-bulk-edit","title":"Use case: bulk editing products from a spreadsheet","tags":["blog","use-case","products"],"body":"\n# Use case: bulk editing products from a spreadsheet\n\nShopify's bulk editor is powerful but limited to what it shows in the grid. With Shopigent + an MCP agent, you can update any field on any product — based on a spreadsheet you already have.\n\n## The workflow\n\n**Step 1:** Export a CSV from Shopify (or prepare one in Google Sheets).\n\n**Step 2:** Feed it to your agent with clear instructions.\n\n**Step 3:** The agent reads each row, matches the product in Shopify, and applies the change. Ask it to call the write with `dryRun: true` first; that preview is not executed.\n\n## Example: price update from a CSV\n\n```\nI have a file called prices.csv with columns: SKU, new_price.\n\nFor each row, find the matching product in Shopify by SKU. \nDry-run updating the price to new_price. Report back any SKUs \nthat don't match before you execute.\n```\n\n## Example: bulk tag addition\n\n```\nAdd the tag \"winter-2026\" to every product in the \"Winter Collection\" \ncollection. Show me how many products will be affected, then dry-run\nbefore executing.\n```\n\n## Example: SEO meta update\n\n```\nRead meta-descriptions.csv (columns: title, new_meta). For each row, \nfind the product by title and update its seo.description to new_meta. \nSkip any products that already have the same meta.\n```\n\n## Example: multi-lingual translation\n\n```\nExport all products. For products where the Spanish description \nis missing, translate the English description using the product's \ntags as context. Dry-run the first 5 translations for review.\n```\n\n## Tips for spreadsheet operations\n\n### Always use SKU or GID\nProduct titles change. Use the **SKU** or the Shopify **GID** (`gid://shopify/Product/123`) to match rows reliably.\n\n### Dry-run first\nAdd `dryRun: true` to every bulk update. The agent will preview every mutation without touching your store.\n\n### Batch by category\nSplit large spreadsheets into batches (e.g. 20 products at a time) to review before committing.\n\n```\nProcess the first 20 rows and stop for review. If the output \nlooks right, I'll tell you to continue with the next 20.\n```\n\n### Use filters to narrow scope\n\n```\nList products in \"Electronics\" with inventory > 10. Export their \nSKUs and current prices, then reduce each price by 15%.\n```\n\nThis avoids updating products that shouldn't change.\n\n## Why this works\n\nShopigent's `update_product` tool can change title, description, status, vendor, product type, tags, SEO title and description, URL handle, and template suffix. It does not set price, compare-at price, or metafields. Price changes use `update_product_variant_price` or `bulk_update_prices`. Metafields use `set_product_metafields`. A dry-run previews the call and does not write."},{"slug":"blog/use-case-bulk-seo","title":"Use case: bulk-editing product SEO with an agent","tags":["blog","use-case","products"],"body":"\n# Use case: bulk-editing product SEO with an agent\n\nProduct SEO is the most common \"should do, never does\" task. There are hundreds of products and writing unique descriptions by hand is brutal. An agent changes the math.\n\n## The prompt\n\n```\nFor products missing a meta description, write a 150-character meta description\nand a compelling product description using the title and vendor. Don't change\nprices or anything else. Show me a preview first.\n```\n\n## How it stays safe\n\n- The agent uses `list_products` (read) to find gaps — no writes yet.\n- For each product it drafts text, then calls `update_product` with `dryRun: true` so you see the before/after. A dry-run does not write.\n- `update_product` has confirmation off by default, so the later live call runs unless you turn confirmation on for that tool. You can also narrow the prompt, for example to one collection.\n\n## Why not a bulk editor app?\n\nBulk editors apply a template. An agent can write per-item copy. The confirmation setting, when you turn it on for `update_product`, pauses that live call. It is not on for this tool by default, and it does not approve a whole batch as one action.\n\n> Tip: on Free you can read and dry-run this write. The preview is not executed and it counts toward 25 tool calls per day. A live write needs Growth or Pro.\n"},{"slug":"blog/use-case-customer-support","title":"Use case: answering customer emails with an agent","tags":["blog","use-case","customers"],"body":"\n# Use case: answering customer emails with an agent\n\nSupport emails pile up fast — \"Where's my order?\", \"Can I change my address?\", \"Do you have this in a different size?\" — and every answer means digging through the Shopify admin.\n\nWith Shopigent, your AI agent reads the store data and drafts replies in seconds.\n\n## The workflow\n\n**Step 1:** A customer emails you.\n\n**Step 2:** Your agent searches for the customer and their orders in Shopify — without you leaving your email client or chat.\n\n```\nFind customer by email \"maria@example.com\" and show their \nlast 3 orders with status and fulfillment info.\n```\n\n**Step 3:** The agent drafts a reply based on live data.\n\n**Step 4:** If the reply involves a store change (refund, address update), the agent prepares it for your approval via the confirmation gate.\n\n## Example prompts\n\n### Check an order status\n\n```\nA customer named \"Elena\" emailed asking about order #1234. \nFind the order status, tracking number, and estimated delivery.\n```\n\n### Update a shipping address\n\n```\nCustomer \"Carlos\" needs his shipping address changed on order #5678 — \nhe moved to 42 New Street, Berlin. Dry-run the update first.\n```\n\n### Process a simple refund\n\n```\nOrder #9012 was returned. Refund the full amount to the original \npayment method. Use dryRun first to preview the refund mutation.\n```\n\n### Draft a support reply\n\n```\nLook up customer \"Sofia\" — her email is sofia@example.com. She has 14 \norders and her last one (#3456) shows \"delivered\" but she says it hasn't \narrived. What's the tracking status? Draft a reply offering options.\n```\n\n## Why this works\n\nShopigent's tools cover everything support needs:\n- **`get_customer`** → fetch contact details, tags, notes\n- **`list_orders`** → find recent orders by customer\n- **`get_order`** → check status, fulfillment, tracking\n- **`update_customer`** → update name, email, phone, note, tags, locale, or tax exemption. Confirmation is on by default. Tags replace the stored list.\n- **`refund_order`** → process returns (with confirmation gate)\n\nAnd the audit log stores each tool call — useful for tracking which tickets were handled through the agent. The page shows the tool, status, time, latency, and client."},{"slug":"blog/use-case-inventory-sync","title":"Use case: inventory sync after a stocktake","tags":["blog","use-case","inventory"],"body":"\n# Use case: inventory sync after a stocktake\n\nAfter a physical count, you often need to reconcile Shopify stock with what's on the shelves. Doing it by hand in the admin is slow and error-prone. Here's the agent-assisted version.\n\n## The prompt\n\n```\nFrom the attached stock count (SKU → quantity), adjust each variant's available\ninventory to match. Use `update_inventory_quantity` with `dryRun: true` first and report what will change.\n```\n\n## What happens\n\n1. The agent matches each SKU to a variant via `list_products` (read) — catching typos before anything changes.\n2. For each mismatch it calls `update_inventory_quantity` with `dryRun: true`, returning a preview of the quantity change. Nothing is written.\n3. You review that preview. This tool's confirmation setting is off by default, so the live call runs unless you turn confirmation on for it in Curated Tools.\n4. `bulk_update_inventory` sets available quantities for a list of items in one call. Its dry-run returns one would-change diff and does not write. Its confirmation setting is on by default. On Free the live write is refused. If the optional inventory scope is not granted, the preview says so and the live call changes nothing.\n\n## The audit trail\n\nEvery adjustment is stored with its input, result, and time. The Audit Log page shows the tool, status, time, latency, and client.\n\n> Particularly useful for stores with multiple locations: the agent can target a specific location per SKU without you hunting through dropdown menus.\n"},{"slug":"blog/use-case-weekend-sale","title":"Use case: set up a weekend sale in two minutes","tags":["blog","use-case","discounts"],"body":"\n# Use case: set up a weekend sale in two minutes\n\nThe workflow merchants automate first is almost always discounts. It's fiddly in the admin (collection, value, dates, limits, code) and it's exactly the kind of structured task agents nail. Here's the full flow with Shopigent.\n\n## The prompt\n\nIn Claude, Cursor, or Hermes:\n\n```\nCreate a 20% discount code WEEKEND20 for the Hoodies collection,\nvalid Friday 00:00 to Sunday 23:59, one use per customer.\n```\n\n## What the agent does\n\n1. `create_discount` with `dryRun: true` — previews the discount document it would create. Nothing is written.\n\n> Worth knowing before you write the prompt: a discount cannot be limited to a\n> collection through this app. `create_discount` builds a code with a percentage or a\n> fixed amount; collection-scoped pricing is an admin job. Ask for the code and apply\n> the collection limit yourself, or leave the code open to everything.\n3. `create_discount` for real — and it lands in the audit log.\n\n`create_discount` ships with its confirmation setting **off**, so this one goes straight\nthrough. Turn confirmation on for it in Curated Tools and the same call returns\n`confirmation_pending` until you approve it in the AI client, or on **Confirmations** with **Approve** or **Decline**.\n\n## What you see when the gate is on\n\nYour AI client gets the confirmation request:\n\n> **create_discount** — WEEKEND20 · 20% · one per customer\n\nYou can approve in the client, or open **Confirmations** and choose **Approve** or **Decline**. Approve runs the tool only if the server check passes. Decline does not run it. The page does not show the token. This is not a dialog on every write.\n\n## Variations that work just as well\n\n- \"How is WEEKEND20 doing?\" → `get_discount` (editing an existing discount is not a capability)\n- \"How did WEEKEND20 perform?\" → `list_orders` gets you the orders, but it returns no discount attribution — that lives in your Shopify reports, not through this app\n- \"Kill the sale now\" → `delete_discount` (confirmation again — deletes are gated by default, and you can turn that off per tool)\n\n## Why this beats doing it by hand\n\nNot because clicking is hard — because the agent fills in the code, dates, and limits, and the audit log stores the call. If you turn confirmation on for `create_discount`, you review that one paused call before it runs.\n"},{"slug":"blog/why-ai-agents-for-shopify","title":"Why your Shopify store needs an AI agent (and how to stay in control)","tags":["blog","ai-agents","mcp"],"body":"\n# Why your Shopify store needs an AI agent (and how to stay in control)\n\nAI agents are past the demo phase. They can genuinely run store operations: updating product copy, building discounts, answering \"what were my top sellers last month\", adjusting inventory after a stocktake. The blocker was never intelligence — it was **safe access**.\n\n## The problem with raw API access\n\nGive an agent your Admin API key and you've given it everything, forever, with no record. One hallucinated `DELETE` and your catalog is gone. That's why most merchants (rightly) never connect an LLM to their store.\n\n## Scoped tools + human gate\n\nShopigent takes a different route:\n\n1. **You pick the categories** an agent can touch — Products yes, Orders no, for example.\n2. **Reads run. Writes follow a gate.** Destructive tools pause by default. You can turn that off, except for four actions that always pause: publishing to a sales channel, changing a variant's shipping weight, uploading product media, and creating a theme pull request. Other writes follow that tool's confirmation setting.\n3. **Tool calls are logged.** Each call's input, result, and latency are stored. The Audit Log page shows the tool, status, time, latency, and client.\n\n> When a tool's confirmation is on, or the action is one of the four that always pause, the agent proposes and you approve in the AI client or on Confirmations.\n\n## What merchants actually automate first\n\nFrom what we see, the first wins are boring and valuable:\n\n- Bulk product copy cleanup and SEO descriptions\n- Seasonal discount setup (\"20% off everything in Collection X this weekend\")\n- Answering ops questions without opening five admin tabs\n- Tagging and segmenting customers after imports\n\nOn Free, use reads and dry-run previews, then use Growth or Pro when you want a live write. The audit log records those calls. Turning a category on does not turn confirmation on for every write in it.\n"},{"slug":"changelog","title":"Changelog","tags":["changelog"],"body":"\n# Changelog\n\n## Current catalog\nThe seeded catalog is **87 distinct seeded merchant tools**, including `bulk_update_inventory`. The 2026-09-24 entry records 86 tools as of that change. `list_themes` and the other direct Shopify theme tools are retired, and the server rejects them. `publish_product_to_channel` is a live tool. A dry-run is optional, does not publish, and is not required before a live call. A live call always requires confirmation. `github_create_theme_pr` is the same: a dry-run previews and does not open a pull request, and a live call requires confirmation. Shopigent never merges, deploys, or publishes theme code.\n\n## 2026-10-05\n- **Files scope pair made explicit.** `upload_product_media` needs **three**\n  prerequisites verified against the live OAuth grant: `write_products`,\n  `read_files` and `write_files`. `read_files` is now declared as a required\n  scope alongside `write_files` rather than inferred from the write scope, and\n  the runtime scope set on the deployed app was corrected — it previously\n  omitted the file scopes entirely, which is why an already-installed store\n  could still hit `MISSING_SCOPE` on media upload after reauthorizing. This\n  supersedes the single-scope wording in the SH-017 entry below.\n\n## 2026-09-29\n- **SH-017 — file-scope authorization for media upload.** `upload_product_media`\n  now requires a verified live OAuth grant containing both `write_products` and\n  `write_files`. The server fails closed before decoding payloads or issuing\n  confirmation tokens when a prerequisite scope is missing. Error normalization\n  now reports exact scope handles (`missingScopes[]`) instead of the vague\n  `\"requiredScope\"` field, and handles the case where Shopify does not disclose\n  the scope (`null` with `knownPrerequisites[]`). The confirmation response\n  carries `resendRequired: \"full_identical_payload\"` — a token alone does not\n  resume after expiry.\n- `write_files` is declared as a **required** scope. The embedded app surfaces\n  four distinct states: tool enabled, scope requested, scope granted (verified),\n  and executable. An enabled toggle is never proof of Shopify authorization.\n  The Scopes & Permissions page now includes a **Files / Media upload** category\n  with the exact scope handle shown in the missing-scopes banner when the store\n  has not yet granted it.\n- The media upload workflow now emits structured observability logs at every\n  pipeline stage with secret-safe rules — no tokens, headers, confirmation\n  tokens, or base64 payloads are logged.\n\n## 2026-09-24\n- **SH-015 publication workflow — as recorded on this date, implementation prepared, not deployed.** Added the catalog contracts for `list_publications` and `publish_product_to_channel`, bringing the deduplicated seed catalog to **86 distinct merchant tools across nine categories**. Runtime-only connection identity and capability utilities remain excluded from this count.\n- `write_publications` is declared as an optional permission. Merchants explicitly enable or revoke it from the embedded **Scopes & Permissions** page; all other tools continue to work without it. Declaring the scope does not grant it, and no live merchant grant was performed as part of this change.\n- Publication targets come from `list_publications` and are selected explicitly by ID. A live publish requires verified `writeProducts` and `writePublications` capabilities and non-disableable human confirmation. A dry-run is optional and does not publish. The live workflow includes idempotency protection, a final readback, and compensation that does not remove a publication edge that was already present.\n- As recorded on 2026-09-24, this entry described a pending implementation: no app-version release, deployment, production data change, or live product publication was claimed then, and SH-015 was not yet resolved. See the current-catalog note above.\n- **Theme Pull Requests replace direct Shopify theme access.** The four `github_*` tools read explicit repository files. A live call creates a branch, commits, and a pull request only after human confirmation. A dry-run is optional and does not open the pull request. Shopigent never merges, deploys, publishes, or directly edits Shopify theme code through the Admin Theme API.\n- After the merchant reviews and merges, Shopify's native GitHub theme integration synchronizes the connected branch to the merchant-selected theme. Reviewer demonstrations use an unpublished connected theme so the live storefront remains unchanged.\n\n## 2026-09-23\n- Added `update_product_variant_weight`, a focused weight-only mutation for product variants.\n- The tool supports grams, kilograms, ounces, and pounds; validates product/variant IDs and weight values before Shopify is called; and always requires an args-bound human confirmation token.\n- `search_products` and `get_product_by_id` now return each variant's current shipping weight for safe readback and verification.\n\n## 2026-09-03\n- Added the four-tool repository workflow now named **Theme Pull Requests**: `github_theme_connection`, `github_list_theme_branches`, `github_theme_file`, and `github_create_theme_pr`.\n\n## 2026-09-03 (earlier)\n- **fix(object-arg payloads)** — object-payload tools (`create/update_metafield_definition`, `create/update_metaobject_definition`, `create/update_metaobject`) now send the full payload to Shopify (previously sent `{}` due to Zod STRIP-mode). (PR #5)\n- **fix(scopes)** — the **Metaobjects & Custom Data** category now shows \"Granted\" after reauthorize (added to scope-granted sync). (PR #6)\n- **fix(MCP metafield-read + HTTP)**:\n  - `list_product_metafield_definitions` & `get_metafield_definition` — correct nested `type { name }` selections (previously GraphQL error).\n  - `get_metafield_definition` accepts only `id` (no longer sends ownerType).\n  - HTTP JSON body limit raised **100KB → 50MB** — media uploads (`upload_product_media`) with large base64 now work (previously HTTP 413). Plus structured JSON errors instead of HTML. (PR #7)\n- **UI polish** — uniform scopes badges + aligned; dashboard quick-stats in one row; responsive mobile pages; tool cards with clear Enable/confirmation.\n\n## 2026-09-03 (earlier)\n- Added the **Metaobjects & Custom Data** category with 15 tools.\n- **Product media (SH-002, SH-007)** — new `upload_product_media` (staged-upload), `get_product_media`, and `get_product_publications`.\n- **Custom Data (SH-003)** — 15 metaobject & metafield-definition tools. Requires a dedicated **Custom Data** scope category; many are destructive and require confirmation.\n- **Template suffix (SH-004)** — `update_product` now supports `templateSuffix` / `clearTemplateSuffix`.\n- **Customers** — `create_customer` added (Customers now 5 tools).\n- **API version 2026-07**, new `read_publications` scope + `metaobjects` scopes.\n\n## 2026-09-01\n- Added read tools for collections, blogs, articles, and pages. Inventory was merged into Products.\n- **`search_products`** — new full-text search tool that finds products by keyword across titles, descriptions, and tags with relevance scoring.\n- **Content read tools** — `list_pages`, `get_page_by_id`, `list_blogs`, `get_blog_by_id`, `list_articles`, `get_article_by_id` make every content category fully readable.\n- **Collection read tools** — `list_collections`, `get_collection_by_id` let you browse collections before writing.\n- **Hero terminal slider** — homepage demo now cycles through 5 interactive prompt examples with prev/next controls and clickable dots.\n\n## 2026-07-20\n- **Connect guides** — added setup guides for Claude, Cursor, and Hermes agents.\n- **Plans & Billing doc** — clarified plan limits and upgrade flow.\n\n## 2026-07-12\n- **Security & Trust doc** — published our access model, key handling, and data practices.\n- **Scopes reference** — mapped every tool category to its Shopify OAuth scopes.\n\n## 2026-06-30\n- **Confirmation gate** ships to all paid plans — confirmation-gated actions are approved in the merchant's AI client.\n- **Audit log** — records tool name, input, result, and latency for every call.\n\n## 2026-06-15\n- **Dry-run preview** — write tools support `dryRun: true` to see what they would send before they send it. The preview is not executed.\n\n## 2026-05-01\n- **Public launch** — Shopigent launched with scope-based access control.\n"},{"slug":"guides/connect-chatgpt","title":"Connect ChatGPT to your store","tags":["guide","chatgpt","mcp","openai"],"body":"\n# Connect ChatGPT to your store\n\nChatGPT (GPT-4o and newer) supports MCP servers natively through **ChatGPT MCP** — giving you a conversational interface to your store data.\n\n> **Prerequisite:** You've installed Shopigent and generated a connection key on the dashboard. The button is **Generate connection key**, or **Regenerate connection key** when one already exists. See [Getting Started](/guides/getting-started).\n\n## 1. Configure the MCP server\n\nOpen **ChatGPT Settings → MCP Servers → Add** and paste:\n\n```json\n{\n  \"mcpServers\": {\n    \"shopigent\": {\n      \"url\": \"https://mcp.shopigent.greeknous.com/mcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer YOUR_API_KEY\"\n      }\n    }\n  }\n}\n```\n\nYou can also configure it via `~/.chatgpt/mcp.json` if you use ChatGPT Desktop.\n\n## 2. Ask your first query\n\n```\nShow me my top 10 products by revenue this month.\n```\n\nChatGPT uses `list_orders` and `list_products` through MCP and answers with live numbers.\n\n## 3. Go further with prompts\n\n```\nI want to create a discount for customers who spent over €200 last year.\nFirst find those customers, then suggest a discount structure.\n```\n\n```\nMy \"Trail Beanie\" has inventory 34 units. Check the last 90 days of sales \nfor that product and tell me if 34 units is enough or if I should reorder.\n```\n\n## 4. Combine with ChatGPT plugins\n\nPair Shopigent with ChatGPT's data analysis (code interpreter) to export store data and build reports — all from the same chat.\n\n## Troubleshooting\n\n- **MCP not showing** — ensure ChatGPT Desktop is updated to a version that supports MCP (GPT-4o, 2025+ releases).\n- **Key rejected** — the key is one-time. Generate a fresh one from the Shopigent dashboard.\n- **Scope denied** — the category you're querying isn't enabled in Curated Tools."},{"slug":"guides/connect-claude","title":"Connect Claude to your store","tags":["guide","claude","mcp"],"body":"\n# Connect Claude to your store\n\nThis guide shows how to connect Claude (Desktop or Code) to Shopigent's MCP endpoint.\n\n> **Prerequisite:** You've installed Shopigent and generated a connection key on the dashboard. The button is **Generate connection key**, or **Regenerate connection key** when one already exists. See [Getting Started](/guides/getting-started).\n\n## 1. Add the MCP server\n\nAdd Shopigent to your Claude MCP configuration (`claude_desktop_config.json` or `~/.claude/mcp.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"shopigent\": {\n      \"url\": \"https://mcp.shopigent.greeknous.com/mcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer YOUR_API_KEY\"\n      }\n    }\n  }\n}\n```\n\n## 2. Verify the connection\n\nAsk Claude something read-only first:\n\n```\nList my 5 most recent orders with customer name and total.\n```\n\nClaude should call `list_orders` and return real data from your store.\n\n## 3. Example prompts for Claude\n\n### Explore your store\n\n```\nDescribe my store: how many products, customers, orders, and active discounts? \nUse the appropriate tools one at a time.\n```\n\n```\nShow me a dashboard: my top 3 products by revenue this month, my 3 lowest-stock \nitems, and how many pending orders I have — all in one response.\n```\n\n### Merchandising\n\n```\nFind 5 products with no meta description. For each one, draft a compelling \nmeta description based on its title and tags. Show me the drafts, then \nupdate them if I approve.\n```\n\n```\nWhich products in my store are tagged \"clearance\" but still at full price? \nList them with options to mark them down.\n```\n\n### Customer insights\n\n```\nWho are my top 5 customers by total spend? Show name, email, order count, \nand total spent. Don't change anything — just report.\n```\n\n```\nI want to send a birthday promo. List customers with birthdays this month, \ntheir email, and their favourite product category based on purchase history.\n```\n\n## 4. Try a write action (Growth/Pro)\n\n```\nCreate a 10% discount code SUMMER10 valid until end of month for all products.\n```\n\n`create_discount` ships with confirmation off, so this example can run as soon as the Discounts category is enabled on a paid plan. Turn on **Require confirmation** for that tool if you want your AI client to pause first. Approval happens in the AI client, or on the Confirmations page in the Shopigent admin. That page lists the stored arguments with secrets and contact-shaped values redacted, and it does not show the token. Then check the audit log.\n\n### Bulk operations (always dry-run first)\n\n```\nDry-run: set the price of all products tagged \"sale\" to 20% off their current \nprice. Show me the before/after for 3 examples before executing.\n```\n\n## Troubleshooting\n\n- **401 Unauthorized** — the API key is wrong or was revoked. Issue a new one from the dashboard.\n- **Tool not available** — the category (e.g. Discounts) is disabled in your Curated Tools settings, or your plan doesn't include write access.\n- **Action blocked** — a pending confirmation is waiting in your AI client. You can also approve or decline it on the Confirmations page in the Shopigent admin. That page does not show the token. A product media upload cannot be approved there because the file payload is not stored."},{"slug":"guides/connect-claude-code","title":"Connect Claude Code to your store","tags":["guide","claude-code","claude","mcp"],"body":"\n# Connect Claude Code to your store\n\n[Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) is Anthropic's terminal-based agent. Combined with Shopigent, you can execute store operations — reads, writes, bulk edits — from the same terminal where you code.\n\n> **Prerequisite:** You've installed Shopigent and generated a connection key on the dashboard. The button is **Generate connection key**, or **Regenerate connection key** when one already exists. See [Getting Started](/guides/getting-started).\n\n## 1. Start Claude Code with the MCP config\n\nCreate `~/.claude/shopigent.json`:\n\n```json\n{\n  \"shopigent\": {\n    \"url\": \"https://mcp.shopigent.greeknous.com/mcp\",\n    \"headers\": {\n      \"Authorization\": \"Bearer YOUR_API_KEY\"\n    }\n  }\n}\n```\n\nThen start Claude Code:\n\n```bash\nclaude --mcp ~/.claude/shopigent.json\n```\n\nOr add Shopigent to your project's `.claude/mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"shopigent\": {\n      \"url\": \"https://mcp.shopigent.greeknous.com/mcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer YOUR_API_KEY\"\n      }\n    }\n  }\n}\n```\n\n## 2. Terminal-native prompts\n\nClaude Code shines at batch operations. Try these:\n\n```\nRead store-data/products.json, then for every product in that file that \nis out of stock, update its status to DRAFT using shopigent.\n```\n\n```\nList all discounts that expire this week. For each one, calculate the \ntotal revenue it generated and create a report.\n```\n\n## 3. Developer workflows\n\n```\nRead \"templates/index.json\" from my connected theme repository. Propose the\nsmallest repository-file change as a dry-run only; do not open a PR yet.\n```\n\n```\nPull the last 50 orders and check if any need fulfillment \n(fulfillment status = null). Return them as a JSON array.\n```\n\n## 4. Script it\n\nBecause Claude Code runs in the terminal, you can pipe results into scripts:\n\n```bash\nclaude --mcp shopigent \"List all products with inventory < 5\" | \\\n  jq '.products[].title' | xargs -I{} echo \"Reorder: {}\"\n```\n\n## Troubleshooting\n\n- **CLI not found** — install Claude Code: `npm install -g @anthropic-ai/claude-code`\n- **Connection error** — verify the MCP URL and key. Keys from the dashboard work for 24 hours.\n- **Batch too large** — use `dryRun: true` to preview mutations before running them in bulk."},{"slug":"guides/connect-codex","title":"Connect Codex CLI to your store","tags":["guide","codex","mcp","openai"],"body":"\n# Connect Codex CLI to your store\n\n[OpenAI Codex CLI](https://codex.openai.com) is an agentic terminal that combines Claude/ChatGPT-like reasoning with a live shell. Add Shopigent's MCP tools and your store becomes accessible from any terminal session.\n\n> **Prerequisite:** You've installed Shopigent and generated a connection key on the dashboard. The button is **Generate connection key**, or **Regenerate connection key** when one already exists. See [Getting Started](/guides/getting-started).\n\n## 1. Configure Codex CLI\n\nCodex reads MCP servers from `~/.codex/mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"shopigent\": {\n      \"url\": \"https://mcp.shopigent.greeknous.com/mcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer YOUR_API_KEY\"\n      }\n    }\n  }\n}\n```\n\nStart Codex:\n\n```bash\ncodex\n```\n\n## 2. Prompts for Codex\n\nCodex is built for engineering workflows. Try:\n\n```\nUse shopigent to export all my product titles, prices, and inventory \ncounts to a CSV file called inventory-export.csv.\n```\n\n```\nFind every product whose description mentions \"cotton\" and replace \nit with \"organic cotton\". Preview with dryRun first.\n```\n\n## 3. Bulk operations\n\n```\nRun a dry-run discount: create a 15% off code called FALL15 for all \nproducts in the \"Fall Collection\" collection. If it looks right, \nconfirm it.\n```\n\n```\nList products where compareAtPrice < price (sale is inverted). \nFix each one.\n```\n\n## Troubleshooting\n\n- **Config not found** — ensure `~/.codex/mcp.json` has valid JSON.\n- **Tool calls return 401** — the key expired. Generate a new one.\n- **Rate-limited** — Free plan: 25 calls/day. Upgrade to Growth or Pro for higher limits."},{"slug":"guides/connect-continue","title":"Connect Continue.dev to your store","tags":["guide","continue","mcp","vscode"],"body":"\n# Connect Continue.dev to your store\n\n[Continue.dev](https://continue.dev) is an open-source AI code assistant that runs in VS Code and JetBrains. It supports MCP servers, so you can add Shopigent and manage your store without leaving the editor.\n\n> **Prerequisite:** You've installed Shopigent and generated a connection key on the dashboard. The button is **Generate connection key**, or **Regenerate connection key** when one already exists. See [Getting Started](/guides/getting-started).\n\n## 1. Add Shopigent to Continue\n\nEdit `~/.continue/config.json`:\n\n```json\n{\n  \"experimental\": {\n    \"mcpServers\": {\n      \"shopigent\": {\n        \"url\": \"https://mcp.shopigent.greeknous.com/mcp\",\n        \"headers\": {\n          \"Authorization\": \"Bearer YOUR_API_KEY\"\n        }\n      }\n    }\n  }\n}\n```\n\nRestart VS Code (or reload the Continue extension).\n\n## 2. Ask from your editor\n\nOpen Continue's chat panel (Ctrl+Shift+R / Cmd+Shift+R) and try:\n\n```\nUsing shopigent, what are my 3 best-selling products this month?\n```\n\n```\nCheck the inventory of product \"Approach Under Glove\" and tell me \nif I need to reorder.\n```\n\n## 3. Code + store data\n\nContinue's strength is combining code context with external data:\n\n```\nI'm building a product card component. Fetch 3 real products from \nmy store with shopigent, and show me the shape of the data I need.\n```\n\n```\nI need to add a \"Low Stock\" badge. Query my inventory and tell me \nwhich products have fewer than 10 units — I'll handle the front-end.\n```\n\n## Troubleshooting\n\n- **Tools not available** — ensure Continue version supports MCP (≥ v0.9.0).\n- **Key invalid** — regenerate from the Shopigent dashboard.\n- **Free plan** — reads run. A dry-run preview of a write is allowed and is never executed. Running the write needs Growth or Pro."},{"slug":"guides/connect-cursor","title":"Connect Cursor to your store","tags":["guide","cursor","mcp"],"body":"\n# Connect Cursor to your store\n\nCursor supports MCP servers natively, which means your editor's AI can read and manage your Shopify store through Shopigent while you code.\n\n> **Prerequisite:** You've installed Shopigent and generated a connection key on the dashboard. The button is **Generate connection key**, or **Regenerate connection key** when one already exists. See [Getting Started](/guides/getting-started).\n\n## 1. Add the MCP server\n\nOpen **Cursor Settings → MCP → Add new MCP server**, or edit `~/.cursor/mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"shopigent\": {\n      \"url\": \"https://mcp.shopigent.greeknous.com/mcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer YOUR_API_KEY\"\n      }\n    }\n  }\n}\n```\n\nRestart Cursor. The Shopigent tools appear in the MCP tools list.\n\n## 2. Verify\n\nIn a Cursor chat, ask:\n\n```\nUsing shopigent, list my products that are out of stock.\n```\n\n## 3. Prompts for Cursor developers\n\n### Development + store data\n\n```\nI'm building a product grid component. Use shopigent to fetch 6 real products \nfrom my store so I can preview how the grid renders with actual data.\n```\n\n```\nI need to test the checkout flow. Use shopigent to create a 1€ test product \nfor me, then delete it after I've finished testing.\n```\n\n### Bulk edits from code\n\n```\nRead the file ./products-to-update.json. For each SKU in the file, find \nthe matching product in Shopify and update its price to the new_price \nfrom the JSON. Use dryRun first.\n```\n\n### Theme development\n\n```\nRead \"sections/header.liquid\" from my connected theme repository on main.\nExplain where the header logo size is controlled. Do not write anything.\n```\n\n```\nI updated \"sections/header.liquid\" locally. Prepare a one-file Theme Pull\nRequest dry-run. Wait for my explicit approval before opening the PR.\n```\n\n## Ideas for developer workflows\n\n- Generate SEO descriptions in your editor and push them straight to products.\n- Pull real product data while building a storefront, instead of mocking it.\n- Propose explicit repository-file changes through a merchant-reviewed Theme Pull Request.\n- Use Cursor's `Cmd+K` to ask: \"Add a 10% student discount using shopigent\" — it calls the tools inline.\n\n## Troubleshooting\n\n- **Tools don't appear** — check the JSON syntax and restart Cursor completely.\n- **401 Unauthorized** — regenerate the API key in the Shopigent dashboard.\n- **Writes rejected** — a live write on Free is refused. A curated dry-run on Free is allowed and is not executed. Custom tools, including their preview, require Pro. A disabled scope category also stops the call."},{"slug":"guides/connect-hermes","title":"Connect Hermes Agent to your store","tags":["guide","hermes","mcp"],"body":"\n# Connect Hermes Agent to your store\n\nHermes Agent can operate your store conversationally — from Discord, Telegram, or the desktop app — through Shopigent's MCP endpoint.\n\n> **Prerequisite:** You've installed Shopigent and generated a connection key on the dashboard. The button is **Generate connection key**, or **Regenerate connection key** when one already exists. See [Getting Started](/guides/getting-started).\n\n## 1. Add the MCP server to Hermes\n\n```bash\nhermes mcp add shopigent https://mcp.shopigent.greeknous.com/mcp \\\n  --header \"Authorization: Bearer ***\"\n```\n\nOr add it to your Hermes `config.yaml`:\n\n```yaml\nmcp:\n  servers:\n    shopigent:\n      url: https://mcp.shopigent.greeknous.com/mcp\n      headers:\n        Authorization: \"Bearer YOUR_API_KEY\"\n```\n\n## 2. Talk to your store\n\nFrom any connected chat:\n\n```\nΠόσες παραγγελίες είχαμε σήμερα; Ποια προϊόντα πούλησαν περισσότερο;\n```\n\nHermes calls `list_orders` / `list_products` and answers with live data — in any language.\n\n## 3. Prompts for any language\n\n### Greek\n\n```\nΒρες τα προϊόντα που έχουν απόθεμα κάτω από 5 τεμάχια και φτιάξε \nμια λίστα για παραγγελία.\n```\n\n```\nΠόσα προϊόντα πωλήθηκαν αυτή την εβδομάδα; Σύγκρινε με την προηγούμενη.\n```\n\n### English\n\n```\nShow me all orders from last week grouped by day. Which day had the most revenue?\n```\n\n```\nCreate a discount called SPRING25 for 25% off on all products in the \n\"Spring Collection\" collection, valid for 7 days. Dry-run first.\n```\n\n## 4. Autonomous routines\n\nBecause Hermes supports scheduled jobs, you can combine it with Shopigent for recurring ops:\n\n- Daily sales summary delivered to Discord every morning\n- Low-stock alerts checked hourly\n- Weekly draft of a promo discount, waiting for your approval via the confirmation gate\n\nThe confirmation gate is key: an autonomous agent can prepare writes around the clock. Destructive tools pause by default. Publishing a product to a sales channel, changing a variant's shipping weight, uploading product media, and creating a theme pull request always pause. For every other write you can turn the gate off in Curated Tools if you trust the workflow.\n\n## Troubleshooting\n\n- **Connection fails** — verify the URL and that the key hasn't been revoked.\n- **Tool missing** — the category is toggled off in Curated Tools, or your plan doesn't include it.\n- **Scheduled job fails** — check Hermes' cron logs; the API key may have expired."},{"slug":"guides/getting-started","title":"Getting Started with Shopigent","tags":["guide","getting-started"],"body":"\n# Getting Started with Shopigent\n\nThis guide walks you through connecting an AI agent to your Shopify store with Shopigent.\n\n## 1. Install\nInstall Shopigent from the [Shopify App Store](https://apps.shopify.com/shopigent) and grant the scopes your store needs.\n\n## 2. Choose what the agent can run\nOn **Curated Tools**, each tool has an enable switch. **Enable all** and **Disable all** apply to the tools in the open category that you can toggle. On **Scopes & Permissions**, each area has its own on/off switch. Turning an area off stops tools in that area from running. It does not, by itself, remove an enabled tool from the MCP tool list.\n\nOptional: **Turn on read-only starter set**. It enables product and order read tools whose areas Shopify has granted. It does not turn write tools on. If a write tool in an area this turns on was already enabled, that write can run again because the area is on.\n\n## 3. Issue a connection key\nShopigent is the MCP server. The server address is `https://mcp.shopigent.greeknous.com/mcp`. On the dashboard, the button is **Generate connection key** when no key exists, and **Regenerate connection key** when one does. The key is shown once. Shopigent stores a SHA-256 hash of the key. The hash is not salted. Send the key as a Bearer token. Do not use `https://app.shopigent.greeknous.com/mcp`.\n\nA downloaded client config for Claude Desktop, Cursor, Hermes, or the generic file uses the placeholder `YOUR_API_KEY`. The download does not contain the key. Paste the key from the dashboard into the file.\n\n## 4. Connect your AI agent\n\nNow that you have an API key, choose your tool and follow the dedicated guide:\n\n- **[Claude Desktop](/guides/connect-claude)** — Anthropic's standalone app\n- **[Claude Code](/guides/connect-claude-code)** — Claude in your terminal\n- **[Cursor](/guides/connect-cursor)** — AI-native code editor\n- **[Hermes Agent](/guides/connect-hermes)** — Autonomous agent (Discord, Telegram)\n- **[ChatGPT](/guides/connect-chatgpt)** — OpenAI's MCP-capable chat\n- **[Codex CLI](/guides/connect-codex)** — OpenAI's terminal agent\n- **[Continue.dev](/guides/connect-continue)** — VS Code extension\n\nOr read the **[Prompts & Best Practices](/guides/prompts-best-practices)** guide for tips and examples.\n\n## 5. Confirmation, audit log, and dashboard checks\n\nDestructive actions default to requiring your approval. Publishing a product to a sales channel, changing a variant's shipping weight, uploading product media, and creating a theme pull request always pause and cannot be turned off. Other writes follow that tool's confirmation setting.\n\nA paused call returns `confirmation_pending` and a token. Approve by re-calling with `confirmed` true and that token, or, when the pause is stored, open **Confirmations** and choose **Approve** or **Decline**. Approve runs the tool only if the server check passes. Decline does not run the tool. The page does not show the token. A product media upload cannot be approved there because the file payload is not stored.\n\nThe **Audit Log** shows the tool, read or write, status, time, latency, and client. **View stored details** shows a redacted copy of the stored input and response summary. The view redacts secret- and token-like fields, names, addresses, and email- or phone-shaped values. Opening it does not change the database row.\n\n**Recent activity** on the dashboard counts successful writes, dry-runs, confirmations waiting, and confirmations declined for the last 24 hours and the last 7 days. A write in that card is a mutation stored as success. Dry-runs were not executed. **Setup check** is the install health check. It is read-only: it can report a missing connection key, an enabled tool whose scope category is not granted, or an enabled destructive tool with confirmation off. It does not change areas, confirmation settings, or the key. **Curated Tools** shows usage from the stored audit log for each tool: call count, last status, and last dry-run time. When none are stored, it says \"No calls stored.\"\n\n## Reauthorizing after an app update\n\nWhen Shopigent ships a new scope (e.g. the `read_files` and `write_files` pair from a recent update),\nalready-installed stores need to reauthorize the app before the new tool\nbecomes available. Here is the lifecycle:\n\n1. **Deploy.** The app update ships with the new scope declared in its\n   configuration.\n2. **Detect.** The Scopes & Permissions page shows the scope as\n   **Not yet granted** and lists the exact missing handle.\n3. **Grant.** Click **Grant missing permissions** — this redirects to the Shopify\n   install screen. Your store sees the exact new permissions requested.\n4. **Approve.** Shopify asks you to confirm the new permissions. Approve to\n   complete reauthorization.\n5. **Persist.** The app stores the updated grant and verifies it against a\n   live Shopify snapshot.\n6. **Refresh.** Capability caches are invalidated. The Curated Tools page\n   updates to **Media upload available** or **Media upload unavailable**.\n7. **Verify.** The media badge can show **Media upload available**. A live upload still requires confirmation. Retry the agent call with the same\n   full payload.\n\n> **Enabled vs. granted:** The Enabled checkbox on Curated Tools controls\n> whether that tool is offered to your AI agent. It does **not** add or remove\n> Shopify OAuth scopes. The Enabled checkbox on Scopes & Permissions is separate:\n> turning an area off stops tools in that area from running, and turning it back\n> on does not ask Shopify for permission again. A tool can be enabled and still\n> fail until the store has granted the underlying permission. The app shows both\n> states independently.\n\n## Next steps\n\n- Browse all [curated tools](/reference/tools) with descriptions\n- See [scopes & permissions](/reference/scopes-and-permissions) to enable only what your agent needs\n- Compare [plans and pricing](/guides/plans-and-billing)\n- Read the [Prompts & Best Practices](/guides/prompts-best-practices) guide\n"},{"slug":"guides/plans-and-billing","title":"Plans & Billing","tags":["guide","plans","billing"],"body":"\n# Plans & Billing\n\nShopigent bills through the [Shopify App Store](https://apps.shopify.com/shopigent). The Free plan does not require a card. Shopify Billing charges Growth and Pro. Shopigent does not collect a card of its own.\n\n## Plans at a glance\n\n| | Free | Growth | Pro |\n|---|---|---|---|\n| Price | $0 | $9.99/mo | $29/mo |\n| Read tools | All except translations | All except translations | ✅ All |\n| Write tools | Dry-run only | ✅ Core resources | ✅ Core resources |\n| Translations | — | — | ✅ |\n| Theme Pull Requests | Read tools; create is preview only | Live create after confirmation | Live create after confirmation |\n| Tool calls / day | 25 | 500 | No daily cap |\n| Trial | — | 7-day trial | 7-day trial |\n\n**Core resources** = Products, Orders, Customers, Discounts, Content, Inventory, Fulfillments, Custom Data, and Theme Pull Requests.\n\nThere is no Agency or multi-store plan. The plans are Free, Growth, and Pro.\n\n## What you get per plan\n\n### Free — $0\n- Reads across your store\n- 87 curated merchant tools across 9 categories\n- Up to 25 tool calls per day\n- Full audit log of each tool call\n- Dry-run preview of curated write tools (the preview is not executed)\n- Custom tools are not available, including their preview\n- Connect any MCP client (Claude, Cursor, Hermes)\n- Email support\n- The Free plan does not require a card. Shopify Billing charges Growth and Pro.\n\n### Growth — $9.99/mo (7-day trial)\n- Everything in Free, plus write tools\n- create, update and delete products; create, update and refund orders\n- create and update customers; create and delete discounts; create, update and delete collections\n- Up to 500 tool calls per day\n- Mandatory approval on publishing, variant shipping weight, media upload, and theme pull requests\n- create, update and delete blogs; create, update and delete pages; create and update articles\n- update inventory quantities; close and open fulfillment orders\n- Theme Pull Requests through a connected GitHub repository\n\n### Pro — $29/mo (7-day trial)\n- Everything in Growth\n- Translations (set/get/delete) for non-theme Shopify resources\n- No daily call cap (a fair-use limit of 60 calls per minute still applies)\n- Custom tools builder\n\n## Theme Pull Request boundary\n\nWhen the GitHub App is configured and this shop already has a stored installation, Theme Pull Requests can choose a repository that installation can access. Starting a new GitHub App connection on that page is unavailable. The page then says to use a repository-scoped personal access token. If the GitHub App is not configured, the page says the one-click flow is not set up and the token form remains. Shopigent reads explicit\nfiles. A dry-run returns a preview and does not open a pull request. A live\ncall requires confirmation, then creates only a branch, commits, and a pull\nrequest. It never merges, deploys, publishes, or directly\nedits Shopify theme code through the Admin Theme API. You review and merge;\nShopify's native GitHub theme integration synchronizes the connected branch to\nthe theme you selected. Use an unpublished connected theme for review so the\nlive storefront remains unchanged.\n\n## How limits work\n\n- A **tool call** is one MCP tool invocation — one `list_products` call, one `refund_order`, etc.\n- Limits reset at midnight UTC.\n- The call that returns `confirmation_pending` already counts. Approving it, from the MCP client or the Confirmations page, is another tool call and counts again. That call runs the write only if the server check passes. Declining on the Confirmations page does not run the tool and does not add another call.\n- `dryRun: true` calls count as normal calls. The argument name is `dryRun`, not `dry_run`. A dry-run does not execute the write. Custom-tool previews are not included on Free.\n- Every plan, including Pro, also has a fair-use limit of 60 calls per minute.\n\n## Upgrading & downgrading\n\n1. Open the Shopigent dashboard in your Shopify admin.\n2. Go to **Billing** and choose **Continue to Shopify**.\n3. Shopify's pricing page is where the subscription is confirmed. This app does not change the plan on that click. It stores the plan Shopify reports.\n\nShopigent does not schedule a downgrade for the end of the billing cycle, and it does not calculate proration. When Shopify reports a new plan, the admin shows that plan. Settings, the connection key, and the audit log are kept. Write tools are not executed on a plan that does not include them.\n\n## FAQ\n\n**Do unused calls roll over?** No, the daily allowance resets every day.\n\n**What happens if I hit my limit?** A daily cap returns an error that asks you to upgrade. A burst past 60 calls in a minute returns \"Rate limit exceeded\" and a retry delay. The per-minute limit applies on every plan, including Pro.\n\n**Is there a trial for paid plans?** Growth and Pro include a 7-day free trial. Shopify Billing handles the trial. The Free plan has no trial.\n"},{"slug":"guides/prompts-best-practices","title":"Prompts & Best Practices","tags":["guide","prompts","best-practices"],"body":"\n# Prompts & Best Practices\n\nGet the most out of Shopigent by using the right prompts, safety patterns, and workflows.\n\n## Key principles\n\n### 1. Dry-run before write\n\nEvery curated write tool accepts `dryRun: true`. Preview before a live call:\n\n```\nDry-run: update the price of \"Approach Under Glove\" to €39.99.\n```\n\nThe preview is not executed. When the tool has a GraphQL document, the preview includes that document. A dry-run does not require approval and does not run the write. A later call without `dryRun` runs the write when the plan allows it. That live call pauses only if the tool's confirmation setting is on, or the action is one of the four that always pause. `update_product_variant_price` has confirmation off by default. `bulk_update_prices` has confirmation on by default.\n\n### 2. Use categories explicitly\n\nShopigent has **nine seeded merchant-tool categories** (Products, Content, Metaobjects & Custom Data, Theme Pull Requests, Orders, Customers, Discounts, Fulfillments, Inventory). Mention the category for faster tool routing:\n\n```\nUse products tools: find all products with no description in Italian.\n```\nvs.\n```\nWhat products need Italian descriptions?\n```\n\n### 3. Chain operations, don't guess\n\nInstead of asking for everything at once, chain:\n\n```\n1. List my products tagged \"clearance\".\n2. For each, update the price to 40% off current.\n3. Show me the total discount value.\n```\n\nThe agent resolves each step separately. A write pauses only when that tool's confirmation setting is on, or the action is one of the four that always pause.\n\n### 4. Confirmation gate pattern\n\nConfirmation-gated writes (for example deletes, refunds, and Theme Pull Request creation) require **confirmed: true**. Use this pattern:\n\n```\nDelete product with ID gid://shopify/Product/12345.\n```\n\n→ Returns `confirmation_pending` with a token.\n\n```\nConfirm: yes, with the same token.\n```\n\n→ Executes the delete and logs it.\n\nAn agent can propose writes on its own. Destructive tools pause by default. Publishing, variant shipping weight, media upload, and theme pull request creation always pause. You can turn the gate off for every other write.\n\n## Prompts by role\n\n### Store owner\n\n```\nI'm going on holiday. Check my inventory: what needs reordering, \nare there any stuck fulfillments, and do I have active discounts \nthat expire while I'm away?\n```\n\n```\nList all products with 0 inventory and no reorder point set.\n```\n\n### Developer\n\n```\nI need to rename \"Trail Beanie\" to \"Trail Beanie 2.0\" across all \nlanguages. Dry-run a translation update for each language the product \nhas, then execute.\n```\n\n```\nRead \"config/settings_schema.json\" from my connected theme repository and\nreview it for schema issues. Do not write or open a PR.\n```\n\n### Marketing\n\n```\nDraft a 15% discount code for email subscribers. The code should \nbe single-use per customer, valid for 48 hours, and exclude sale items.\n```\n\n```\nFind products with less than 2 inventory — these are running out \nand need promotion before they're gone.\n```\n\n## Tool priorities by task\n\n| If you want to… | Use… |\n|---|---|\n| Discover what's in stock | `list_products` with inventory filters |\n| Edit a product | `update_product` (dry-run first) |\n| Check revenue | `list_orders` (date range filter) |\n| Run a promotion | `create_discount` |\n| Fix customer details | `get_customer` → `update_customer` |\n| Manage content | `create_article`, `update_page` |\n| Propose a theme repository change | `github_create_theme_pr` (Growth/Pro) |\n| Translate the store | `set_translations` (Pro) |\n\n## Rate limits by plan\n\n| Plan | Tool calls / day | Best for |\n|---|---|---|\n| Free | 25 | Reads, and curated write previews that are not executed. Live writes are refused. Custom tools are not included. |\n| Growth | 500 | Daily operations with writes |\n| Pro | No daily cap (60/min fair use) | Power use + custom tools + translations |\n\nA dry-run counts toward the daily allowance. It does not execute the write.\n\n## Pro tips\n\n1. **Name your operations** — agents follow steps better when you say \"Step 1… Step 2…\"\n2. **Use filters** — `list_products` with `query: \"status:ACTIVE\"` returns useful subsets\n3. **Check the audit log** — every call is recorded. Review what your agent did.\n4. **Least privilege scopes** — enable only the categories your agent needs\n5. **Trial the Pro plan** — unlock non-theme resource translations and remove the daily call cap. The 60-calls-per-minute fair-use limit still applies."},{"slug":"guides/security-and-trust","title":"Security & Trust","tags":["guide","security","trust"],"body":"\n# Security & Trust\n\nGiving an AI agent access to your store is a trust decision. Here is exactly how Shopigent limits, records, and controls that access.\n\n## The access model\n\n1. **You choose the categories.** Each of the nine merchant-tool categories has an on/off switch on Scopes & Permissions. Turning an area off stops tools in that area from running. An enabled tool can still appear in the MCP tool list. The call does not read or change store data for that area.\n2. **Reads and writes are separated by plan.** Free can run reads and can dry-run a write. The dry-run is not executed. Running a write requires Growth or Pro.\n3. **Confirmation-gated actions stop before execution.** When the tool's confirmation setting is on, or the action always pauses (sales-channel publishing, variant shipping weight, media upload, theme pull request creation), the tool returns a signed, single-use `confirmation_pending` result. You approve **in your AI client** and the agent re-calls the tool with the confirmation token bound to the same arguments. You can also approve or decline that pending token on the Confirmations page. Approve uses the same check as the client re-call. Decline uses the token and does not run the tool. The page does not show the token.\n4. **Tool calls are recorded.** The audit log stores the tool name, input, result, latency, and timestamp. **View stored details** shows a redacted copy. The view redacts secret- and token-like fields, names, addresses, and email- or phone-shaped values. Opening it does not change the database row. `customers/redact` can later redact matching stored payloads. The log is not append-only.\n\n## API keys\n\n- Keys are shown **once** at creation. Shopigent stores a SHA-256 hash of the key. The hash is not salted.\n- **Regenerate connection key** replaces that hash, and the previous key is rejected. Uninstall clears the hash. There is no separate revoke control that leaves the old key in place.\n- Each key is scoped to one store.\n\n## Data handling\n\n- Shopigent processes your store data to execute tool calls; it does not sell it or use it for advertising.\n- Store data is **not used to train AI models**.\n- Audit log entries are retained so you can review agent activity; you can request deletion of your data at any time.\n- Uninstalling the app deletes session rows and clears the MCP API key hash. It does not delete the shop row. `shop/redact`, about 48 hours later, deletes the shop row. `customers/redact` redacts matching audit payloads. See the [Privacy Policy](/privacy-policy).\n\n## Shopify-side protections\n\n- Shopigent requests only the Shopify scopes needed for the categories you use.\n- All traffic runs over TLS.\n- Billing is handled entirely by Shopify — Shopigent never sees your payment details.\n\n## Operational observability\n\nProduct media uploads emit structured JSON log entries (`event: \"media_upload_stage\"`)\nat every pipeline stage: stagedUploadsCreate, confirmation_issued, binary_upload,\nfileCreate, file_status_polling, productSet, and final_readback.\n\nEach entry carries:\n- stage name and shop identifier\n- operation ID (your idempotency key or a generated hash)\n- product ID and file count\n- on error: a sanitized diagnostic excerpt and Shopify request ID\n\n**The following are never included in any log entry:**\n- Shopify access tokens, client secrets, or session tokens\n- HTTP headers or bearer tokens\n- Confirmation token values\n- Base64 file payloads or staged upload target parameters\n- Signed URLs, X-Amz headers, or S3 credentials\n\nSee the [Privacy Policy](/privacy-policy) for data retention and deletion.\n\n## GitHub theme connections\n\n- A new store cannot connect Theme Pull Requests through the GitHub App. The page does not start an install, and the `/auth/github` callback does not save a first installation. Connect with a repository-scoped personal access token. A shop that already has a stored installation can choose a repository that installation can access. If the GitHub App is not configured, the page says the one-click flow is not set up and the token form remains.\n- A personal access token is encrypted at rest (AES-256-GCM) before it is stored and is never returned by any API or shown again.\n- Shopigent reads only explicitly requested repository files. A dry-run returns a preview and does not open a pull request. A live call requires confirmation, then it can create a branch, commits, and a pull request containing the declared changes.\n- Shopigent never merges, deploys, publishes, or directly edits Shopify theme code through the Admin Theme API, and it does not request direct Shopify theme scopes.\n- You review and merge the PR. Shopify's native GitHub theme integration then synchronizes the connected branch to the theme you selected. Use an unpublished connected theme for review so the live storefront remains unchanged; publication is always merchant-controlled.\n\n## Reporting a vulnerability\n\nFound something? Email **shopigent@greeknous.com**. We respond fast and appreciate responsible disclosure.\n"},{"slug":"guides/troubleshooting-faq","title":"Troubleshooting & FAQ","tags":["guide","faq","troubleshooting"],"body":"\n# Troubleshooting & FAQ\n\n## Connection issues\n\n**401 Unauthorized**\nThe API key is invalid or no longer matches the stored hash. Generate or regenerate the connection key on the dashboard and update your MCP client config. The key is shown only once.\n\n**MCP client can't find the server**\nThe MCP server address is `https://mcp.shopigent.greeknous.com/mcp`. Send the connection key as a Bearer token. Do not use `https://app.shopigent.greeknous.com/mcp`. Cursor and Claude Desktop need a full restart after a config change.\n\n## Missing or failing tools\n\n**A tool doesn't appear in the tools list**\nThe tool's enable switch is off on **Curated Tools**, the tool is retired (`list_themes` and the other direct theme tools are rejected and are not listed), or a custom tool is not active. A Theme Pull Request tool can still be listed when no repository is connected. A new GitHub App connection cannot be started on the Theme Pull Requests page. A shop that already has a stored installation can choose a repository that installation can access. Otherwise use the personal access token form. If the GitHub App is not configured, the page says so and the token form remains. Turning an area off on **Scopes & Permissions** stops the tool from running. It does not, by itself, remove an enabled tool from the list.\n\n**Upgrade required**\nThe live call is not included in the current plan. The response is an error with `upgradeRequired: true`. On Free, a curated write can still be previewed with `dryRun: true`, and that preview is not executed. Custom tools, including their preview, require Pro. `get_translations`, `set_translations`, and `delete_translations` require Pro. See [Plans & Billing](/guides/plans-and-billing).\n\n**Daily cap or \"Rate limit exceeded\"**\nFree allows 25 tool calls per day and Growth allows 500. Pro has no daily cap. Every plan, including Pro, also stops at 60 calls per minute. A daily-cap error asks you to upgrade. A per-minute error says \"Rate limit exceeded\" and includes a retry delay.\n\n## Confirmation gate\n\n**\"Action pending confirmation\" — nothing happens**\nThe action is waiting for approval in your AI client, or on the Confirmations page in the Shopigent admin. Approving in the AI client re-calls the tool with the signed token and exactly the same arguments. Approving on the Confirmations page asks the MCP server to run that same check. Declining there uses the token and does not run the tool. The page does not show the token. A product media upload cannot be approved on that page because the file payload is not stored.\n\n**Can I turn off confirmations?**\nYes — per tool, except four actions that always confirm: publishing a product to a sales channel, changing a variant's shipping weight, uploading product media, and creating a theme pull request. Destructive tools arrive with confirmation on, and you can turn that off for the others. Use `dryRun: true` for a preview. The preview is not executed. On the Free plan a write preview is allowed and the write itself is not.\n\n## Theme Pull Requests\n\n**Why did my PR not change the Shopify storefront?**\nShopigent stops after creating the pull request. Review and merge it on GitHub.\nShopify's native GitHub theme integration then synchronizes the connected branch\nto the theme you selected. For testing, connect an unpublished theme so the live\nstorefront stays unchanged. Publishing is always a merchant action.\n\n**Can Shopigent edit or publish a Shopify theme directly?**\nNo. It does not request direct Shopify theme scopes and does not use the Admin\nTheme API. Theme repository changes are PR-only.\n\n## General\n\n**Does Shopigent work with any MCP client?**\nYes — anything that speaks Model Context Protocol: Claude, Cursor, Hermes, custom agents.\n\n**Can agents see data in disabled categories?**\nNo. Turning an area off stops tools in that area from running, so the call does not return that area's store data. An enabled tool can still be listed.\n\n**Where do I see what my agent did?**\nThe **Audit Log** shows each call's tool, read or write, status, time, latency, and client. **View stored details** shows a redacted copy of the stored input and response summary. The view redacts secret- and token-like fields, names, addresses, and email- or phone-shaped values. Opening it does not change the database row. The dashboard **Recent activity** card counts successful writes, dry-runs, confirmations waiting, and confirmations declined for the last 24 hours and the last 7 days. **Curated Tools** shows each tool's stored call count, last status, and last dry-run time.\n\n**How do I completely revoke an agent's access?**\nChoose **Regenerate connection key** on the dashboard, or uninstall the app. Regenerating replaces the stored hash, so the previous key is rejected. Uninstall clears the key immediately, so the old token returns 401. Generating a new key after you reinstall replaces it. Uninstall does not delete the shop row; `shop/redact` does that about 48 hours later.\n\n## Missing scope\n\n**`MISSING_SCOPE` error — what does it mean?**\nShopify refused the operation because the app's current OAuth token is missing\na required permission. This is different from a disabled Shopigent category\n(which controls tool visibility) — the Shopify installation itself does not\ninclude the scope.\n\n**How do I read the error?**\nThe error response includes a `details` object with:\n- `missingScopes[]` — exact Shopify handles the store still lacks (e.g.\n  `[\"read_files\", \"write_files\"]`). This list is authoritative only when `scopeSource` is\n  `\"shopify_error\"` or `\"live_grant\"`.\n- `scopeDisclosed` — `true` when Shopify named the required scope, `false`\n  when it only said \"access denied\" without a handle.\n- When `scopeDisclosed` is `false` and known prerequisites are available,\n  `knownPrerequisites[]` lists what the tool needs — check those against your\n  OAuth grant.\n- `deniedField` — the Shopify GraphQL field that triggered the denial (e.g.\n  `stagedUploadsCreate`).\n\n**What if `requiredScope` is `null`?**\nShopify refused the operation without naming the specific scope. Open\n**Scopes & Permissions** in the embedded app, find the category the tool\nbelongs to, and grant its scopes. The app shows exact missing handles in the\nbanner when they can be determined.\n\n**How do I reauthorize?**\n1. Open the **Scopes & Permissions** page in Shopigent's embedded app.\n2. Read the missing scopes listed in the warning banner (e.g. `read_files`, `write_files`).\n3. Click **Grant missing permissions** — this opens the Shopify OAuth install screen\n   with all scopes the app needs.\n4. Approve the permissions. Shopify redirects back to the app.\n5. The app verifies the new live grant. Capabilities refresh automatically;\n   the Curated Tools badge reads **Media upload available** or **Media upload unavailable**.\n6. Retry the agent call with the same full payload.\n\n**Can I use the Scopes & Permissions checkboxes instead?**\nNo. Those checkboxes turn areas on or off for your agent. Turning an area off\nstops its tools from running. They do not add or remove Shopify's permission.\nOnly the Shopify approval screen can grant a permission the store does not have yet.\n\n**Does write_files imply read_files?**\nNot for this app — Shopigent requests `read_files` and `write_files` as two\nseparate required scopes, and product media upload needs both. In Shopify's model\na write scope carries effective read access for the same resource, but\n`upload_product_media` polls file status through the Files API, so the app\ndeclares both handles explicitly rather than relying on that implication.\n\nStill stuck? Email **shopigent@greeknous.com**.\n"},{"slug":"index","title":"Shopigent — Operate your Shopify store with AI agents","tags":["home","overview"],"body":""},{"slug":"privacy-policy","title":"Privacy Policy","tags":["legal","privacy"],"body":"\n# Shopigent — Privacy Policy\n\n**Last updated:** 2026-10-24 | **Controller:** Greek Nous | **Contact:** shopigent@greeknous.com\n\n## 1. What Shopigent is\nShopigent is an MCP server and a Shopify app. Merchants connect an MCP client (for example Hermes, Claude, Cursor, OpenAI Codex, or xAI Grok) to it. It exposes a curated set of read and write operations (\"tools\") over the Shopify Admin API, gated by the merchant's chosen plan and the scopes they grant at install time.\n\n## 2. Data we access\nAt install, the merchant authorizes Shopify OAuth scopes. Shopigent only accesses data required by the tools the merchant enables. Possible scope categories include products, orders, customers, discounts, inventory, fulfillments, online store content, translations, and custom data. Shopigent does not request direct Shopify Theme API scopes and does not directly read, modify, duplicate, or publish Shopify themes. We access store data **only** to fulfill a tool call the merchant (or their agent, on their behalf) explicitly triggers.\n\nWe do **not** access: payment instrument numbers, passwords, or any data outside the authorized Shopify scopes.\n\n## 3. Data we store\n- **Shop connection metadata** (store domain, granted scopes, current plan).\n- **API key hashes** — we store a SHA-256 hash of the MCP API key. The hash is not salted. The plaintext key is shown once at creation and never stored.\n- **Tool-call audit logs** — for every tool call we record: shop, tool key, operation type, input arguments, result status, and latency.\n- **Plan / billing events** — subscription status and Shopify charge IDs (received via Shopify webhooks), not card numbers.\n- **GitHub connection (optional)** — repository owner, name, branch, and either an encrypted Shopigent Connector installation marker or the merchant's encrypted fine-grained PAT. Tokens are never returned after saving.\n\n## 4. Data we share\nWe do not sell or share merchant data with third parties except:\n- with **Shopify**, necessarily, to perform the requested operations;\n- with the merchant's chosen AI client (e.g. OpenAI Codex, Anthropic Claude, xAI Grok), only the data needed to answer the specific request they made;\n- with **GitHub (optional)**, when a stored GitHub App installation is already on the shop, or when the merchant supplies a fine-grained PAT. A new store cannot start a GitHub App connection on the Theme Pull Requests page, and the `/auth/github` callback does not save a first installation. That page tells the merchant to use a repository-scoped personal access token. If the GitHub App is not configured, the page says so and the token form remains. Shopigent reads explicitly requested repository files. A dry-run returns a preview and does not open a pull request. A live call requires confirmation, then can create a branch, commits, and a pull request containing declared changes to valid theme files such as locale JSON, JSON templates, Liquid sections/snippets/blocks, assets, and `config/settings_data.json`. Shopigent never merges, deploys, publishes, or directly edits Shopify theme code through the Admin Theme API. The merchant reviews and merges the PR. Shopify's native GitHub integration then synchronizes the connected branch to the merchant-selected theme; reviewer demonstrations use an unpublished theme so the live storefront remains unchanged. General product, page, collection, article, and metaobject translations remain Shopify Translations API operations.\n\n## 5. Data retention\nAudit logs and connection metadata are kept while the shop row exists. `app/uninstalled` deletes session rows and clears the MCP API key hash. It does not delete the shop row. `shop/redact`, which Shopify sends about 48 hours after uninstall, deletes the shop row and the records that cascade from it. `customers/redact` redacts matching audit payloads for the customer id in the webhook. It does not delete the whole shop.\n\n## 6. Security\nMCP API keys are hashed (never stored in plaintext). GitHub connection tokens (PATs and installation markers) are encrypted at rest with AES-256-GCM before they are written to the database and are never returned by any API or tool response. All Admin API calls use the store's OAuth access token, transmitted only to Shopify over TLS. The high-risk writes carry an explicit confirmation step: the tool returns a signed, single-use, time-limited `confirmation_pending` result. The human approves **in their AI client** and the agent re-calls the tool with the signed confirmation token before anything executes, or approves or declines the same pending token on the Confirmations page. Approve on that page asks the MCP server to run the same check. Decline uses the token and does not run the tool. The page does not show the token. Confirmation is on by default for destructive tools. It can never be turned off for publishing a product to a sales channel, changing a variant's shipping weight, uploading product media, or creating a theme pull request. For every other tool the merchant can disable it per store. Translation writes go through the same gate when it is enabled.\n\n## 7. Your rights\nMerchants may request deletion of their data via shopigent@greeknous.com. Uninstalling the app follows the webhook behavior in section 5. Uninstall alone does not delete the shop row.\n\n## 8. Changes\nMaterial changes to this policy will be communicated via the support email."},{"slug":"proof-of-resolution-theme-workflow","title":"Shopigent — Theme workflow clarification and screencast","tags":["review","github","theme-workflow"],"body":"\n<style>\n.proof-intro{border-left:4px solid #269982;padding:1rem 1.25rem;background:rgba(38,153,130,.08);border-radius:0 12px 12px 0}.proof-video{width:100%;height:auto;background:#111;border-radius:16px;border:1px solid rgba(69,43,96,.25);margin:1rem 0}.proof-flow{display:flex;flex-wrap:wrap;gap:.65rem;margin:1.4rem 0}.proof-flow span{padding:.6rem .85rem;border:1px solid rgba(38,153,130,.4);border-radius:10px;font-size:.9rem}.proof-compare{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem;margin:1.25rem 0}.proof-compare>div{padding:1.2rem;border:1px solid rgba(38,153,130,.45);border-radius:14px}.proof-compare strong{display:block;margin-bottom:.6rem}.proof-compare p{margin:.3rem 0!important}.proof-label{font-size:.8rem;text-transform:uppercase;letter-spacing:.06em;opacity:.8}.proof-note{font-size:.9rem;opacity:.85}@media(max-width:640px){.proof-compare{grid-template-columns:1fr}}html.dark .proof-intro{background:rgba(38,153,130,.14)}\n</style>\n\n# Theme workflow clarification & screencast\n\n**Shopigent · GitHub Theme Pull Requests**\n\n<div class=\"proof-intro\">\n<strong>Answer to the open theme-functionality clarification</strong>\n<p>Shopigent does not give an AI platform direct access to Shopify’s theme editor. The merchant’s MCP client requests specific files from a designated GitHub repository through Shopigent, proposes changes, and asks Shopigent to create a pull request. The merchant reviews and merges that PR in GitHub. Shopify’s separate native GitHub integration synchronizes the connected branch to its associated theme.</p>\n<p><strong>Shopigent creates the proposal. The merchant controls the merge. Shopify performs the native synchronization. Theme publication is a separate merchant action.</strong></p>\n</div>\n\n## Watch the recorded workflow\n\nThis screencast shows actual Codex tool calls, the dry-run and confirmation response, PR creation, a human review and merge, and the resulting unpublished-theme preview alongside the unchanged live storefront. English narration and synchronized captions are included.\n\n<video class=\"proof-video\" controls playsinline preload=\"metadata\" aria-label=\"Shopigent GitHub pull request and unpublished theme workflow\">\n<source src=\"/Shopigent-GitHub-PR-Unpublished-Theme-Workflow.mp4\" type=\"video/mp4\">\nYour browser cannot play the embedded video. Use the direct MP4 link below.\n</video>\n\n[Open the video directly](/Shopigent-GitHub-PR-Unpublished-Theme-Workflow.mp4) · [Download MP4](/Shopigent-GitHub-PR-Unpublished-Theme-Workflow.mp4)\n\n<p class=\"proof-note\">Browser-compatible MP4: H.264 video, AAC audio, faststart. Runtime: approximately 2 minutes 50 seconds. Once published, this page and its video are intended to be accessible without signing in to Shopify or GitHub.</p>\n\n## How are theme updates implemented?\n\n<div class=\"proof-flow\" aria-label=\"Workflow sequence\">\n<span>1 · Explicit file read</span><span>2 · Proposed change + dry-run</span><span>3 · Merchant confirmation</span><span>4 · Branch + commit + PR</span><span>5 · Human GitHub merge</span><span>6 · Shopify-native sync</span><span>7 · Unpublished preview</span>\n</div>\n\n1. **Read an explicitly named repository file.** The merchant chooses a repository connection in Shopigent. The client calls `github_theme_file` with a path and ref; in the video, it requests `templates/index.json` on `main`. Shopigent retrieves that file from GitHub and returns it to the client. The client can use its chosen AI provider to process that returned content; this is not a claim that file content stays on the merchant’s machine.\n2. **Propose a targeted change.** Codex derives replacement file content from the actual file read. The recorded change replaces one homepage headline, preserving the rest of the file. The PR tool accepts explicitly supplied file changes; the headline example demonstrates that mechanism, not a restriction that all proposals must be text-only.\n3. **Preview and obtain confirmation.** A `dryRun: true` call returns a plan without creating a branch, commit, or PR. A subsequent unconfirmed request returns `confirmation_pending`. The merchant explicitly approves PR creation, and the confirmed request uses a token bound to the proposal arguments. Approval to create a PR is not approval to merge or publish.\n4. **Create a fresh proposal branch and PR.** Shopigent writes the declared changes to a new branch and opens a PR targeting the selected base branch. It does not write this proposal directly to the existing connected branch, merge the PR, or publish a Shopify theme.\n5. **Merchant reviews and merges outside Shopigent.** The video shows the merchant inspect the GitHub diff and manually merge the PR into `main`. This is a GitHub action performed by the merchant, not a Codex or Shopigent merge operation.\n6. **Shopify synchronizes its native theme connection.** After the merge, Shopify’s own GitHub integration synchronizes the connected `main` branch to the associated unpublished theme. Shopigent targets a repository and branch—not a Shopify theme ID. The destination and publication status are determined by the merchant’s Shopify-native theme connection.\n7. **Preview without publication.** The merchant opens the draft theme’s preview and separately checks the live storefront. No Publish action is performed in this demonstration.\n\n## Does the AI platform access and manually edit the store theme?\n\n**Not directly through Shopigent’s Shopify theme-editing APIs.** In the current GitHub Theme Pull Requests workflow, the AI client reads specifically requested GitHub files through Shopigent and supplies proposed replacement content through MCP. Shopigent applies approved proposals to a fresh GitHub branch and opens a PR. The AI provider is not given a Shopify Admin session or a direct theme-editor capability by this workflow.\n\nThe client’s AI provider may receive the requested file content to process the merchant’s instructions. GitHub receives the approved branch, commit, and PR. Shopigent processes the tool inputs and outputs, and its merchant-visible audit log records tool activity; this page does not claim that Shopigent receives or retains no content. See the [Privacy Policy](/privacy-policy) for data-handling details.\n\n**Clarification of our earlier replies:** references to direct theme duplication, theme-file updates, and theme publication described an earlier implementation. The current GitHub-only workflow does not expose those direct Shopify Theme API tools. It does not create a brand-new Shopify theme, install a theme, or publish one. Existing theme files in a merchant-designated repository can be proposed for modification, but merge and publication remain separate merchant-controlled actions.\n\n## Full scope of the GitHub functionality\n\n| Tool | Function | Boundary |\n|---|---|---|\n| `github_theme_connection` | Returns the merchant’s designated repository connection. | Does not expose connection credentials. |\n| `github_list_theme_branches` | Lists branches in that repository. | Read-only. |\n| `github_theme_file` | Reads an explicitly requested repository file at a ref. | Read-only; no Shopify theme-editor access. |\n| `github_create_theme_pr` | Previews a proposal or, after confirmation, creates a new branch, commits declared file changes, and opens a PR. | No merge, Shopify-native sync, or theme publication operation. |\n\nRepository access can use a GitHub App installation that is already stored for the shop, or a merchant-supplied GitHub token. Starting a new GitHub App connection on the Theme Pull Requests page is unavailable. Repository authorization in Shopigent and Shopify’s native GitHub theme connection are **two separate integrations**; configuring one does not configure the other.\n\n## What the screencast demonstrates\n\n| Sequence | Visible evidence |\n|---|---|\n| Initial state | The Current theme and a separate GitHub-connected unpublished theme. |\n| Explicit read | Codex verifies `shopigent-test.myshopify.com`, `MK-XXXVII/shopigent-kosmos-theme`, `main`, and `templates/index.json`. |\n| Dry-run | Proposed head `update/homepage-headline-01`, base `main`, one affected file, and `dry_run`. |\n| Confirmation | `confirmation_pending`, the client stops, and the merchant explicitly approves PR creation. |\n| PR creation | PR #2, **Update homepage headline**, is returned. |\n| Human review and merge | The merchant opens Files changed, reviews the one-line replacement, and manually merges in GitHub. |\n| Draft result | The GitHub-connected unpublished theme preview displays **Discover something special today**. |\n| Live result | The original Current theme’s storefront still displays **Browse our latest products**. |\n\n<div class=\"proof-compare\">\n<div><span class=\"proof-label\">Unpublished · GitHub-connected theme</span><strong>Discover something special today</strong><p>Before the proposal: Explore our latest products.</p><p>Updated through the connected branch after the merchant’s merge; not published.</p></div>\n<div><span class=\"proof-label\">Current · Live storefront</span><strong>Browse our latest products</strong><p>The original theme is not connected to this GitHub branch.</p><p>Its headline remains unchanged in the final comparison.</p></div>\n</div>\n\n**Important scope of this evidence:** the unchanged live storefront follows from this store’s separate theme connections. It is not a blanket guarantee that merging into any GitHub branch cannot affect a live theme. If a merchant connects a branch to a published theme, Shopify’s native integration can synchronize that branch to the published theme. The merchant must choose and verify the destination before merging.\n\nRecorded repository and PR: `MK-XXXVII/shopigent-kosmos-theme`, PR #2. The screencast provides the visible diff and merge evidence; direct repository access may require GitHub authorization. No reviewer credentials are published on this page.\n\n## Review scope\n\nThis page addresses **item (a)**: how AI-assisted theme proposals are implemented and the scope of the GitHub functionality. **Item (b) was marked resolved by Shopify** and is not reopened here. This evidence page does not imply Shopify has accepted the resolution; the review decision remains with Shopify.\n"},{"slug":"reference/scopes-and-permissions","title":"Scopes & Permissions Reference","tags":["reference","scopes","permissions"],"body":"\n# Scopes & Permissions Reference\n\nTwo layers of scoping protect your store: **Shopify OAuth scopes** (what the app itself may access) and **Shopigent category toggles** (what your agents may access). An agent can never exceed either layer.\n\n## Category → Shopify scope map\n\n| Shopigent category | Read scope | Write scope |\n|---|---|---|\n| Products | `read_products` | `write_products` |\n| Orders | `read_orders` | `write_orders` |\n| Customers | `read_customers` | `write_customers` |\n| Discounts | `read_discounts` | `write_discounts` |\n| Content (pages/blogs) | `read_content` | `write_content` |\n| Metaobjects & Custom Data | `read_metaobjects` | `write_metaobjects` |\n| Theme Pull Requests | app-internal GitHub repository connection | app-internal GitHub repository connection |\n| Files / Media upload | `read_files` (required) | `write_files` (required) |\n| Inventory | `read_inventory` | `write_inventory` |\n| Fulfillments | `read_fulfillments` | `write_fulfillments` |\n| Translations *(Pro)* | `read_translations` | `write_translations` |\n| Publications | `read_publications` | `write_publications` *(optional)* |\n\n### Optional publication access\n\n`read_publications` remains part of Shopigent's required read access. The separate `write_publications` scope is optional and lets Shopigent publish or unpublish resources on a sales channel selected by the user. It is not required for the rest of the tool catalog: product, order, content, media, and other enabled tools continue to work when publication access is not granted.\n\nTo enable it, open **Scopes & Permissions** in the embedded app, find **Product publication management**, and choose **Enable product publication management**. Shopify then asks the merchant to approve this scope. The embedded app reports the verified live grant; declaring the optional scope in app configuration does not grant it. A merchant may decline the request or later choose **Revoke publication access** without disabling the other tools.\n\nPublication targets are always selected explicitly from the IDs and channel labels returned for that store. Shopigent does not infer a channel from its name, choose an \"Online Store\" automatically, or publish to every channel.\n\nShopigent does **not** request Shopify's direct theme scopes. Theme repository\nwork uses the four `github_*` tools. When the GitHub App is configured and this shop already has a stored installation, the page can choose a repository that installation can access. Starting a new GitHub App connection on that page is unavailable; the page then says to use a repository-scoped personal access token. If the GitHub App is not configured, the page says the one-click flow is not set up and the token form remains. Shopigent reads\nexplicit files. A dry-run returns a preview and does not open a pull request. A live\ncall requires confirmation, then can create a branch,\ncommits, and a pull request. It never merges, deploys, publishes, or edits theme\ncode through the Admin Theme API. After the merchant merges, Shopify's native\nGitHub theme integration can synchronize that connected branch to the\nmerchant-selected theme; use an unpublished theme for review so the live\nstorefront remains unchanged.\n\n## File scopes for media upload\n\n`upload_product_media` requires a **verified** live grant containing\n`write_products`, `read_files` and `write_files`. A configured or stored scope\nstring is never proof of authorization — only a live OAuth grant snapshot counts.\n\n`read_files` and `write_files` are declared as required scopes in the app\nconfiguration. Together they cover the staged upload workflow:\n\n1.  **stagedUploadsCreate** — request signed upload targets from Shopify.\n2.  **Binary upload** — PUT each file to its signed target.\n3.  **fileCreate** — register the uploaded files as Shopify MediaImage records.\n4.  **File-status polling** — wait for Shopify to process each image to READY.\n5.  **productSet (attach)** — attach images to a product in gallery order and set the featured index.\n\nA verified live grant of both `read_files` (file-status polling) and\n`write_files` (staged upload + registration) is required; requesting one without\nthe other is not enough.\n\n### Four dashboard states (never conflated)\n\nShopigent's embedded app distinguishes four layers that the dashboard surfaces\nseparately:\n\n| Layer | Means | Example state |\n|-------|-------|---------------|\n| Tool enabled | The merchant toggled `upload_product_media` on in Curated Tools | Checkbox == on |\n| Requested | The app configuration declares `read_files` and `write_files` | Process.env.SCOPES includes both |\n| Granted | The store's *live* Shopify installation actually granted `read_files` and `write_files` | Verified OAuth snapshot |\n| Executable | All four layers satisfied | Green badge: \"Media upload is available\" |\n\nWhen the live grant is stale or unverifiable, the dashboard shows\n**\"Reauthorization required\"** and an exact list of missing scope handles\n(`read_files`, `write_files`, `write_products`) rather than a vague \"access\" label.\nAn enabled toggle is never proof of Shopify authorization — a store may have\nthe tool on but the scope not yet granted.\n\nSee the [Troubleshooting guide](/guides/troubleshooting-faq#missing-scope) for\nsteps to resolve a `MISSING_SCOPE` error.\n\n## How the layers interact\n\n```\nAgent calls tool\n   │\n   ├─ 1. Is this tool enabled for the shop?           ── no → not included in tools/list\n   ├─ 2. Does the plan allow this live call?          ── no → error, upgradeRequired: true\n   │      A curated dry-run on a plan that cannot run the write still previews\n   │      and does not write. Custom tools, including a dry-run, require Pro.\n   ├─ 3. Is the scope category enabled and granted?   ── no → the call does not run\n   ├─ 4. Confirmation on, or one of the four actions  ── yes, and this is not a dry-run\n   │      that always pause?                              → confirmation_pending\n   └─ 5. Execute → record in the audit log\n```\n\n## Principle of least privilege\n\n- Start with only the categories you need. You can enable more later without reinstalling.\n- There is one connection key per store. Regenerating it rejects the previous key for every client.\n- Review the audit log after enabling a new category to see how your agent uses it.\n"},{"slug":"reference/tools","title":"Tools Reference","tags":["reference","tools"],"body":"\n# Tools Reference\n\nShopigent exposes **87 distinct seeded merchant tools** across nine categories. Each category can be toggled on/off per store, and destructive write tools default to the confirmation gate. Runtime-only connection identity and capability utilities (`get_connection_identity`, `get_connection_capabilities`, registered by the MCP server rather than seeded) are listed separately and are not included in the 87-tool catalog count.\n\n> Tools are *curated operations*, not raw API passthrough — inputs are validated, outputs are trimmed to what agents actually need, and destructive calls default to requiring human approval.\n\n## Plans at a glance\n\n| Plan | Daily calls | Access |\n|------|-------------|--------|\n| Free | 25/day | Reads, plus dry-run previews of writes (never executed) |\n| Growth | 500/day | All writes except premium tools |\n| Pro | No daily cap (60/min fair use) | Everything, including general Shopify resource translations |\n\n**Premium (Pro-only) tools:** `set_translations`, `get_translations`, `delete_translations`.\n\n---\n\n## Products (31)\n\n| Tool | Type | Plan | What it does |\n|------|------|------|--------------|\n| `list_products` | Read | Free | Paginated list of products, optionally filtered by a search query. |\n| `search_products` | Read | Free | Full-text search across product titles, descriptions, and tags with relevance scoring. |\n| `get_product_by_id` | Read | Free | Fetch a single product by its Shopify GID. |\n| `create_product` | Write | Growth | Creates a product with optional description, status, vendor, type, tags, options, and default-variant price. |\n| `update_product` | Write | Growth | Updates a product's fields including title, description, status, vendor, type, tags, SEO, handle, and template (templateSuffix / clearTemplateSuffix). |\n| `delete_product` | Write | Growth | Permanently deletes a product. Irreversible. |\n| `create_product_variant` | Write | Growth | Adds a variant with price, SKU, barcode, and option values to an existing product. |\n| `update_product_variant` | Write | Growth | Sets SKU, price, barcode, and option values on an existing product variant. |\n| `update_product_variant_price` | Write | Growth | Sets the price of a single product variant by its GID. |\n| `update_product_variant_weight` | Write | Growth | Safely updates only a variant's shipping weight. Supports grams, kilograms, ounces, and pounds; live writes always require confirmation. |\n| `bulk_update_prices` | Write | Growth | Sets prices for a list of variants on one product. dryRun returns one wouldChange diff and does not write. Free can preview and cannot execute. Growth and Pro execute with this tool's confirmation setting, which is on by default. |\n| `update_product_images` | Write | Growth | Manages product images (add, update alt text, delete) via the Shopify Media API. |\n| `upload_product_media` | Write | Growth | Uploads media to a product using Shopify's staged-upload flow. **Prerequisites:** a verified live grant containing `write_products`, `read_files` and `write_files`. Requires confirmation. |\n| `get_product_media` | Read | Free | Lists media attached to a product. |\n| `get_product_publications` | Read | Free | Lists where a product is published (channels, online store, etc.). |\n| `list_publications` | Read | Free | Lists this store's sales-channel publications, channel labels, and authoritative IDs for explicit selection. |\n| `publish_product_to_channel` | Write | Growth | Publishes one product to one explicitly selected publication. A dry-run does not publish. A live call always requires confirmation. |\n| `get_product_metafields` | Read | Free | Reads metafields attached to a product. |\n| `set_product_metafields` | Write | Growth | Writes or updates one or more product metafields. |\n| `get_store_info` | Read | Free | Returns store-level information: name, email, currency, timezone, and plan. |\n| `list_collections` | Read | Free | Lists all collections (manual + automated) with id, title, handle, and sort order. |\n| `get_collection_by_id` | Read | Free | Fetches a single collection by its GID, including description, image, product count. |\n| `create_collection` | Write | Growth | Creates a new manual or automated collection. |\n| `update_collection` | Write | Growth | Updates a collection's title, description, handle, sort order, and/or SEO. |\n| `delete_collection` | Write | Growth | Deletes a collection by its GID. |\n| `add_products_to_collection` | Write | Growth | Adds one or more products to a collection. |\n| `remove_products_from_collection` | Write | Growth | Removes one or more products from a collection. |\n| `update_collection_image` | Write | Growth | Adds, updates, or deletes a collection's image. |\n| `get_translations` | Read | Pro | Reads all translatable content for a resource (product, collection, page, etc.) in a locale. |\n| `set_translations` | Write | Pro | Writes or updates translation values for a resource in a locale. |\n| `delete_translations` | Write | Pro | Removes translation overrides for given keys in a locale. |\n\n### Publication tools: exact contracts\n\n#### `list_publications`\n\nUse this read-only tool before publishing to obtain publication IDs and the channel names and handles that belong to the connected store. The user or agent must choose one returned `publicationId` explicitly; Shopigent does not select a target by matching a channel name.\n\nInputs:\n\n| Field | Required | Rules |\n|---|---|---|\n| `first` | No | Number of publications per page; defaults to 25 and is capped at 50. |\n| `after` | No | Opaque pagination cursor from the previous `pageInfo.endCursor`. |\n| `catalogType` | No | Exact filter: `APP`, `COMPANY_LOCATION`, or `MARKET`. |\n\nThe result contains `publications[]` with each publication's ID, automatic/future-publishing flags, catalog identity, and `channels[]` (`id`, `name`, `handle`, `specificationHandle`, and app identity when available), plus `pageInfo.hasNextPage` and `pageInfo.endCursor`. It requires publication read access and performs no write or confirmation flow.\n\n#### `publish_product_to_channel`\n\nThis compound write establishes one publication edge for one product. It requires an authoritative live grant for both `writeProducts` and `writePublications`; a configured or cached scope is not enough. If either capability is missing or cannot be verified, the call fails closed before any write. `write_publications` is optional and can be enabled or revoked from **Scopes & Permissions** without affecting the rest of the catalog.\n\nInputs:\n\n| Field | Required | Rules |\n|---|---|---|\n| `productId` | Yes | Canonical `gid://shopify/Product/...` ID. |\n| `publicationId` | Yes | Canonical `gid://shopify/Publication/...` ID selected from `list_publications`. |\n| `activateProduct` | Yes | Explicit boolean. No default: the user decides whether a draft product may become active. |\n| `idempotencyKey` | Yes | Stable 8–128 character key using letters, numbers, `.`, `_`, `:`, or `-`. |\n| `stateFingerprint` | Confirmed call only | Server-issued state fingerprint from the preview; prevents execution of a stale plan. |\n\nBefore any mutation, the tool verifies that the product and publication belong to the connected store and reads the current product status and target publication edge. A `dryRun: true` call returns that product, the selected publication, the intended final state, and a state fingerprint, with zero mutations.\n\nEvery live call requires human confirmation even if a store-level confirmation toggle is off. The confirmation is bound to the store, tool, product ID, publication ID, activation choice, idempotency key, and state fingerprint. The confirmed call must resend the exact fields requested by the preview together with `confirmed` and the confirmation token. Expired, replayed, mismatched, or stale confirmations are rejected.\n\nIdempotency is scoped to the store and this tool. Reusing a key for a different target is rejected. A retry reads live state rather than trusting an earlier audit entry; if a merchant changed a previously successful target afterward, the caller must use a new key instead of silently restoring that state.\n\nThe workflow publishes the selected edge first when possible and activates the product only when the confirmed `activateProduct` choice requires it. Success is reported only after readback confirms the requested state. If a later step fails after this operation created the edge, compensation may remove **only that newly created target edge** and roll back a status change made by the same operation. It never removes a pre-existing edge. Partial or unknown final states return a structured error with the failed phase, final readback when available, mutations attempted, and compensation results; they are never reported as successful publication.\n\nThis tool does **not** change product media, prices, inventory, SEO, tags, templates, translations, or any other channel. It does not accept a channel name, a publish date, multiple publication IDs, or an \"all channels\" option.\n\n### `upload_product_media` — capability contract and retry semantics\n\n#### Prerequisites\n\nThis tool requires a **verified** live OAuth grant that includes\n`write_products`, `read_files` and `write_files` (see the [Scopes reference](/reference/scopes-and-permissions#file-scopes-for-media-upload)).\nNeither a configured scope string nor a stored offline session scope counts\nas proof — only an authoritative live-grant snapshot from Shopify is accepted.\nIf any of these prerequisites is missing from the live grant, the call fails *before* the\nupload payload is decoded, before any staged target is requested, and before a\nconfirmation token is issued.\n\n#### Capability response\n\nThe server's `get_connection_capabilities` tool returns `capabilities.uploadProductMedia`\nas true only when a **verified** live grant contains `write_products`, `read_files`, and `write_files`.\nThat tool response does not include `capabilityGaps`. `capabilities.uploadProductMedia` is the boolean the tool returns. It is false when the live grant is missing any of those scopes or when the live grant cannot be verified. Configured scopes in the app environment are not treated as a live grant.\n\n#### Confirmation gate\n\n`upload_product_media` always requires human confirmation. The dry-run\npreview (`dryRun: true`) validates the files and returns a plan of the steps\nit would take, including a staged-upload step. It does not call Shopify and\nit does not upload. Every\nlive call that passes the capability gate issues a signed, single-use\nconfirmation token bound to:\n- the shop\n- the tool\n- an args-hash of the complete payload (product ID, files, featured index)\n\nThe initial confirmation response includes a `content.resendRequired` field\nset to `\"full_identical_payload\"`. The agent must present the **complete\nidentical payload** together with the confirmation token; a token alone is\nnot enough to resume after expiry. This is an explicit design choice\n(SH-014 Option A was chosen over Option B): the server does not store upload\npayloads, so every retry after a 25-minute token TTL must re-send the full\nfiles and arguments. The confirmation gate accepts a previously used token\nfor retry purposes as long as the args hash matches, the shop matches, and\nthe token is not expired — this allows the agent to retry the identical call\nafter a partially completed workflow without re-confirming.\n\n#### Retry contract with idempotency\n\nThe upload workflow uses idempotency keys across the multi-step pipeline:\n\n- **Idempotency is key-based.** Reusing the same idempotency key on a\n  previously successful call returns the prior result without creating\n  duplicate files. The server caches completed results for one hour.\n- **A mismatched payload on the same key** is rejected before any upload.\n- **A key used on a different store** is independent (idempotency is\n  scoped to the store + key).\n- **Partial/failed idempotent retries** resume from the last safe\n  checkpoint. If files were already created by a prior attempt, the retry\n  skips stagedUploadsCreate, binary upload, and fileCreate — it goes\n  directly to file-status polling and attachment. Duplicate files are\n  prevented.\n- **When the upload completes but the product attachment step (productSet)\n  fails**, the response includes `reconciliationRequired: true` and the\n  created file IDs, so the agent can re-attach them with the same\n  idempotency key.\n\n#### Observability stages\n\nEach upload emits a structured JSON log entry at every pipeline stage:\n\n1. `stagedUploadsCreate` — requesting signed targets\n2. `confirmation_issued` — token created with args-hash binding\n3. `binary_upload` — PUT'ing each file (per-file reporting)\n4. `fileCreate` — registering uploaded files as MediaImage records\n5. `file_status_polling` — waiting for READY status (with bounded timeout)\n6. `productSet` — attaching to product gallery in order\n7. `final_readback` — confirming the expected gallery state\n\nThese logs carry the stage name, shop ID, operation ID, product ID, file\ncount, and (on error) a sanitized diagnostic excerpt and Shopify request ID.\nThey never contain tokens, headers, confirmation tokens, base64 payloads,\nor staged target parameters. See [Security & Trust](/guides/security-and-trust)\nfor the logging rules.\n\n## Content (16)\n\n| Tool | Type | Plan | What it does |\n|------|------|------|--------------|\n| `list_pages` | Read | Free | Lists all pages with id, title, handle, and status. |\n| `get_page_by_id` | Read | Free | Fetches a single page by its GID, including body and SEO metadata. |\n| `create_page` | Write | Growth | Creates a new online store page. |\n| `update_page` | Write | Growth | Updates a page's title, body, and/or handle. |\n| `delete_page` | Write | Growth | Deletes a page by its GID. |\n| `list_blogs` | Read | Free | Lists all blogs with id, title, handle, and comment policy. |\n| `get_blog_by_id` | Read | Free | Fetches a single blog by its GID, including its articles and metadata. |\n| `create_blog` | Write | Growth | Creates a new blog. |\n| `update_blog` | Write | Growth | Updates a blog's title and/or handle. |\n| `delete_blog` | Write | Growth | Deletes a blog by its GID. |\n| `list_articles` | Read | Free | Lists all blog articles for a blog, with optional filters. |\n| `get_article_by_id` | Read | Free | Fetches a single blog article by its GID, including body, summary, tags, author. |\n| `create_article` | Write | Growth | Creates a new blog post (article) under a blog. |\n| `update_article` | Write | Growth | Updates a blog post's title, body, summary, handle, tags, author. |\n| `delete_article` | Write | Growth | Deletes a blog post by its GID. |\n| `update_article_image` | Write | Growth | Adds, updates, or deletes a blog article's featured image. |\n\n## Metaobjects / Custom Data (15)\n\nAccess to **Custom Data** (metaobjects & metafield definitions) requires a dedicated scope category in **Scopes & Permissions**.\n\n| Tool | Type | Plan | What it does |\n|------|------|------|--------------|\n| `list_metaobjects` | Read | Free | Lists metaobjects of a given type. |\n| `list_metaobject_definitions` | Read | Free | Lists metaobject definitions. |\n| `get_metaobject` | Read | Free | Fetches a single metaobject by its GID. |\n| `create_metaobject` | Write | Growth | Creates a new metaobject. |\n| `update_metaobject` | Write | Growth | Updates an existing metaobject. |\n| `delete_metaobject` | Write | Growth | Deletes a metaobject. Destructive. |\n| `get_metaobject_definition` | Read | Free | Fetches a single metaobject definition. |\n| `create_metaobject_definition` | Write | Growth | Creates a metaobject definition. |\n| `update_metaobject_definition` | Write | Growth | Updates a metaobject definition. |\n| `delete_metaobject_definition` | Write | Growth | Deletes a metaobject definition. Destructive. |\n| `list_product_metafield_definitions` | Read | Free | Lists metafield definitions scoped to products. |\n| `get_metafield_definition` | Read | Free | Fetches a single metafield definition. |\n| `create_metafield_definition` | Write | Growth | Creates a metafield definition. |\n| `update_metafield_definition` | Write | Growth | Updates a metafield definition. |\n| `delete_metafield_definition` | Write | Growth | Deletes a metafield definition. Destructive. |\n\n\n## Orders (6)\n\n| Tool | Type | Plan | What it does |\n|------|------|------|--------------|\n| `list_orders` | Read | Free | Paginated list of orders, filterable by status/date/tags. |\n| `search_orders` | Read | Free | Full-text search across orders by number, customer name, or email. |\n| `get_order` | Read | Free | Fetches a single order by its GID (line items, addresses, tags, status). |\n| `create_order` | Write | Growth | Creates an order directly (not a draft) from line items. Optional customer, note, tags, and shipping address. Confirmation is off by default. |\n| `update_order` | Write | Growth | Updates editable order fields: tags, note, and/or email. |\n| `refund_order` | Write | Growth | Issues a refund on an order. Irreversible financial action. |\n\n## Customers (5)\n\n| Tool | Type | Plan | What it does |\n|------|------|------|--------------|\n| `list_customers` | Read | Free | Lists store customers with optional filters. |\n| `search_customers` | Read | Free | Full-text search across customers by name, email, or phone. |\n| `get_customer` | Read | Free | Fetches a single customer by GID (name, email, phone, tags, note). |\n| `create_customer` | Write | Growth | Creates a new customer. |\n| `update_customer` | Write | Growth | Updates first name, last name, email, phone, note, tags, locale, or tax exemption. Pass only the fields to change. Confirmation is on by default. |\n\n## Discounts (4)\n\n| Tool | Type | Plan | What it does |\n|------|------|------|--------------|\n| `list_discounts` | Read | Free | Lists store discounts (discount nodes). |\n| `get_discount` | Read | Free | Fetches a single discount by its GID node id. |\n| `create_discount` | Write | Growth | Creates a new discount code or automatic discount. |\n| `delete_discount` | Write | Growth | Deletes a discount by its GID. |\n\n## Fulfillments (4)\n\n| Tool | Type | Plan | What it does |\n|------|------|------|--------------|\n| `list_fulfillment_orders` | Read | Free | Lists store fulfillment orders. |\n| `get_fulfillment_order` | Read | Free | Fetches a single fulfillment order by its GID. |\n| `close_fulfillment_order` | Write | Growth | Closes a fulfillment order (marks it fulfilled/complete). |\n| `open_fulfillment_order` | Write | Growth | Re-opens a closed fulfillment order. |\n\n## Inventory (2)\n\n| Tool | Type | Plan | What it does |\n|------|------|------|--------------|\n| `update_inventory_quantity` | Write | Growth | Sets the available inventory quantity for an item at a location. |\n| `bulk_update_inventory` | Write | Growth | Sets available quantities for a list of inventory items at locations. dryRun returns one wouldChange diff and does not write. Free can preview and cannot execute. Growth and Pro execute with this tool's confirmation setting, which is on by default. If the optional inventory scope is not granted, the preview says so and the live call changes nothing. |\n\n## Theme Pull Requests (4)\n\nThe **Theme Pull Requests** category uses a stored GitHub App installation when one is already saved and the App is configured. Starting a new GitHub App connection on that page is unavailable; the page then says to use a repository-scoped personal access token. If the GitHub App is not configured, the page says the one-click flow is not set up and the token form remains. Shopigent reads only explicitly requested files. A dry-run of `github_create_theme_pr` returns a preview and does not open a pull request. A live call requires confirmation, then creates a branch, commits the declared changes, and opens a pull request. Valid paths include `locales/*.json`, `templates/*.json`, `sections/snippets/blocks/*.liquid`, assets, and `config/settings_data.json`. Shopigent never merges the PR, deploys or publishes a theme, or edits Shopify theme code through the Admin Theme API. The merchant reviews and merges. Shopify's native GitHub theme integration then synchronizes the connected branch to the merchant-selected theme. Reviewer demonstrations use an unpublished connected theme so the live storefront remains unchanged; publication remains merchant-controlled.\n\n| Tool | Type | Plan | What it does |\n|------|------|------|--------------|\n| `github_theme_connection` | Read | Free | Returns the designated repository connection (owner/repo/defaultBranch) without exposing tokens. |\n| `github_list_theme_branches` | Read | Free | Lists branches on the connected repository. Read-only. |\n| `github_theme_file` | Read | Free | Reads one explicitly named repository file at a ref and returns path, branch, SHA, and content. |\n| `github_create_theme_pr` | Write | Growth | Creates a branch, commits the declared file changes, and opens a PR on the connected theme repo. PR-only — never merges, deploys, or publishes. Confirmation required (approved in your AI client or on the Confirmations page); dry-run available. The Theme Pull Requests page shows the latest stored dry-run or pending-confirmation plan, including files and branch, and labels it not executed. That page does not create, merge, or publish a pull request. |\n\n## Runtime utilities (not counted in the 87-tool catalog)\n\n`get_connection_identity` and `get_connection_capabilities` are registered directly by the MCP server (not seeded as catalog tools) and provide connection metadata only. They are excluded from the published catalog count. (`get_store_info` is a seeded catalog tool and is counted above.)\n\n## Cross-cutting features\n\n- **Dry-run** — write tools accept `dryRun: true`. The response says it was not executed, describes what would change, and includes the GraphQL document and variables when the tool has one. On the Free plan this preview is allowed and the write is never run.\n- **Confirmation gate** — when a tool's confirmation setting is on, or when the action is one of the four that always pause (sales-channel publishing, variant shipping weight, product media upload, and theme pull request creation), the call returns a signed, single-use `confirmation_pending` result. The human approves **in their AI client**, and the agent re-calls the tool with the confirmation token before anything executes. The same pending token can be approved or declined on the Confirmations page in the Shopigent admin. That page does not show the token. Approve asks the MCP server to run the same check as a client re-call. Decline uses the token and does not run the tool. A product media upload cannot be approved on that page because the file payload is not stored. Destructive tools default to confirmation on. Other writes can be turned off per tool.\n- **Audit log** — every call is stored with its input, result, and latency. The Audit Log page shows the tool, status, time, latency, and client. Open a row to see the stored input and response summary. Secret, token, and email- or phone-shaped values are redacted in that view.\n- **Scope-based** — each category maps to its own OAuth scope category; enable only what your agent needs (least privilege)."},{"slug":"terms-of-service","title":"Terms of Service","tags":["legal","terms"],"body":"\n# Shopigent — Terms of Service\n\n**Last updated:** 2026-10-24 | **Provider:** Greek Nous | **Contact:** shopigent@greeknous.com\n\n## 1. Acceptance\nBy installing or using Shopigent (\"the App\") you agree to these Terms. The App is provided as-is, without warranty, for use with your Shopify store.\n\n## 2. Plans and billing\nThe App offers Free, Growth, and Pro plans. There is no Agency or multi-store plan. Plan limits (daily tool-call allowances, a fair-use limit of 60 calls per minute on every plan, and which tools can run) are enforced as described in the App. Paid plans are billed through Shopify's billing API. Shopify owns the subscription. When Shopify no longer reports a paid subscription, Shopigent treats the store as Free. Shopigent does not schedule a downgrade for the end of the billing cycle and does not calculate proration. We do not store card details.\n\n## 3. Use of tools\nThe App exposes operations over your Shopify store. You are responsible for the actions your agents perform. Destructive tools default to an explicit confirmation step before execution. Publishing to a sales channel, changing a variant's shipping weight, uploading product media, and creating a theme pull request always require that step. Other writes follow the confirmation setting for that tool. You should review audit logs regularly.\n\n## 4. Scope authorization\nThe App only performs operations your store has authorized via Shopify OAuth scopes. Enabling a tool does not grant new Shopify scopes; it only makes an already-authorized operation available to your agents.\n\n## 5. Acceptable use\nYou agree not to use the App to violate Shopify's Acceptable Use Policy, applicable law, or the rights of others. We may suspend access for abuse.\n\n## 6. Limitation of liability\nThe App is provided \"as is.\" To the maximum extent permitted by law, Greek Nous is not liable for indirect or consequential damages arising from use of the App, including merchant actions performed via AI agents.\n\n## 7. Termination\nEither party may terminate use by uninstalling the App. Upon uninstall, store data handling follows our Privacy Policy.\n\n## 8. Contact\nQuestions: shopigent@greeknous.com\n"}]}