Shopify

Manages one or more connected Shopify stores from Clapilot — full product management (create/edit/delete, variants, images), inventory per location, orders, and customers via the Shopify GraphQL Admin API.

What it does

shopify is a bundled iframe module that manages one or more Shopify stores from Clapilot. Per connected store it provides a store overview dashboard, full product management (create products, edit title, description, status, vendor, and tags, delete products, manage variants including prices and SKUs, and add/remove product images by URL), inventory management (relative adjustments and absolute stock set per location), order browsing (open, closed, and cancelled, with line-item and fulfillment detail), and customer search/browse. Product management is available both in the module UI — new-product form, product delete with a two-step confirm, vendor/tags and variant SKU editing, an absolute stock set input, and image add/remove in the product detail — and via the agent tools listed below. The UI is tabbed — Übersicht, Produkte, Bestellungen, Kunden — plus store connection management, and is localized in German, English, and Italian.

Store connections are workspace-global (org-level): every authenticated user of the instance sees and works with the same connected stores. created_by is attribution/audit only and is never used as a read/write filter.

The module is not installed by default. It appears in the Module Store as a bundled module and must be enabled by an admin; a global migration (db/migrations/288_shopify_module_default_off.sql) seeds a module_installs override so it also stays off on instances using the legacy_all install policy. Installing the module runs its schema migration (bundled-modules/shopify/migrations/001_create_shopify_schema.sql), which creates the shopify_stores table.

Connecting a store

The module talks to the Shopify GraphQL Admin API, version 2026-07, called server-side with an Admin API access token in the X-Shopify-Access-Token header. There is no browser OAuth flow and no Shopify SDK dependency; the REST Admin API is legacy and is not used — this module is GraphQL-only.

Stores are connected with the client credentials of a Shopify Dev Dashboard app (Shopify no longer allows creating the older admin custom apps with a permanent token, so this is the only method the connect UI offers):

  1. Create the app in the Shopify Dev Dashboard (dev.shopify.com). App and store must belong to the same Shopify organization.
  2. Configure the Admin API access scopes on the app version: read_products, write_products, read_orders, read_customers, read_locations, and write_inventory.
  3. Install the app on the store via custom distribution.
  4. Copy the Client ID and Client Secret (shpss_…) from the app's Settings page into the module's connect form.

For backward compatibility the API additionally accepts a permanent Admin API access token (access_token in POST stores) from a pre-existing legacy admin custom app; the store object records the method in use in its auth_method field ("access_token" or "client_credentials").

With client credentials the module never stores a permanent token: Shopify mints short-lived (~24 h) Admin API access tokens via the client-credentials grant, which the module caches encrypted and refreshes automatically before expiry. Rotating the client secret in the Dev Dashboard invalidates the stored credential — the connection must be updated with the new secret.

POST stores verifies the connection live before it is saved — for tokens via the GraphQL shop query, for client credentials by first minting a token via the client-credentials grant and then running the same shop query; POST stores/:id/verify re-runs the check for an existing connection.

Credential storage

Admin API access tokens, client secrets, and cached client-credentials tokens are stored AES-256-GCM-encrypted in the credentials JSONB column of shopify_stores. Secrets are never returned by any API: neither the module API (GET stores included) nor any agent tool exposes a stored token or client secret after the initial connect. Store objects only expose the non-secret auth_method field.

API

The module API lives under /api/modules/shopify/api and is authenticated like all module handler APIs (session cookie or agent service auth); the iframe UI and the agent tools both use it. All endpoints return {"error":"module_not_installed"} while the module is disabled. See API Reference for the endpoint list: store CRUD/verify, stores/:id/overview, products (list/detail/create/update/delete — update also covers vendor and tags, and the product detail includes vendor, tags, and images as [{id, url, alt_text}]), variants (create/update/delete plus the price endpoint), product images (add by URL — processed asynchronously by Shopify — and remove), locations and inventory (relative adjust and absolute set), orders (list/detail), and customer search. Error codes: 422 shopify_auth_failed, 400 invalid_domain, 404 store_not_found, and 429 shopify_rate_limited. For client-credentials connections, shopify_auth_failed responses may carry a detail string noting that app and store must belong to the same Shopify organization and that the app must be installed on the store.

Agent tools

shopify_list_stores, shopify_get_store_overview, shopify_list_products, shopify_get_product, shopify_create_product, shopify_update_product (now also vendor/tags), shopify_delete_product, shopify_create_variant, shopify_update_variant, shopify_delete_variant, shopify_set_variant_price, shopify_adjust_inventory, shopify_set_inventory, shopify_add_product_image, shopify_delete_product_image, shopify_list_locations, shopify_list_orders, shopify_get_order, and shopify_list_customers are install-gated on this module (native bundle shopify) and run against the module API above, so the same product-management capabilities are available in the module UI and via agent tools. All create/update/delete/set tools are mutating: they change the live Shopify store, so the agent should confirm with the user before calling them. shopify_delete_product, shopify_delete_variant, and shopify_delete_product_image are additionally destructive and irreversible — archiving a product via shopify_update_product with status=archived is the reversible alternative to deletion. Images added via shopify_add_product_image are processed asynchronously by Shopify and may take a moment to appear. Connecting a store is intentionally not an agent tool — access tokens must not pass through chat; admins connect stores in the module UI. See Agent Tool Contracts for the full contracts.

Apple clients

Shopify ships as an iframe web module only. The iOS and macOS clients have no corresponding native module screen; stores are managed in the web app.