App functions: the 9 declarative function types
App functions let your app reshape carts, shipping, payments, deliveries, pickups, orders, and discounts without owning a server. You ship a tiny JavaScript file, the platform compiles it to a 3 KB portable bytecode module via Javy, and the host runs it inline during checkout. Functions are deterministic, fast, and isolated.
There are nine function types. This guide explains what each one does, where it runs, and how to test it.
The nine types at a glance
cart_transform— rewrite line items (price, properties, merge/split).shipping_rate— emit custom shipping methods (e.g. zone-based or weight-tier rates).payment_customization— hide, rename, or reorder payment methods.delivery_customization— hide, rename, or reorder shipping options.order_validation— block order placement (e.g. min quantity, country block).discount— emit per-line or order-level discount entries.local_pickup_options— emit in-store / local pickup options at checkout.pickup_point_options— emit third-party pickup points (parcel lockers, courier pickup points).fulfillment_constraints— restrict which locations may fulfill each line, and block orders that can't be fulfilled.
Where each function fires
Functions execute at two hooks in the order pipeline:
- Cart validation — runs every time the cart is recalculated: cart page, checkout, address page. Seven function types fire here:
cart_transform,shipping_rate,payment_customization,delivery_customization,discount,local_pickup_options, andpickup_point_options. - Order placement — runs when the customer places an order.
order_validationfires first, thenfulfillment_constraintsruns after validation succeeds but before the order is created. The cart-validation set also re-runs here as a final guard against client tampering.
The split matters: a cart_transform changes the visible cart instantly, while order_validation and fulfillment_constraints only block at the moment of order placement.
Anatomy of a function
Every function is a single function.js that exports a run(input) function returning an output object:
// cart_transform example: tag any line over $100 as "vip"
export function run(input) {
return {
operations: input.cart.lines
.filter(line => line.cost.totalAmount.amount > 100)
.map(line => ({
update: {
id: line.id,
properties: { tier: 'vip' },
},
})),
};
}
Alongside the JS you ship a manifest declaring the type, input/output schema version, and any merchant-configurable settings:
{
"name": "VIP tier tag",
"type": "cart_transform",
"handle": "vip-tier-tag",
"version": "1.0.0",
"input": { "schema": "cart.v1" },
"settings": [
{ "key": "threshold", "type": "number", "default": 100, "label": "Min line price ($)" }
]
}
How compilation works
The platform compiles your function.js using javy compile -d in dynamic mode. Dynamic mode links against a shared provider.wasm at runtime, which keeps your function module to roughly 3 KB. The hard size cap is 256 KB — plenty even for elaborate logic. Compilation happens server-side on upload. The compiled bytecode and the original source are stored alongside the app, while the app’s extension manifest stores just the metadata and a pointer to the compiled module.
Inputs and outputs by type
| Type | Input shape | Output shape |
|---|---|---|
cart_transform | { cart, settings } | { operations: [{ update | merge | split }] } |
shipping_rate | { cart, address, settings } | { rates: [{ name, price, code }] } |
payment_customization | { cart, paymentMethods, settings } | { operations: [{ hide | rename | move }] } |
delivery_customization | { cart, deliveryOptions, settings } | { operations: [{ hide | rename | move }] } |
order_validation | { cart, customer, settings } | { errors: [{ message, target }] } |
discount | { cart, settings } | { discounts: [{ targets, value, reason }] } |
local_pickup_options | { cart, address, settings } | { pickupOptions: [{ name, locationId, address, price }] } |
pickup_point_options | { cart, address, settings } | { pickupPoints: [{ name, carrier, code, address, price }] } |
fulfillment_constraints | { cart, locations, settings } | { constraints: [{ lineId, allowedLocationIds }], message } |
Storefront consumption
The output of each function flows into a specific UI surface:
- cart_transform →
/cartpage renders the updated line items. - shipping_rate → checkout shipping picker appends the custom shipping rates.
- payment_customization → payment radios filter out hidden methods; renames swap the label.
- delivery_customization → shipping zones display renamed labels.
- order_validation → checkout displays the error and blocks the place-order button.
- discount → entries appear in the price breakdown on the order page as separate discount lines.
- local_pickup_options → the Pickup tab at checkout lists the app-provided pickup locations alongside any native store pickup points.
- pickup_point_options → checkout shows selectable third-party pickup points (e.g. parcel lockers) for the buyer’s area.
- fulfillment_constraints → no buyer-facing UI; the order routing engine reads the allowed-location list and the order is blocked at placement if any line can’t be fulfilled.
Native pickup vs. app pickup: a store can offer in-store pickup natively (turn on Offer local pickup on a warehouse, no app required) — those native points always appear on the checkout Pickup tab. The local_pickup_options and pickup_point_options function types are for apps that want to add pickup locations or carrier pickup points on top of the native ones.
Testing your function
The portal includes a function test endpoint that runs your compiled function against a sample cart payload without going through real checkout:
POST /apps/functions/test
Content-Type: application/json
Authorization: Bearer YOUR_DEVELOPER_JWT
{
"appId": "...",
"handle": "vip-tier-tag",
"input": {
"cart": { "lines": [{ "id": "gid://line/1", "cost": { "totalAmount": { "amount": 150 } } }] },
"settings": { "threshold": 100 }
}
}
The response includes the function’s output, execution time, and any console logs. Great for unit testing edge cases before publishing.
For end-to-end verification, install the app on a test store and drive the real checkout UI — that’s the only way to confirm the UI consumers (price breakdown, payment radios, pickup tab, etc.) actually render the function’s effect.
FAQ
How is a cart_transform different from a discount function?
cart_transform rewrites the line item itself — you can change price, properties, merge lines into bundles, split a single line. discount leaves the line untouched and emits a separate discount entry that shows up in the price breakdown. Use cart_transform for structural changes; use discount for “minus X dollars” with an audit trail.
When should I use a pickup function vs. the native pickup setting?
If you just want to let buyers collect orders from your own store locations, turn on Offer local pickup on the warehouse — no app needed. Use local_pickup_options when an app needs to inject pickup locations programmatically (e.g. a network of partner stores), and pickup_point_options when integrating a carrier’s parcel-locker or pickup-point network at checkout.
What does fulfillment_constraints actually block?
It runs at order placement and returns, per line, the list of locations allowed to fulfill that line. If any line ends up with no allowed location, the order is rejected with your function’s message. Use it to enforce rules like “hazmat items ship only from the licensed warehouse” or “this SKU can’t be combined with that one in a single shipment.”
Can one app ship multiple functions?
Yes. An app can ship any number of functions of any of the nine types. Each function has its own handle and runs independently. Functions from the same app receive the same merchant settings namespace. Each store has a per-type active-install cap so effects can’t compound unbounded.
What language do I write functions in?
JavaScript today, compiled to portable bytecode by Javy. The dynamic-mode compile keeps modules tiny (~3 KB) by sharing a runtime provider; you focus on logic, not runtime weight.
Do functions have I/O?
No. Functions are pure: input in, output out. They can’t read databases, call HTTP APIs, or read environment variables. If you need to fetch data, do it in your normal app backend and write the relevant facts into a metafield the function can read.
What’s the timeout?
Functions run inline in cart validation and at order placement, so they must be fast — budget under 10 ms. The sandbox is single-threaded with no I/O, which makes hitting that easy in practice.
How do I roll back a buggy function?
Rollback is a developer action, not a merchant one. In the developer portal, open your app and find the function in the functions list — each function shows its published version history. Click Rollback next to the version you want to restore, and that function is reverted to the chosen version. There is no merchant-side rollback button for individual functions. You can also deprecate a version from the portal so no further stores install it.