# Run Custom Code (JS)

## The Basics

The custom code step is an advanced tool for processing data in Relay.app. It can be used to build complex logic that goes beyond the capabilities of Relay.app's *Transform data* steps.

Custom code is written in JavaScript, and some familiarity with programming is required to use it.

### When to use

Common scenarios that work well are:

* Data from [step-outputs](https://docs.relay.app/data/step-outputs "mention") need to be converted into another format.
* Complex logic is required to decide what the workflow should do. Custom code steps are limited to working with local data. Communicating with external services within a custom code step is not possible.

## JavaScript support

Code can use all [ES2023](https://tc39.github.io/ecma262/2023) language features and the [luxon](https://moment.github.io/luxon) and [lodash](https://lodash.com/) libraries.

### Async support

Custom code can be asynchronous and return a `Promise`. To use `await` inside the step, declare the exported function as `async`:

```javascript
export default async function (inputs) {
  // await any promise-returning operation here
  return { /* ... */ };
}
```

The step waits for the returned promise to resolve before passing its value to later steps in the workflow.

### Built in objects

The custom code step runs in a narrow sandbox, not a full Node.js runtime. Only the globals listed below are available. There is no `require`, no module loader, and no access to the Node.js standard library.

The following globals are exposed:

* **`console`** — implements `console.log` to record messages during execution.
* **`_`** — the [lodash](https://lodash.com/) library.
* **`Buffer`** — Node.js-style `Buffer` for working with binary data.
* **`atob` / `btoa`** — base64 encode and decode strings.
* **`TextEncoder` / `TextDecoder`** — encode and decode between strings and `Uint8Array` byte sequences (typically UTF-8).
* **`crypto.getRandomValues`** — fill a typed array with cryptographically strong random values.
* **`crypto.randomUUID`** — generate a random RFC 4122 v4 UUID.
* **`crypto.subtle.digest`** — compute a cryptographic hash (e.g. SHA-256) of a byte sequence.
* **`crypto.subtle.importKey`** — import a key for use with other `crypto.subtle` operations.
* **`crypto.subtle.sign`** — produce a signature over a byte sequence using an imported key.

{% hint style="info" %}
This is the full list of sandbox globals. Anything not listed here — including most of the Web Platform and Node.js APIs — is not available inside a custom code step.
{% endhint %}

### Date and time data

Custom code uses the [luxon](https://moment.github.io/luxon) library to handle date and time data. Input data with the corresponding type is passed to the function as a `DateTime` or `Duration` value. If returning the output of a Luxon call, those values should be typed accordingly in Relay.app (e.g., using Relay.app's Date/DateTime/Duration variable types).

The luxon documentation offers a good overview of [formatting](https://moment.github.io/luxon/#/formatting) and math capabilities. The complete API can be viewed in the[ API reference](https://moment.github.io/luxon/api-docs/index.html).

### Limitations

The custom code sandbox is intentionally restrictive. In particular:

* **No `require`** — modules cannot be loaded dynamically. Only the built-in objects listed above are available.
* **No `fetch` and no outbound network access** — custom code cannot call external services, APIs, or websites from inside the step. Use a dedicated HTTP or integration step for that.
* **`crypto.subtle` is limited** — only the operations listed under *Built in objects* (`digest`, `importKey`, `sign`) are supported. Other `SubtleCrypto` methods (such as `encrypt`, `decrypt`, `verify`, `deriveKey`, `generateKey`, `exportKey`, `wrapKey`, `unwrapKey`) are not available.
* **`importKey` supports binary key formats only** — `pkcs8`, `spki`, and `raw` are supported. The `jwk` (JSON Web Key) format is not supported.

## Working with data

Steps in Relay.app define their interface ahead of time. For custom code this requires configuring both inputs and outputs:

* Configure the **Inputs** by selecting data produced by earlier steps of the workflow\*\*.\*\*
* Configure the shape of the **Outputs** returned by your code block. Your code must return a value whose structure matches the **Outputs** configuration.

{% hint style="info" %}
Anything configured in **Outputs** is accessible in later steps of the workflow.
{% endhint %}

### Inputs

The inputs section defines what data from the workflow can be used from the custom code step. Inputs are typed according to the source data. All inputs are passed to the function together as a single object, keyed by their name.

In addition to regular fields the custom code step also supports picking whole data objects. These give access to the underlying raw data used throughout Relay.app.

{% hint style="info" %}
Use the *Interface* section in the *Inputs* tab of the code editor to see the structure of the raw data objects.
{% endhint %}

### Outputs

The output schema defines what data the step will produce when executed. This is required to enable later steps to work with the data.

There are two main recommended patterns for using the custom code step that allow automatic configuration of the output schema:

1. If the goal is to produce data in a format defined by another step, first build out the full workflow. Next, add the custom code step in the middle. Now the outputs can be configured to match these inputs, which guarantees a perfect fit and full support by the compiler when working on the code.
2. If the step is used to implement custom logic, first configure the inputs, and then build the code. Running the code via the *Test* tab will produce an inferred output schema that can be automatically applied to your step. Make sure to test with a variety of inputs, to build confidence that your schema covers all potential outputs.

{% hint style="info" %}
Using one of these two approaches enables automatic configuration of output schema for the vast majority of use cases. While possible, it should rarely be necessary to configure schemas manually.
{% endhint %}

## Debugging

The *Test* tab in the code editor offers the best way to quickly verify that code is working correctly. To provide additional visibility into the execution beyond input and output values, use `console.log`. Anything logged during code execution is displayed in Relay.app alongside your output.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.relay.app/built-in-actions/run-custom-code-js.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
