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_completedvsOrder CompletedvsorderCompleted) - 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
transformand take one argument — the parsed event object - Return the event to forward it, modified however you like
- Return
nullto 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 andvar. - 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
- Navigate to Streams > Transformations
- Create a new transformation and give it a name and a description
- Write the
transformfunction in the code editor - Test it (see below)
- 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
| Status | Meaning |
|---|---|
| Draft | Saved and not yet in use |
| Active | In use by at least one forwarding rule |
| Error | The 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}/testTesting 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.propertiesmay be absent;event.properties.x.yon an event that lacksxthrows, 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.