Functions
Functions let your app run custom business logic at well-defined points in the cart → checkout → order-placement pipeline. Unlike Extensions which add UI, functions return declarative JSON — operations, rates, discounts, errors — that the platform applies to the order on your behalf. Functions are sandboxed Javy dynamic-mode WASM modules. You compile them on your own machine withlms function build (which calls Javy /
QuickJS) and upload the pre-built .wasm to the platform — the server
validates the artifact and runs it during the cart-verify and
order-placement phases. See
CLI Setup → Local Function Compilation
for the full build/test/deploy flow.
Function Types
Cart Transform
Update unit prices, merge lines into bundles, or expand a single line
into multiple. Surfaces as
cartTransformDiscount + per-entry
cartTransformDiscounts[].Discount
Apply order / line-item / shipping discounts with app-supplied
labels. Surfaces as
appDiscount + shippingDiscount with per-entry
arrays.Shipping Rate
Add custom shipping options alongside the merchant’s zones. Customer
can pick from either.
Payment Customization
Hide, rename, or reorder payment methods. Enforced server-side at
order placement.
Delivery Customization
Hide, rename, or reorder the merchant’s shipping zones. Enforced
server-side at order placement.
Order Validation
Block the order entirely if it fails your rules — quantity caps,
geo restrictions, compliance checks.
Fulfillment Constraints
Restrict which fulfillment locations can ship which lines. Blocks
order placement if no location can fulfill a constrained line.
Local Pickup Options
Offer in-store / curbside pickup at merchant-owned locations as a
delivery option in checkout.
Pickup Point Options
Offer carrier-operated parcel lockers and partner pickup points
(DHL, UPS, InPost) as a delivery option.
fulfillment_location_rule as the unified entry for
order routing rules — JSON matchers that pick a fulfillment location
at order placement. Rules don’t run WASM; see
Order Routing Rules for the manifest
format and match operators.
Cross-cutting capabilities
Input field selection
Declare
inputFields on a manifest entry to project the input down
to only the REST fields your function needs. Faster dispatch, smaller
WASM payloads.Network access
Opt in with
network_access: true plus an allowed_hosts allow-list
to call external HTTP services from inside your function.Function Manifest
Declare functions in your app’sapp.json under extensions.functions[]:
| Field | Required | Description |
|---|---|---|
type | yes | One of cart_transform, discount, shipping_rate, payment_customization, delivery_customization, order_validation, fulfillment_constraints, local_pickup_options, pickup_point_options, or fulfillment_location_rule (declarative — see Order Routing). |
handle | yes | Unique handle within the app. |
entrypoint | yes | Path to the compiled WASM module, relative to the install dir. |
title | no | Display label in the merchant’s app admin. |
appId | no | App id (defaults to the install folder name). |
inputFields | no | REST field selection tree — see Input fields. |
network_access | no | true to enable outbound HTTP — see Network access. |
allowed_hosts | no | Required when network_access: true. Hostname allow-list. |
Execution Points
| Function Type | Cart verification | Order placement | What it changes |
|---|---|---|---|
cart_transform | ✓ | ✓ | Per-line price/title + bundle totals |
discount | ✓ | ✓ | Order / line-item / shipping discount rows |
shipping_rate | ✓ | ✓ | Adds custom shipping / local-pickup / pickup-point options |
payment_customization | ✓ | ✓ (enforced) | Filters/relabels payment methods; rejects hidden selections |
delivery_customization | ✓ | ✓ (enforced) | Filters/relabels shipping zones; rejects hidden selections |
order_validation | ✓ | Rejects the order with an error message | |
fulfillment_constraints | ✓ | Blocks lines with no eligible fulfillment location | |
local_pickup_options | reserved | reserved | Adds merchant-owned pickup locations as a delivery option |
pickup_point_options | reserved | reserved | Adds carrier parcel-locker pickup points as a delivery option |
order_validation and
fulfillment_constraints are final-gate checks at order placement only.
local_pickup_options and pickup_point_options manifests are honored
today; the checkout consumer ships in the delivery-options
release.
Common Input Shape
Every function receives acart envelope with the same shape:
destination,
paymentMethods, shippingAddress, customer, deliveryOptions,
discountCodes) — see each type’s page.
All monetary values are in display currency units, not cents.
price: 19.99 means ₹19.99 / $19.99. Same convention for everything
your function returns.Output Format
Each function type has its own output. Browse the dedicated pages for the full contract — quick reference:| Type | Returns |
|---|---|
cart_transform | { operations: [update | merge | expand] } |
discount | { discounts: [{ title, value, valueType, target, ... }] } |
shipping_rate | { rates: [{ name, price, description?, kind?, pickup? }] } |
payment_customization | { operations: [hide | rename | reorder] } |
delivery_customization | { operations: [hide | rename | reorder] } |
order_validation | { errors: [{ message, target?, lineId?, code? }] } |
fulfillment_constraints | { constraints: [{ lineId, allowedLocationIds, message? }] } |
local_pickup_options | { pickupOptions: [{ id, title, address, hours?, distance?, fee? }] } |
pickup_point_options | { pickupOptions: [{ id, title, address, hours?, distance?, fee? }] } |
Persistence
For monetary outputs (cart_transform deltas, discount entries) the
platform persists both:
- Summed scalars on
additionalFields(e.g.appDiscount,shippingDiscount,cartTransformDiscount) — backwards compatible. - Per-entry arrays on
additionalFields(e.g.appDiscounts[],shippingDiscounts[],cartTransformDiscounts[]) — used to render one row per app-supplied label on cart, checkout, order page, and email receipts.
appId so the merchant can attribute
revenue back to your app in reporting.
Testing
- Local sandbox —
lms function test -f src/function.js -t <type> --input-file fixtures/cart.jsoncompiles your source locally and runs it against the hosted sandbox without persisting anything. The fastest way to iterate on cart-transform, shipping, payment, delivery, and order-validation logic. - Pre-built artifact —
lms function test --wasm dist/function.wasmuploads a.wasmyou’ve already compiled (useful in CI where you build once and reuse). - Live storefront — install your app into the Apps developer portal, then place a real order. The platform runs the same dispatch for both the cart preview and final order placement.
Rate Limits & Constraints
-
Execution timeout: per-function-type defaults (override via env):
All timeouts are clamped to a 5 s hard cap server-side.
Function Type Default timeout discount500 ms payment_customization500 ms cart_transform1 s order_validation1 s delivery_customization1 s shipping_rate2 s - Memory limit: 128 MB per execution.
- Source size cap: 64 KB of JavaScript.
- WASM size cap: 256 KB compiled.
-
Output size cap: 20 KB of JSON written to stdout. Functions that emit
more are dropped per their
fallbackpolicy — the cart still ships, but with no effect from that function on this request. -
Failure mode: a function that times out or throws is dropped for
that request. Other active functions in the same dispatch (subject to
the per-shop caps below) still run. The customer never sees the error
directly — it’s recorded in the function’s execution log, visible via
lms function logs <handle>.
WASM runtime limits. Functions ship as Javy dynamic-mode WASM
modules with a 256 KB compiled-WASM cap and a shared QuickJS provider
loaded once per worker. The WASM source patterns and bundle-size
constraints work for any standard Javy or AssemblyScript build.
Why 256 KB is enough
Functions compile in Javy dynamic mode: your JavaScript is bundled as a thin stub (typically 1–4 KB) that imports the shared QuickJS runtime (“provider”) at execution time. The provider is loaded once per worker and reused across every function, so each compiled function only carries your code — not a copy of the runtime. For comparison: a 500-line discount function compiles to roughly 3 KB, two orders of magnitude under the cap. If your function approaches 256 KB you’re probably bundling unnecessary dependencies — strip them out rather than asking for a higher cap.Active-Install Caps Per Shop
A store can have at most N active apps contributing functions of the same type. The cap is enforced at install time — both merchant install (/apps/:appId/install) and OAuth token exchange (/apps/oauth/token)
reject the install with HTTP 409 if it would push the store over the cap.
| Function Type | Max active per shop |
|---|---|
cart_transform | 1 |
payment_customization | 5 |
delivery_customization | 5 |
order_validation | 5 |
shipping_rate | 5 |
discount | 25 |
An app that declares functions across multiple types counts once per type
toward the relevant cap. E.g. an app shipping one
cart_transform and one
discount function consumes one slot of each.Best Practices
Keep functions fast
Keep functions fast
Cart / checkout latency is conversion-critical. Time-bounded
work only — no carrier-API calls without aggressive caching.
Make outputs idempotent
Make outputs idempotent
Functions re-run on every cart change. Derive your output from the
current input + config, not from cached deltas — otherwise you’ll
double-apply discounts or operations.
Always pass through `appId` info via `message`/`title`
Always pass through `appId` info via `message`/`title`
Customer-facing labels (
message, title, rate name) become
the visible row in cart, checkout, and the order page. Use clear,
branded text — “VIP: 15% off” beats “Discount applied”.Version with the app
Version with the app
App versioning + rollback is wired via
/apps/developer/:appId/versions/*. Test new function logic on a
staged version before promoting to production.