Skip to main content

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 with lms 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.
In addition to the nine WASM types above, the dispatcher also accepts the declarative type 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’s app.json under extensions.functions[]:
{
  "handle": "my-app",
  "name": "My App",
  "version": "1.0.0",
  "extensions": {
    "functions": [
      {
        "type": "cart_transform",
        "handle": "vip-pricing",
        "title": "VIP per-line pricing",
        "entrypoint": "dist/cart-transform.wasm"
      },
      {
        "type": "shipping_rate",
        "handle": "weekend-pickup",
        "title": "Weekend Pickup",
        "entrypoint": "dist/shipping-rate.wasm"
      }
    ]
  }
}
FieldRequiredDescription
typeyesOne 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).
handleyesUnique handle within the app.
entrypointyesPath to the compiled WASM module, relative to the install dir.
titlenoDisplay label in the merchant’s app admin.
appIdnoApp id (defaults to the install folder name).
inputFieldsnoREST field selection tree — see Input fields.
network_accessnotrue to enable outbound HTTP — see Network access.
allowed_hostsnoRequired when network_access: true. Hostname allow-list.

Execution Points

Function TypeCart verificationOrder placementWhat it changes
cart_transformPer-line price/title + bundle totals
discountOrder / line-item / shipping discount rows
shipping_rateAdds 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_validationRejects the order with an error message
fulfillment_constraintsBlocks lines with no eligible fulfillment location
local_pickup_optionsreservedreservedAdds merchant-owned pickup locations as a delivery option
pickup_point_optionsreservedreservedAdds carrier parcel-locker pickup points as a delivery option
Functions that affect cart-time pricing or option lists fire on cart verification (cart/checkout preview) for parity. 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 a cart envelope with the same shape:
interface CartInput {
  items: Array<{
    id: string;             // lineItemId
    variantId: string;
    productId: string;
    title: string;
    quantity: number;
    price: number;          // Effective unit price (display currency)
    originalPrice: number;  // Compare-at unit price
  }>;
  totalPrice: number;       // Subtotal in display currency
  itemCount: number;        // Sum of quantities
  currency: string;         // e.g. "INR", "USD"
}
Different function types receive different extras (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:
TypeReturns
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.
Every entry is stamped with appId so the merchant can attribute revenue back to your app in reporting.

Testing

  1. Local sandboxlms function test -f src/function.js -t <type> --input-file fixtures/cart.json compiles 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.
  2. Pre-built artifactlms function test --wasm dist/function.wasm uploads a .wasm you’ve already compiled (useful in CI where you build once and reuse).
  3. 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):
    Function TypeDefault timeout
    discount500 ms
    payment_customization500 ms
    cart_transform1 s
    order_validation1 s
    delivery_customization1 s
    shipping_rate2 s
    All timeouts are clamped to a 5 s hard cap server-side.
  • 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 fallback policy — 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 TypeMax active per shop
cart_transform1
payment_customization5
delivery_customization5
order_validation5
shipping_rate5
discount25
The dispatch site runs every active function of a given type on every cart verification / order placement — without these caps a store could end up with compounded discounts, hidden payment methods, or unbounded cart_transforms. To install a new app of a type that’s already at the cap, uninstall an existing one first.
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

Cart / checkout latency is conversion-critical. Time-bounded work only — no carrier-API calls without aggressive caching.
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.
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”.
App versioning + rollback is wired via /apps/developer/:appId/versions/*. Test new function logic on a staged version before promoting to production.