> This page is for Docs.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://zuora.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://zuora.ferndocs.com/_mcp/server.

# Mediation code editor guide

> The Mediation code editor guide offers a comprehensive overview of using GraalVM 23.0.1, GraalJS, and GraalPy to execute custom logic in meters. It includes examples of transformers, accumulators, and aggregators, along with guidelines for handling dates and currency in JavaScript and Python.

The Mediation code editor guide offers a comprehensive overview of using GraalVM 23.0.1, GraalJS, and GraalPy to execute custom logic in meters. It includes examples of transformers, accumulators, and aggregators, along with guidelines for handling dates and currency in JavaScript and Python.

Zuora Mediation code editors run on GraalVM 23.0.1, which provides a high-performance, multi-language runtime. GraalJS / GraalPy are the JavaScript and Python runtimes that Mediation's code‑based operators use to run your custom logic inside meters.

|         | Version | Language Version | Description                                                                 | Link                                                           |
| ------- | ------- | ---------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------- |
| GraalVM | 23.0.1  | n/a              | Unified polyglot runtime for JS, Python                                     | [GraalVM](https://www.graalvm.org/)                            |
| GraalJS | 23.0.1  | ECMAScript 2023  | Full ES2023 (ECMA-262) compliance; ES2024 features rolling in incrementally | [GraalJS – GitHub Releases](https://github.com/oracle/graaljs) |
| GraalPy | 23.0.1  | Python 3.11.7    | Full Python 3.11 standard library support                                   | [GraalPy Documentation](https://www.graalvm.org/python/)       |

## Example: Minimal transformer

The structure of a transformer that runs once per event:

GraalJS

```
// minimal transformer
exports.step = (payload) => {
  const price = Number(payload.price) || 0;
  const qty = Number(payload.quantity) || 0;
  const totalCents = BigInt(Math.round(price * 100)) * BigInt(qty);
  return { ...payload, total_cents: totalCents };
};
```

GraalPy

```
# minimal transformer
from decimal import Decimal
def step(payload):
    price = Decimal(str(payload.get("price", "0")))
    qty = int(payload.get("quantity") or 0)
    total_cents = (price * Decimal("100") * qty).to_integral_value()
    payload["total_cents"] = str(total_cents)
    return payload
```

## Example: Accumulator (windowed / batch)

An accumulator runs on a group of events collected in a time window and returns one summary. It usually does not rely on persistent state.

GraalJS

```
exports.step = (events, ctx) => {
  if (!events || events.length === 0) return null;
  let total = 0;
  for (const e of events) {
    total += Math.round((Number(e.price) || 0) * 100) * (Number(e.quantity) || 0);
  }
  return { groupKey: ctx?.groupKey, eventCount: events.length, total_cents: total };
};
```

GraalPy

```
GraalPy (example)
from decimal import Decimal
def step(events, ctx):
    if not events: return None
    total = sum((Decimal(str(e.get("price","0"))) * Decimal("100") * int(e.get("quantity") or 0)).to_integral_value() for e in events)
    return {"groupKey": ctx.get("groupKey"), "eventCount": len(events), "total_cents": str(total)}
```

## Aggregator (stateful per-group)

An aggregator keeps a small, persistent state per group (for example per customer) and releases records when business rules are met. The context provides `state.get(key)` and `state.set(key, value)`. Use those to read and update small values that persist across calls for the same group.

## Function structure

* The entry point is usually a function named `step`.

  * In JavaScript export it as `exports.step = ….`
  * In Python define `def step(...):`
* A transformer receives one event (payload) and an optional context. It returns a transformed object or `null/None` to drop the event.
* An accumulator receives a list of events for a time window and returns a single summary object or `null/None`.
* An aggregator receives a list of events and a context that includes state; it may return multiple released records or `null/None`.
* Always document parameter shapes (JSDoc for JS or type hints and a docstring for Python) and validate inputs.

## Common context fields

* `state`: object with `.get(key)`, `.set(key, value)`, and `.clear(key)` (present for aggregators).
* `windowStart`, `windowEnd`: ISO timestamps for accumulators.
* `groupKey` / `groupFields`: the grouping key(s) that scope state to each group.
* Other metadata such as `triggerType` or `eventCount` may also appear.

## State rules

* Call state.get(key) and state.set(key, value) to read and write state. In JS you often do const state = context.state.
* State is scoped per `groupFields`, so each group gets its own isolated state.
* Keep state small and JSON-serializable (numbers, strings, small objects or arrays).
* Serialize complex values (for example `Decimal`, `BigInt`, or `datetime` objects) as strings or compact JSON and parse them back when you read them.
* Do not store large files, binary blobs, file handles, or OS resources in state.

## Rules for common operations

Dates:

* Prefer ISO-8601 strings like `2025-11-21T14:00:00Z`.
* JS: parse with `new Date(iso)` and get canonical form with `toISOString()`. Use `Intl.DateTimeFormat` for display formatting.
* Python: normalize `Z` to `+00:00` if needed, then use `datetime.fromisoformat()` and `zoneinfo.ZoneInfo` to handle time zones. Store timestamps in UTC.

Money and numbers:

* JS: do currency math in integer cents or with `BigInt`. Convert `BigInt` to string before JSON/state.
* Python: use `decimal.Decimal` for currency. Convert `Decimal` values to string before saving in state or JSON.

JSON and regular expressions (regex):

* JS: use `JSON.parse()`/ `JSON.stringify()` with `try/catch`, and `RegExp` for patterns. Pre-compile regexps if reused.
* Python: use`json.loads()` / `json.dumps()` and `re/re.compile()` for patterns. Convert non-serializable types to strings before dumping.

Encoding, CSV, and binary:

* JS: use `TextEncoder` / `TextDecoder` for text/bytes conversions. Use base64 only if you must store binary.
* Python: use the `base64` module and the `csv` standard library to parse CSV files. Do not store raw binary in state.

Error handling: Always validate inputs and catch parse/convert exceptions. Use Return `null/None` to drop bad events and log helpful context for debugging.

## Other guidelines

* Use standard libraries only: Always prefer built-in or standard libraries provided by the runtime.
* Follow version compatibility: Write code compatible with ES2023 or Python 3.11 to ensure consistent behavior.
* Keep it portable: Write code that does not depend on system-specific or external dependencies.
* No external native libraries: Avoid using Node.js packages, CPython binary extensions (.pyd, .so), or OS-level native modules. They may not be compatible.
* No browser-specific code: GraalJS is not a browser. DOM APIs (`window`, `document`) do not exist.
* Node.js globals: `require`, `process`, and similar globals are not supported in GraalJS.
* Do not mix unrelated language constructs: Avoid mixing language runtimes unnecessarily. Polyglot should serve a clear integration purpose.
* No experimental flags in production: Unless explicitly approved, do not enable unstable or undocumented flags.