Skip to main content

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’s app.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.
Other surfaces declared in 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 WASM function types; a tenth declarative fulfillment_location_rule is accepted at install but not yet dispatched at runtime) — declarative modules that fire on cart verification / order placement.

How It Works

  1. Declare extensions in your app’s app.json (manifest).
  2. Install them into a merchant’s store via POST /api/apps/install-extensions — either inline (extensions: {…}) or by referencing the marketplace catalog (fromCatalog: true).
  3. The platform writes the files under extensions/{domainSlug}/{appHandle}/.
  4. 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:
The host reads from this bundle through a server-side cache. After a manifest change, re-run the install call (which refreshes the cache) or wait up to 60 seconds for the manifest cache to expire.
This directory is for extension artifacts only.aqua templates, schemas, compiled .wasm functions, static CSS/JS assets. Do not write per-store runtime data (config, state, logs, counters) to disk. App runtime data lives in metafields. See the next section.

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, …).

Accessing metafields

Your app reads and writes metafields through the OAuth Metafields API — /api/v1/metafields.json — authenticated with your Bearer access token and the read_metafields / write_metafields scopes.

Namespace convention

Use one namespace per app, formatted as app_<handle_with_underscores>: The 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: Compound config is fine — store the whole blob as a single 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 are stored as per-handle schema files under 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 by POST /api/apps/install-extensions. This is a server-to-server endpoint (no CORS): inline uploads require the platform’s internal API key and are performed by the backend as part of the app install flow — not called directly from a browser with a merchant token. The only browser-accepted path is Mode B (fromCatalog: true) with a merchant Bearer whose store matches domainSlug.

Mode A — inline upload (internal, server-to-server)

For platform-side deploys that deliver arbitrary extension files:
The handler writes:
  • blocks/{handle}.aqua + blocks/{handle}.schema.json
  • snippets/{handle}.aqua
  • assets/{filename} (fetched from URL)
  • admin-actions/{handle}.schema.json
  • print-actions/{handle}.schema.json
  • email-templates/{event}.json

Mode B — fromCatalog

For apps already published to the marketplace catalog:
Copies app.json, blocks/, snippets/, functions/ from the catalog into the per-merchant directory. This is the one path that accepts a merchant Bearer instead of the internal API key — the backend verifies the merchant owns the domainSlug store before copying.
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

Removes the entire 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:
Inside the .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 — Homepage
  • product — Product pages
  • collection — Collection pages
  • cart — Cart page
  • article — Blog articles
  • page — Custom pages
  • blog — Blog listing
  • search — Search results
schema.target is a passthrough hint (not enforced at install). Full list: Storefront Blocks.

Checkout UI (currently wired slots)

  • checkout-contact-after
  • checkout-shipping-after
  • checkout-shipping-method-before
  • checkout-payment-before
  • checkout-payment-after
  • checkout-order-summary-before
  • checkout-order-summary-after
  • purchase.checkout.cart-line-list.render-after
  • purchase.checkout.reductions.render-after
  • purchase.checkout.actions.render-before
  • purchase.thank-you.block.render
  • purchase.order-status.block.render
  • purchase.thank-you.cart-line-list.render-after
  • purchase.order-status.cart-line-list.render-after
Full list: Checkout UI Extensions.

Admin blocks

  • product.details.block
  • order.details.block
  • customer.details.block
  • collection.details.block
  • discount.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)
Full event list: Email Templates.

See Also