Plugins: build your own features
Plugins are small JavaScript programs that add features to your store: reports, automations, storefront widgets, integrations. You can write them yourself in the built-in code editor (the same editor as VS Code, with autocompletion for the sp API), ask your AI ("write a ShopPress plugin that…"), upload a plugin file, or install one from the gallery or the shared library. Plugins are part of the Builder and Studio plans and the Plugins module (switch it on under Modules).
They run in a sealed sandbox (QuickJS, WebAssembly):
- only your store; no other store's data is reachable
- no file system, no Node.js, no direct internet; outside services only through
sp.http.fetchto hosts you approved - limits on time, memory and calls
- deleting data needs your explicit permission
Plugins run at your own risk; test them first (Test = dry run).
The shape of a plugin
plugin = {
name: "Low stock alert",
description: "Hourly check for low stock.",
scopes: ["read_inventory"], // extra Shopify permissions (optional)
http: ["hooks.slack.com"], // outside hosts it may call (optional)
settings: { // a settings form is built from this (optional)
threshold: { type: "number", label: "Alert at or below", default: 3 },
webhook: { type: "secret", label: "Slack webhook URL" },
},
tools: {
check_stock: {
description: "Variants at or below the threshold.",
input: { limit: "number?" }, // string | number | boolean, add ? for optional
schedule: 60, // run every 60 minutes (optional, minimum 15)
on: ["inventory_levels/update"], // run on Shopify events (optional)
public: false, // callable from the storefront (optional, read-only)
flow: true, // offered in Shopify Flow (optional)
run(input, sp) {
const r = sp.shopify.graphql(`{ shop { name } }`);
return { shop: r.shop.name };
},
},
},
};
run may be normal or async; return any JSON. Tool names use lowercase letters, digits and _.
The sp toolbox
| API | What it does |
|---|---|
sp.shopify.graphql(query, variables) | Shopify Admin GraphQL for your store, within granted permissions. Retries automatically when Shopify is busy. |
sp.kv.get(key) / set(key, value) / delete(key) / list() | Small JSON storage for this plugin (1,000 keys, 100 KB each). |
sp.settings | Values from the plugin's settings form (secrets excluded). |
sp.secrets.get(name) | A secret setting (stored encrypted, never shown again). |
sp.http.fetch(url, { method, headers, body }) | Call an outside HTTPS service on an approved host. Returns { status, ok, headers, text(), json() }. |
sp.theme.setBlock(key, html) | Save HTML for the ShopPress plugin content theme block with that key. Scripts are removed. |
sp.flow.trigger(event, details) | Start Shopify Flow workflows that use the ShopPress plugin event trigger. |
sp.event | What started the run: a Shopify event ({ topic, data }), a Flow call or a storefront request. |
sp.trigger | manual, mcp, schedule, flow, storefront, or the event topic. |
sp.shop | { name, domain, currency, timezone, plan, plus, devStore }. Use sp.shop.plus for Shopify Plus-only features. |
sp.utils | csv.parse/stringify, date.now/addDays/daysBetween/format, chunk, slug, escapeHtml. |
sp.log(...) | Lines shown in the plugin's run log. |
Permissions (scopes)
ShopPress installs with product and file access only. A plugin lists extra permissions in scopes; after saving, the Plugins page shows Grant permissions, which opens Shopify's permission window. Until they are granted, the plugin won't run and says which ones are missing.
Available: inventory, locations, orders, draft orders, order edits, fulfillments, returns, customers, content (pages, blogs), navigation, metaobjects, discounts, price rules, shipping, markets, translations, locales, publications, themes (read), reports, product listings, gift cards, and B2B companies (Plus).
Orders and customers include personal data. Shopify allows that data only after the app's data-protection review.
Triggers: when a plugin runs
| Trigger | How |
|---|---|
| By hand | Plugins page → Run (or Test for a dry run) |
| By your AI | MCP plugin_run or the tool plugin__<slug>__<tool> |
| On a schedule | schedule: 60 (minutes, 15 to 10,080) |
| On Shopify events | on: ["products/create", "orders/paid", …], the event is in sp.event |
| From Shopify Flow | flow: true, then use the Run ShopPress plugin action in Flow |
| From the storefront | public: true, then use the ShopPress live plugin theme block |
Supported events: products/create, products/update, products/delete, collections/create, collections/update, inventory_levels/update, orders/create, orders/paid, orders/fulfilled, orders/cancelled, orders/updated, refunds/create, draft_orders/create, fulfillments/create, customers/create, customers/update.
Automatic runs (schedule, events, Flow) that fail 5 times in a row pause the plugin. The Plugins page shows why; switch it back on after fixing.
Show plugin output in your theme
See Theme blocks. In short: sp.theme.setBlock("my_banner", html) plus the ShopPress plugin content block with key my_banner. For live, per-product content, mark a tool public: true and add the ShopPress live plugin block.
Lifecycle: install, verify, activate
Like WordPress plugins, every plugin is installed, then activated or deactivated:
- Save, upload or install. Upload a
.jsfile or a.shoppress-plugin.jsonpackage (made with Export). - Safe-mode verification runs automatically. It checks the manifest and limits, runs the plugin's
tests, and dry-runs every tool that needs no input. Missing permissions or unapproved hosts are warnings; crashes are failures. - A new plugin is activated only if verification passes. A new version that fails verification deactivates the plugin, so broken code never runs automatically. The report shows each check.
- Deactivate keeps the plugin and its data. Delete removes it with its stored data and logs (export it first if you may want it back).
Safe mode (top of the Plugins page, or MCP plugin_safe_mode) stops all plugins at once: no runs, schedules, events or storefront widgets. Use it if anything looks wrong, then switch plugins back on one by one.
Tests
Add tests to a plugin. They run in dry-run mode (Run tests, MCP plugin_tests_run, and during verification):
tests: {
returns_a_list: { tool: "products_without_images", input: { limit: 5 }, check: (result) => Array.isArray(result) || "expected a list" },
},
check returns true to pass, or a message explaining what went wrong.
Widgets: HTML, CSS and JavaScript for your storefront
widgets: {
countdown: {
label: "Sale countdown",
html: '<div class="sp-countdown" data-end="2026-12-24T23:59:00Z"></div>',
css: ".sp-countdown{font-weight:700;padding:8px;background:#111;color:#fff;text-align:center}",
js: "document.querySelectorAll('.sp-countdown').forEach(function(el){ /* … */ })",
},
},
Add a widget with the ShopPress widget theme block (plugin slug and widget name), or anywhere in your theme with <div data-shoppress-widget="<plugin>/<widget>"></div> when the ShopPress app embed is on. That includes Custom Liquid sections, where you can mix Liquid and widgets. HTML is cleaned of scripts. The widget's JavaScript runs only after you tick "Allow this plugin's widget JavaScript", because it runs on your live store.
Discounts with ShopPress's Shopify Function
ShopPress includes a rule discount (a Shopify Function). It runs inside Shopify's checkout on every plan. Plugins create automatic discounts that use it and set their rules in the discount's $app.rules metafield:
{ "rules": [
{ "type": "product", "vendor": "Trixie", "minQuantity": 2, "percent": 10, "message": "10% off Trixie (2+)" },
{ "type": "product", "productType": "Wet Cat Food", "amount": 1.5, "message": "£1.50 off" },
{ "type": "order", "minSubtotal": 50, "percent": 5, "message": "5% off orders over 50" }
] }
Product rules can use vendor, productType, productIds and minQuantity, with percent or amount (add eachItem: true to take the amount off each item). Order rules use minSubtotal. Every matching line gets its product discount. Install Discount rules from the gallery for a ready-made plugin: set the rules in its settings and run apply_rules. It needs the discount permission.
Share, back up, get help
- Library: publish a verified plugin (choose a category and author name). ShopPress reviews it before others can install it. Installed library plugins show updates when the author publishes a new version.
- Backups: Backups hold all plugins, settings and stored data; restore any time.
- Support access: "Give access for 72 hours" lets ShopPress support read your plugins and run logs (never secrets) to help fix a problem. End it any time.
Test, run, versions
- Test runs a dry run. Shopify reads work, but no changes, no kv writes and only GET requests to outside services.
- Run is for real. Every run is logged (input, time, result or error, your
sp.loglines). - Every save is a new version; Roll back puts an earlier one back (the last 20 are kept).
- Turn off is the kill switch: the plugin stops running and its tools disappear from MCP.
Limits
| Limit | Value |
|---|---|
| Run time | 20 seconds |
| Memory | 32 MB |
| Shopify calls per run | 50 |
| Outside requests per run | 20 (10 s, 1 MB each) |
| Code size (incl. widgets) | 250 KB (widgets: 150 KB) |
| Result size | 256 KB |
| Plugins per store | Builder 25 · Studio 100 |
| Runs per month | Builder 50,000 · Studio 300,000 |
Errors
Plugin errors never break ShopPress or your store. The run log shows the message. Common ones:
| Message | Fix |
|---|---|
| needs these Shopify permissions first | Grant permissions on the Plugins page |
| … is not approved for this plugin | Add the host to plugin.http, then approve it |
| Dry run: mutations are not sent | Use Run instead of Test |
| This mutation deletes or removes data | Run with Allow deleting after checking |
| More than 50 Shopify calls | Process fewer items per run (use schedule and sp.kv to continue) |
| interrupted | The run took over 20 seconds; do less per run |
Gallery
Install a ready-made plugin in one click, then change it freely:
| Plugin | What it does |
|---|---|
| Products without images | Lists active products with no image |
| Auto-tag new products | Tags new products with vendor and type |
| SEO meta filler | Fills empty SEO titles and descriptions |
| Low stock alert | Hourly check, starts a Flow workflow for new low-stock items |
| Free shipping bar | A banner for your theme (theme block) |
| More from this brand | Live product-page block with the brand's other products |
| Tag big orders | Tags paid orders over an amount (order permission) |
| Discount rules | Automatic discounts from rules, using ShopPress's Shopify Function |
| B2B companies report | CSV of B2B companies (Shopify Plus) |