Extensions
Extensions let your app inject UI or behaviour into a merchant’s store. Each extension type maps to a specific surface — storefront, checkout, post-purchase, admin — and is declared in your app’sapp.json.
For declarative business logic (cart transforms, discounts, shipping
rates, validation), see Functions — those run
server-side and return JSON, not UI.
Extension Types
Storefront Blocks
Liquid blocks merchants drop into theme sections (e.g. product page,
homepage).
Checkout UI
Sandboxed iframe rendered at a named slot inside the checkout.
Talks back to the host via App Bridge.
Post-Purchase
Iframe rendered on the order status (thank-you) page right after checkout. Smaller wired-action set
than checkout UI.
Admin Blocks
Iframe embedded on admin resource detail pages (product, order,
customer, etc.).
Admin Actions
Button on an admin resource page that opens an iframe modal.
Admin Print Actions
Generate printable templates for orders, invoices, packing slips.
Email Templates
Override the merchant’s transactional emails for named events.
App Scripts
JavaScript files auto-injected on every storefront page — pixels,
chat widgets, floating buttons. No theme edits.
Web Pixels
Sandboxed customer-event listeners (
page_viewed, cart_updated,
checkout_completed).Customer Account
Iframe blocks on the new customer-account dashboard, order status,
profile, and order list pages.
Order Routing
Rules that pick the fulfillment location for an order at checkout.
App Proxy
Serve dynamic content from your app server on the merchant’s own
domain — tracking widgets, account pages, AJAX endpoints.
MCP Provider
Register MCP tools so AI assistants can drive your app capabilities
through the merchant’s MCP server.
app.json:
- Storefront snippets — reusable
Liquid includes (
{% render 'foo' %}). - Storefront embeds —
overlay/floating scripts injected at
<head>or before</body>per the merchant’s app-embed toggle. - Functions (nine types plus the
fulfillment_location_ruledispatch wrapper) — declarative WASM modules that fire on cart verification / order placement.
How It Works
- Declare extensions in your app’s
app.json(manifest). - Install them into a merchant’s store via
POST /api/apps/install-extensions— either inline (extensions: {…}) or by referencing the marketplace catalog (fromCatalog: true). - The platform writes the files under
extensions/{domainSlug}/{appHandle}/. - The matching storefront / checkout / admin surface picks them up via
/api/apps/extensions,/api/apps/checkout-extensions,/api/apps/admin-extensions, etc.
Extension Directory Layout
After install, every app lives at one path on disk:Where to store app data
Apps do not write to the platform filesystem for runtime state. Every piece of merchant- or customer-scoped data your app needs to read back later — install config, feature toggles, per-product counters, per-customer state, event logs — is persisted as a metafield. There is no app-owned database on the platform, so apps push public summaries (and private blobs) into metafields scoped to a resource (shop, product, customer, order, …).
Two ways to access metafields
| Caller | Endpoint | Auth |
|---|---|---|
| Third-party OAuth app | /api/v1/metafields.json | Bearer access token + read_metafields / write_metafields scopes |
| First-party / internal helper | /metafields/internal/* | x-internal-api-key shared secret (server-side only) |
domainSlug.
Namespace convention
Use one namespace per app, formatted asapp_<handle_with_underscores>:
| App handle | Metafield namespace |
|---|---|
channels-hub | app_channels_hub |
foundry-reviews | app_foundry_reviews |
restock-alerts | app_restock_alerts |
app_* prefix is the reserved per-app namespace convention on the
platform; it keeps your app’s data isolated from custom (merchant-managed)
and from other apps’ namespaces.
A few apps additionally publish a public summary under a second,
shorter namespace so themes can read it via Aqua:
Owner-type scoping
Pick the owner that matches the scope of the data:| Data scope | ownerType | ownerId |
|---|---|---|
| Per-store app config / install settings | shop | (resolved from store) |
| Per-product counter / summary | product | product UUID |
| Per-variant inventory note | variant | variant UUID |
| Per-customer state (subscription, wishlist) | customer | customer UUID |
| Per-order metadata | order | order UUID |
type: "json" metafield under one key like config. The metafield
validator decodes JSON on read so consumers get back an object.
Example: write install config
Example: append to a per-product log
For log-shaped data (click logs, recent reviews, subscriber lists), read the existing JSON array, append, and write back. Cap the array length to avoid unbounded growth — there is no platform-side trimming.Cache invalidation
Every metafield write fires automatic cache invalidation — both the cached copy of the owning resource and any cached page HTML that rendered it. The next storefront render sees the new value. You do not need to manually invalidate anything.What not to store as metafields
- Large blobs (raw images, video, full PDF documents). Use the file reference type instead — upload to R2 / CDN and store the URL.
- High-churn counters that update on every page view (
view_count). Batch into hourly aggregates or use a backend counter service. - Cross-store data. Metafields are scoped per store; if you need global app state, run your own backend.
Manifest
app.json declares all extensions in one place. Keys under extensions
are camelCase:
admin-actions/ and print-actions/ — not as inline arrays in
app.json. The install endpoint writes them; see
Admin Actions and
Admin Print Actions.
Installing Extensions
Two install modes are supported byPOST /api/apps/install-extensions:
Mode A — inline upload
For third-party apps that deliver extension files via API:blocks/{handle}.aqua+blocks/{handle}.schema.jsonsnippets/{handle}.aquaassets/{filename}(fetched from URL)admin-actions/{handle}.schema.jsonprint-actions/{handle}.schema.jsonemail-templates/{event}.json
Mode B — fromCatalog
For apps already published to the marketplace catalog:app.json, blocks/, snippets/, functions/ from the catalog
into the per-merchant directory.
Email templates are persisted by the platform during install.
The file written here is a read-only preview — the authoritative copy
is the stored template record.
Uninstalling
extensions/{domainSlug}/{appHandle}/
directory. Asset cache + manifest cache are invalidated automatically.
Storefront Block Schemas
Storefront blocks define settings the merchant can configure in the theme editor:.aqua template, settings are available as block.settings.*
(or block.<id> — both work; the Aqua engine auto-resolves to .settings.<id>).
Targets — Quick Reference
Storefront blocks
index— Homepageproduct— Product pagescollection— Collection pagescart— Cart pagearticle— Blog articlespage— Custom pages
Checkout UI (currently wired slots)
checkout-contact-aftercheckout-shipping-aftercheckout-shipping-method-beforecheckout-payment-beforecheckout-payment-aftercheckout-order-summary-beforecheckout-order-summary-afterpurchase.checkout.cart-line-list.render-afterpurchase.checkout.reductions.render-afterpurchase.checkout.actions.render-beforepurchase.thank-you.block.renderpurchase.order-status.block.renderpurchase.thank-you.cart-line-list.render-afterpurchase.order-status.cart-line-list.render-after
Admin blocks
product.details.blockorder.details.blockcustomer.details.blockcollection.details.blockdiscount.details.block- 26 total — see Admin Blocks.
Admin actions
admin.order-details.action.render(more shipping as resource pages wire up)
Admin print actions
admin.order-details.print.render
Email templates
Replace built-in transactional emails for named events:order_confirmation(MVP-wired)
See Also
- App Bridge Overview — postMessage contract used by checkout, post-purchase, and admin iframes.
- Functions Overview — declarative server-side business logic, not UI.