App Proxy
An App Proxy lets your app serve dynamic content — tracking widgets, account dashboards, lookup forms, AJAX endpoints — from the merchant’s own storefront domain. A buyer requestshttps://store-name.launchmystore.io/apps/{your-app}/api/tracking?awb=…,
the platform signs and forwards the request to your server, and your
response is streamed back as if it came from the merchant’s domain.
App Proxy lets you ship customer-facing surfaces that:
- Read first-party cookies (cart, session, locale) — no CORS, no third-party cookie blocking.
- Render Aqua templates with full theme context (
shop,customer,cart, themesettings) so your app blends visually with the store. - Look authentic to buyers — no random
*.your-domain.comiframe.
When to use App Proxy
If you only need a static script tag, use
App Scripts instead. If you need to render UI
inside checkout, use Checkout UI.
How it works
- The buyer’s browser hits
/apps/{your-handle}/{anything}on the merchant’s domain. - The storefront’s middleware rewrites the URL to the internal proxy
handler and looks up your manifest’s
extensions.appProxyblock to find the upstream URL. - The platform signs the outbound query with HMAC-SHA256 using your
app’s
clientSecretand appends five params:shop,logged_in_customer_id,path_prefix,timestamp,signature. - Your server verifies the signature and runs the request.
- The response streams back to the buyer. If your response’s
Content-Typeisapplication/liquid, the platform first renders it through the merchant’s active theme with global objects (shop,customer,cart, etc.).
Manifest
Declare a proxy inapp.json under extensions.appProxy:
Only one App Proxy per app is supported. If you need to serve multiple
namespaces (tracking, returns, lookup), route them under sub-paths of the
same
url (e.g. /api/tracking, /api/returns).URL mapping
With the manifest above and a buyer request tohttps://acme-store.launchmystore.io/apps/shiprocket/api/tracking?awb=ABC123:
The path you read on your server is
subPath only — the
/apps/{handle} prefix is consumed by the platform.
Signature (HMAC-SHA256)
Every proxied request is signed so your server can trust theshop value
and reject anyone calling your URL directly.
Signed params (always forwarded):
Any caller-supplied query param (e.g.
?awb=ABC123) is also included
in the signature, so a tampered URL fails verification.
Canonical string
The signature is computed over a canonical string built by:- Take every query param except
signature. - Sort the keys alphabetically (ASCII).
- Join as
key=valuewith no separator (no&, no,). - Array values are joined by
,before signing.
logged_in_customer_id
is always present — empty here because no customer is logged in):
key=value pairs joined with
no separator, hashed with HMAC-SHA256 under your app’s clientSecret.
Verifying in your app
If your app uses the@launchmystore/apps-shared package, drop in the
ready-made middleware:
Or verify by hand
Any HTTP framework / language. Pseudo-code:Response types
Your app server can respond with either a regular content type orapplication/liquid for server-rendered theme HTML.
JSON / HTML / text — passthrough
The body streams back to the browser verbatim. Use this for AJAX endpoints called from your storefront block:application/liquid — server-rendered theme HTML
Respond with Content-Type: application/liquid and the platform renders
your body through the merchant’s active theme before sending it to the
browser. Your response can use any Aqua object,
filter, or tag:
https://store.launchmystore.io/apps/loyalty/dashboard that inherits the
merchant’s header, footer, fonts, and colours.
application/liquid responses are rendered with the storefront’s full
global objects — shop, customer, cart,
settings, linklists, etc. No need to pass them through; they’re
already in scope.Common patterns
1. Order tracking widget
A storefront snippet on/orders/{id}
calls your proxy endpoint:
2. Customer-facing app page
A merchant-link in the storefront header points to/apps/loyalty/dashboard. Your proxy returns application/liquid and
the platform themes it.
3. Same-origin AJAX from a storefront block
A reviews app’s block JS posts new reviews to/apps/reviews/api/submit. Cookies sent automatically — your server
identifies the customer via req.appProxy.shop + the platform session
cookie.
Caching
The platform forwards your response’sCache-Control header. To cache
proxied responses at the edge, set:
Cache-Control: no-store (the default) are not
cached.
Security checklist
- ✅ Always verify the signature in production. The
optional: truedev mode skips verification — never ship that to prod. - ✅ Check
timestampfreshness (≤ 5 minutes). Prevents replay attacks if a logged URL leaks. - ✅ Use
req.appProxy.shop(verified) — never trust?shop=from a caller without verification. - ✅ Don’t echo unsanitized user input into
application/liquidresponses. Liquid output is auto-escaped, but{{ x }}inside an attribute can still break ifxcontains quotes. Use theescapefilter for paranoia. - ❌ Don’t expose write endpoints (anything that mutates merchant data) on the proxy unless you also require a signed-in customer session — the proxy only proves the request originated from a merchant storefront, not which buyer made it.
Development & testing
For local development, your app server typically runs onhttp://localhost:PORT. Point your dev app’s extensions.appProxy.url
at it via a tunneling tool (ngrok, cloudflared, tailscale funnel) so the
platform can reach it from the cloud.
signAppProxyUrl():
Limits
A timeout or 5xx from your server returns
502 Bad Gateway to the buyer.
The buyer’s browser sees that response; the storefront does not retry
on your behalf — handle retries in your app.
See also
- Storefront Snippets — call your proxy endpoints from theme-author Liquid.
- App Scripts — when you only need a
<script>tag on every page (no signed server endpoint). - Webhooks — server-to-server events from the platform to your app (the inbound direction; App Proxy is the buyer-to-app direction).
- App Bridge: Session Tokens — equivalent trust mechanism for admin iframes.