> ## Documentation Index
> Fetch the complete documentation index at: https://woku.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Data transformations with code

> Transform and derive fields with JavaScript inside Data flow, with code generated by an agent that you can review, edit, and simulate before running

<Info>
  **Available on the Corporate plan.** This capability is part of woku's enterprise features. [Talk to our sales team](https://woku.app/pricing).
</Info>

woku lets you **transform and derive fields with JavaScript** over the
records of your data sources, inside the same platform and **without
external middleware** (no Zapier, no custom Lambda, no separate ETL
process). These transformations live in the
[Data flow](/docs/en/datos/flujo-de-datos) module, where the code is generated, tested,
and run.

## Where transformations live

Transformations are part of a **data flow**. A flow works
over a published [data source](/docs/en/datos/fuentes-de-datos) and decides, row
by row, which action to run: send a survey, invite the respondent to leave
feedback, create a client, or update their data. Before evaluating those
rules, the flow can transform each record: clean values, derive new
fields, normalize formats, or discard rows that are not useful.

You find the module in the **Data** group of the side menu, next to
**Data source**.

## How a transformation is built

You do not write the transformation from scratch. The flow builder is
conversational:

<Steps>
  <Step title="Describe what you need">
    In the builder chat you explain in natural language what you want
    to do, for example "combine first name and last name into a single field, classify
    the NPS as promoter, passive, or detractor, and discard the rows with no email or
    phone".
  </Step>

  <Step title="The agent generates the code">
    The agent writes the complete JavaScript code of the flow, including the
    transformations, and shows it in two synchronized representations:
    **Pseudocode**, a human description of each step, and **Code**, the
    actual JavaScript, which you can edit by hand if you prefer to adjust something
    directly.
  </Step>

  <Step title="Validate with a simulation">
    The **Simulation** tab runs the code over the sample records
    of the source, without sending messages or modifying clients, and
    shows you the effect of each transformation row by row.
  </Step>
</Steps>

<h2 id="field-transformation">
  Field transformation
</h2>

Inside the flow code, transformations operate over each record
with explicit operations: `setField` derives or modifies a field,
`discardRecord` discards the row with a reason, and `hold` leaves it on hold
with no action. There are also helpers like `normalizePhone`, which brings phones to
international format, and `hasContent`, which verifies that a field has
real content.

These are examples of fragments like the ones the agent generates, and that you can
adjust in the **Code** tab:

<CodeGroup>
  ```js Derive a new field theme={null}
  // Generates a "fullName" field combining first and last name.
  setField(
    record,
    'fullName',
    `${record.firstName ?? ''} ${record.lastName ?? ''}`.trim(),
  );
  ```

  ```js Conditional logic theme={null}
  // Classifies an NPS (0..10) into promoter / passive / detractor.
  const score = Number(record.npsScore);

  if (score >= 9) {
    setField(record, 'npsCategory', 'promotor');
  } else if (score >= 7) {
    setField(record, 'npsCategory', 'pasivo');
  } else {
    setField(record, 'npsCategory', 'detractor');
  }
  ```

  ```js Normalize dates and phones theme={null}
  // Derives the date in YYYY-MM-DD format and normalizes the phone.
  setField(
    record,
    'purchaseDate',
    new Date(record.createdAt).toISOString().slice(0, 10),
  );
  setField(record, 'phone', normalizePhone(record.phone));
  ```

  ```js Discard records theme={null}
  // Discards the rows with no contact channel at all, with a reason.
  if (!hasContent(record.email) && !hasContent(record.phone)) {
    discardRecord(record, 'No contact email or phone');
  }
  ```
</CodeGroup>

Transformations are applied before evaluating the flow rules, so
the conditions and the send actions work over the fields that are already
derived and normalized.

## Simulation and versions

The **Simulation** tab is the way to validate a transformation before
it touches real data. It runs the code over the saved sample of the
source and returns a breakdown by result, examples of records with the
detail of the transformations applied to each row, and a validation
that the referenced fields exist and the commands are valid.

The flow code is **versioned**. On running, if the code changed
since the last run, a new version is frozen. From the history
you can see any previous version in read-only mode and restore it as
the current version, and each run is recorded with the version it used.

## Execution and runtime isolation

Flow execution is manual and always runs **on the server side**.
The code runs in an isolated context, **without network access or file system
access**: it can only read the input record and express what to do
with it through the flow operations. You do not expose credentials nor
maintain your own infrastructure.

## Best practices

* **Null tolerance:** source data may come in incomplete; use
  `??` and `hasContent` before computing or concatenating.
* **Explicit derived fields:** prefer creating new fields
  (`fullName`, `npsCategory`, `purchaseDate`) instead of overwriting the
  originals, so that the flow rules and the simulation detail
  are easy to read.
* **Discard with a reason:** when a row is not useful, use `discardRecord` with
  a clear reason. The reason appears in the simulation and in the history, and
  makes it easier to understand why a record did not generate an action.
* **Simulate before running:** validate the transformation in the
  **Simulation** tab and review the per-row detail before running the flow with
  real sends.
