Network access
Functions are sandboxed by default — no network, no filesystem, no ambient time. Opt in to outbound HTTP per-function by settingnetwork_access: true on the manifest and listing the hosts you need
in allowed_hosts.
When to use it
Most functions should not need network access. The platform supplies cart, customer, metafields, locations, shop currency, and addresses inline — that covers 95% of real-world function logic. Reach for network access only when:- You need a real-time lookup that the merchant can’t pre-compute (live currency rate, live tax table, stock at a 3PL).
- You’re integrating with an external service whose data isn’t appropriate to mirror into metafields (fraud score, loyalty tier, carrier rate).
- You need to side-effect to your own backend (write an analytics event when a function fires — though prefer the host’s audit log when possible).
Manifest
network_access and allowed_hosts apply per function (not per app),
so a single app can have one function with network access and others
without.
Field reference
allowed_hosts entries are hostnames only — no scheme, no port, no
path, no wildcards. The install endpoint rejects malformed entries
with HTTP 400:
The validator regex requires at least one
. and each label to match
[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])? (case-insensitive). This is
intentional: an allowlist is not a regex — every host you want to
reach must be enumerated.
Install-time validation
The LaunchMyStore install endpoint (/api/apps/install-extensions) runs
the same validator as the dispatcher before persisting the manifest.
Errors block install with a clear message:
Runtime API
Your WASM module gets a single synchronous host import (exposed by the Javy runtime, not part of the WASI spec):urlPtr/urlLen point at the request URL in WASM memory; outPtr/outMaxLen
are the output buffer the response body is written into. The return value
is the number of bytes written (>= 0) on success, or a negative error code
(see below). There is no options pointer.
The bundled JS runtime will expose this as globalThis.fetch once the
shim lands — call it exactly as you would in Node:
Limits
These limits are platform-enforced — your function cannot raise them.
If you need a longer call, do the slow work async on your own service
and return a cached answer from a fast endpoint.
Return codes
lms_host.fetch_url returns an int32 to your WASM module. The bundled
runtime shim translates these to fetch errors / response objects:
-2 is the most common error in practice: a developer copy-pastes a
URL with a slightly different host (api.example.com vs
api.staging.example.com) and forgets to add the new host to
allowed_hosts. The error message logged on the function run row
includes the offending host so this is quick to debug.
Dispatch behaviour when a fetch is blocked
When a fetch fails with any of-1 through -4, the function still
runs to completion — only the specific fetch_url call returns
failure. Your function decides how to react:
throw from the function instead of returning, the dispatcher
treats it like any other WASM error: the result is discarded (no
output applied to the cart / checkout) and an error is logged to the
function run row. Other apps’ functions of the same type still run —
one bad app cannot take the whole pipeline offline.
The platform never auto-retries blocked or timed-out fetches. If you
need retries, build them into the function:
TLS / CORS notes
- TLS is recommended, not enforced. The runtime allows both
http://andhttps://URLs to allow-listed hosts (matching the Limits table above). Usehttps://for anything carrying credentials or customer data — plaintext requests are your responsibility. - No CORS. The host import is a server-to-server fetch initiated by
the WASM worker process — there is no browser, no preflight, no
Access-Control-Allow-Originto consider. Your endpoint can be locked down toOrigin: lms-functions.launchmystore.ioif you need to verify the caller. - No cookies. The runtime doesn’t carry merchant/customer cookies on outbound calls. Authenticate with an API key or Bearer token your app already has.
- No streaming. Response bodies are fully buffered (capped at 64 KB)
before the function sees them. There is no
ReadableStream.
Authenticating outbound calls
The function runtime doesn’t have a built-in secret store. You have two practical options:- Bake a key into the WASM bundle. Simplest for first-party apps — the key sits in your compiled module and lands on the merchant’s disk only as part of your signed bundle. Risk: anyone who pulls the WASM apart can read the key.
- Embed the key in a merchant-specific app metafield that the
function reads from
cart.metafields.app_<handle>.api_key.value. The merchant can rotate the key independently of the function binary.
Observability
Every outbound call is recorded on the function run’smeta object
(visible via the function runs API):
- Calls per function per day — to track usage.
- p50 / p95 outbound latency — to spot slowness before it shows up as checkout latency.
- Failure rate by error code — to catch new
-2host-allowlist errors after a deploy.
Common patterns
Real-time rate API
Geo-IP / fraud check
Loyalty tier lookup
Security considerations
allowed_hostsis an allowlist, not a regex. Every host you want to reach must be enumerated by exact match. This is intentional — a regex allows escalation by typo, an allowlist doesn’t.- Don’t include
localhostor private IP ranges. The validator rejects single-label hosts likelocalhost(it requires a dotted name), but it does not reject dotted IPv4 literals —127.0.0.1and RFC1918 addresses pass validation, and the runtime performs no private-IP / RFC1918 / link-local / SSRF filtering. It only enforces thehttp/httpsscheme and the exact host allow-list. So a private or link-local address that is allow-listed will be reachable from inside the function. Because of this, merchants should install only reviewed, trusted apps that request network access. - Treat your endpoint as untrusted-input territory. Anyone who can install your app on a merchant can trigger calls to your allowed hosts. Rate-limit by API key, log every call, and refuse requests with suspicious payloads.
- Cache aggressively on your side. Returning a cached response for
the same
(customer, cart)tuple keeps your function fast and your upstream costs predictable. - Rotate API keys regularly. Use the metafield-based pattern (see Authenticating outbound calls) so rotation doesn’t require a WASM rebuild.
Best practices
- Short timeouts on your side too. Your endpoint should fail in <500 ms 99% of the time. The platform’s 1500 ms hard timeout is a safety net, not a target.
- Return structured errors. A 200 with
{ "ok": false, "reason": ... }is faster than a 5xx because the runtime doesn’t have to retry. - Cache aggressively. Each cart-verification and order-placement call can trigger function dispatch — if your endpoint is slow or rate-limited you’ll cause cart latency that shows up in conversion metrics.
- Keep allow-lists tight. A single misconfigured host can leak data if your function reads sensitive merchandise attributes.
- Decide fail-open vs fail-closed deliberately. On
-3(timeout), decide whether your function should return a no-op output (fail open — preserve cart usability) or surface an error (fail closed — block the bad state). Most discount/shipping functions should fail open; order-validation and fulfillment-constraints functions might fail closed. - Log the request id. Pass the
cart.idorcustomer.idas a header so your upstream logs can be correlated with the function run in the developer portal.
See also
- Input field selection — the other manifest-level field that shapes function runtime behaviour.
- Discount functions, Shipping rate functions, Order validation — common consumers of network access.
- App metafields — for storing per-merchant API keys outside the WASM bundle.