# App functions: the 6 declarative function types

APP FUNCTIONS: THE 6 DECLARATIVE FUNCTION TYPES

App functions let your app reshape carts, shipping, payments, deliveries,
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
[https://github.com/bytecodealliance/javy], and the host runs it inline during
checkout. Functions are deterministic, fast, and isolated.

There are six function types. This guide explains what each one does, where it
runs, and how to test it.


THE SIX 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.


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. Five function types fire here: cart_transform,
   shipping_rate, payment_customization, delivery_customization, discount.
 * Order placement — runs when the customer places an order. order_validation
   fires here, and the cart-validation set re-runs as a final guard against
   client tampering.

The split matters: a cart_transform changes the visible cart instantly, while
order_validation only blocks 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 BY TYPE

TypeInput shapeOutput 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 }] }


STOREFRONT CONSUMPTION

The output of each function flows into a specific UI surface:

 * cart_transform → /cart page renders the updated line items.
 * shipping_rate → checkout shipping picker appends customShippingRates.
 * payment_customization → payment radios filter out hidden methods; renames
   flagged via __renamed 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.


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, 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.


CAN ONE APP SHIP MULTIPLE FUNCTIONS?

Yes. An app can ship any number of functions of any of the six types. Each
function has its own handle and runs independently. Functions from the same app
receive the same merchant settings namespace.


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?

Functions ship as part of an app version. Open the merchant's installed app row,
click Rollback, and the previous version's functions are restored. As a
developer you can also unpublish a version from the portal so no further
merchants install it.