Skip to Content
StreamsTransformations

Event Transformations

An event transformation is a JavaScript function that rewrites an event on its way to a destination. It lets you rename events, add or drop properties, reshape a payload, or discard an event entirely — without touching your source instrumentation.

Why Transformations?

Transformations solve common data engineering problems at the CDP layer:

  • Standardize naming — different sources use different conventions (order_completed vs Order Completed vs orderCompleted)
  • Remove sensitive data — strip PII before events reach a particular destination
  • Enrich events — add computed properties before forwarding
  • Filter noise — drop events a destination does not need, reducing volume and cost
  • Fix instrumentation — correct property names or types without redeploying source code

The Transform Function

Your code defines a function named transform that receives the event and returns it:

// Transform function receives the event object and returns a modified event. // Return null to drop the event entirely. function transform(event) { // Example: add a processed timestamp event.properties = event.properties || {}; event.properties.processed_at = new Date().toISOString(); return event; }

The rules are short:

  • The function must be named transform and take one argument — the parsed event object
  • Return the event to forward it, modified however you like
  • Return null to drop the event, which is how filtering is expressed
  • Every field is yours to change — event.event, event.type, event.userId, event.properties, event.context

The Runtime

Transformations run in a sandboxed ECMAScript 5.1 interpreter, in a fresh VM per event, with a two-second timeout. What follows from that:

  • No network access. There is no fetch, no HTTP client, no way to call an external service. Enrichment from another system is done by event enrichment, which reads from a Store, not from inside a transformation.
  • No shared state. Each event gets a new VM, so a variable set while processing one event is gone by the next.
  • ES5.1, not modern JavaScript. Arrow functions, const/let, template literals and spread are outside the guaranteed set — write plain functions and var.
  • A function that runs too long is a failure, not a slow success. Keep the work proportional to one event.

Examples

Rename an event and normalise a property:

function transform(event) { if (event.event === "orderCompleted") { event.event = "Order Completed"; } if (event.properties && event.properties.order_total) { event.properties.revenue = Number(event.properties.order_total); delete event.properties.order_total; } return event; }

Strip PII before a particular destination:

function transform(event) { if (event.context) { delete event.context.ip; } if (event.traits) { delete event.traits.email; delete event.traits.phone; } return event; }

Drop internal traffic:

function transform(event) { var email = event.traits && event.traits.email; if (email && email.indexOf("@yourcompany.com") !== -1) { return null; // dropped — nothing is forwarded } return event; }

Creating a Transformation

  1. Navigate to Streams > Transformations
  2. Create a new transformation and give it a name and a description
  3. Write the transform function in the code editor
  4. Test it (see below)
  5. Save

Testing

The editor carries a test panel with a sample event on the left and the result on the right. Edit the input to whatever shape you want to check, click Run Test, and read the output — including the error, if the code fails to compile or throws.

Testing runs the same engine the forwarder does, so what you see is what a live event would produce. A saved transformation can also be tested through the API by ID.

Declaring the Output Shape

A transformation can rewrite an event into a shape its source’s schema no longer describes — new properties, renamed ones, a different nesting. Declare the output schema so everything downstream knows what to expect.

The output-schema panel sits under the editor:

  • After a test run, it offers to adopt the shape that run produced, so you rarely type it by hand
  • The declared shape then feeds the field-mapping editor of any forwarding rule using this transformation — you map from the fields the transformation emits, not the ones the source declared
  • Clear removes the declaration, and mapping falls back to the source’s own schema
  • A version badge moves only when the shape itself changes

Leave it undeclared and nothing breaks — the mapping editor simply types events against the source schema, as it did before output schemas existed. It is worth declaring for any transformation that materially reshapes an event.

Attaching a Transformation

A transformation does nothing on its own. It is attached to an event forwarding rule, which is where it takes effect: the rule matches an event, the transformation rewrites it, and the field mapping delivers it.

A rule carries at most one transformation. To apply several changes, write them into one function — which also keeps the order explicit and readable rather than implied by a priority column.

The same transformation can be attached to several rules, so a “strip PII” function written once can guard every destination that needs it.

Status

StatusMeaning
DraftSaved and not yet in use
ActiveIn use by at least one forwarding rule
ErrorThe last execution failed — the recorded error says why

A transformation that fails at run time does not silently pass the event through: the delivery is a failure, recorded against the rule, so a broken function surfaces rather than quietly emitting untransformed events.

API Reference

Every path below is workspace-scoped — {id} is your workspace ID. See Base URL for your instance’s API base URL and Authentication for the required Authorization and X-Workspace-ID headers.

# List transformations GET /api/v1/workspaces/{id}/event-transformations # Get a transformation GET /api/v1/workspaces/{id}/event-transformations/{transformId} # Create a transformation POST /api/v1/workspaces/{id}/event-transformations # Update a transformation PUT /api/v1/workspaces/{id}/event-transformations/{transformId} # Delete a transformation DELETE /api/v1/workspaces/{id}/event-transformations/{transformId} # Test a saved transformation against a sample event POST /api/v1/workspaces/{id}/event-transformations/{transformId}/test

Testing works on a saved transformation, so create it first — leave it inactive — and test it by ID before switching it on. There is no endpoint for testing a transformation that has not been saved.

Best Practices

  • Keep the function small. It runs on every matching event, inside a two-second budget.
  • Guard every access. event.properties may be absent; event.properties.x.y on an event that lacks x throws, and a throw fails the delivery.
  • Test with a real event. Copy one out of the live event stream rather than inventing a shape.
  • Declare the output when the transformation reshapes an event, so forwarding rules map from what it emits.
  • Document it. Use the description to say why the transformation exists — it will be read by someone who did not write it.
  • Prefer contracts for validation. Event contracts enforce shape at ingest, across every destination; a transformation changes data for one delivery path.
Last updated on