Streaming transformations
Amplitude supports streaming transformed data to your destinations, including Custom Events, Derived Properties, Transformed Events, and Transformed Properties. You can select any transformation from your Amplitude taxonomy when configuring event streaming.
Setup
- In Amplitude Data, click Catalog and select the Destinations tab.
- In the Event Streaming section, click on any streaming destination tile.
- Enter a sync name, then click Create Sync.
- Toggle Status from Disabled to Enabled.
- Paste your destination's Server Secret Key.
- Toggle Send events to enable event streaming.
- In Select and filter events, choose which events to send. Select only the events needed for your downstream destination. The dropdown includes any transformed events from your taxonomy.
- (optional) In Select additional properties, select any event properties (including transformed event properties) to include. By default, Amplitude doesn't send any additional properties unless you explicitly select them.
- (optional) In Select additional user properties, select any user properties to include. By default, Amplitude doesn't send any additional user properties unless you explicitly select them.
- When satisfied with your configuration, click Save.
Example use cases
Streaming renamed events to AppsFlyer
AppsFlyer requires unique event names for attribution and doesn't support event properties. Previously, you had to create custom events in your backend and resend them to Amplitude. You can now use Amplitude Data to rename events before streaming them to AppsFlyer, which reduces duplicate ingestion.
Streaming derived properties to Braze
To improve campaign effectiveness, you can stream derived properties from Amplitude to Braze. Select derived properties in your sync filters and include them when configuring additional properties. This enables more targeted campaigns using enriched event data from Amplitude.
Considerations
Note the following when streaming transformations from Amplitude:
- Amplitude sends selected event and user properties along with the event.
- Amplitude targets an end-to-end p95 latency of 60 seconds. Amplitude streams 95% of events within 60 seconds. Amplitude has internal processes, monitors, and alerts in place to meet this target.
Transformation payload structure
The streaming payload includes transformations as nested JSON fields. Understanding this structure is essential when using custom FTL (FreeMarker Template Language) templates or configuring how your destination receives data.
Selection requirement
You must explicitly select transformations in your sync configuration to include them in the streaming payload. You can select transformations in:
- Select and filter events: For example, filtering events where a derived property isn't
null. - Select additional properties: To include specific transformed properties.
- Map properties to destination: When mapping properties to your destination's schema (if applicable).
The streaming payload only includes transformations you explicitly select.
JSON structure
The payload includes transformations as nested JSON objects. The top-level field name depends on the transformation type:
| Transformation Type | Top-level JSON Field Name |
|---|---|
| Merged properties | N/A (replaced in original field) |
| Derived properties | derived_properties |
| Channel properties | derived_properties |
| Lookup properties | lookup_properties |
Field names within these objects match the transformation names displayed in the Amplitude UI.
Example payload
If you select a derived property called sample_derived_property_key1, the streaming payload looks like this:
{
"event_type": "Button Clicked",
"user_id": "12345",
"derived_properties": {
"sample_derived_property_key1": "whatever_value"
}
}
Using transformations with custom FTL
If your destination uses custom FTL templates, you can access transformation data using these patterns.
Example 1: Using FtlUtils to serialize derived properties
<#assign UtilClass=statics['com.amplitude.integrations.connector.utils.FtlUtils']>
{
"version": "derived_properties_sample_ftl1",
"derived_properties": ${UtilClass.toJson(input.derived_properties)}
}
Example 2: Manually iterating over derived properties
<#assign UtilClass=statics['com.amplitude.integrations.connector.utils.FtlUtils']>
{
"version": "derived_properties_sample_ftl2",
"derived_properties": {
<#list input.derived_properties?keys as key>
"${key}": "${input.derived_properties[key]}"<#sep>,</#sep>
</#list>
}
}
Customize payloads with FreeMarker (FTL)
Event streaming destinations that expose a custom payload editor use Apache FreeMarker (FTL) to turn the Amplitude event into the JSON body your destination expects. The editor in the connector builder provides autocomplete, hover documentation, and inline validation for the fields and helpers below. For the FreeMarker language itself, refer to the guide to creating templates.The input object
input is the event (or user) Amplitude forwards. Access its fields with dot notation, wrapped in ${ } to output the value:
"${input.user_id}"
"${input.event_type}"
"${input.event_time}"
event_type, user_id, device_id, time, event_time, session_id, platform, event_properties, user_properties, group_properties, and groups, plus any transformations you select (derived_properties, lookup_properties). For the complete shapes, refer to the event format and user (Identify) format.Fields aren't guaranteed
input fields depend on how each user instruments events, and a selected event property might not be present on every event you stream. Reference fields defensively (refer to Handle missing fields), and keep required-field usage to common fields like user_id, event_type, and event_time. The payload editor flags a referenced property that isn't present on all of the events you selected.Dot and bracket notation
Use dot notation when a property name is a valid identifier, and bracket notation when the name contains spaces or other special characters:
${input.event_properties.plan}
${input.event_properties["Plan Type"]}
Handle missing fields
Because fields aren't guaranteed, guard against missing values:
??tests whether a value exists:<#if input.user_id??>…</#if>.!supplies a default when a value is missing:${input.user_properties.email!}(empty string) or${input.revenue!0}.
{
<#if input.user_id??>
"external_id": "${input.user_id}",
</#if>
"email": "${input.user_properties.email!}"
}
Useful built-ins
FreeMarker built-ins (?name) transform values. Common ones for JSON payloads:
| Built-in | Use |
|---|---|
?c | Format a number for computers, with no locale grouping or scientific notation. Use for numeric JSON values: ${input.revenue?c} |
?json_string | Escape a string for safe use inside a JSON string: "${value?json_string}" |
?keys | Get an object's keys to iterate with <#list> |
?is_number, ?is_boolean, ?is_string | Test a value's type before serializing it |
Iterate over properties
Use <#list> with ?keys, and <#sep> to place commas between items only:
"user_properties": {
<#list input.user_properties?keys as key>
"${key}": ${UtilClass.toJsonString(input.user_properties[key])}<#sep>,</#sep>
</#list>
}
Helper methods (FtlUtils)
Amplitude exposes helper methods for serializing values and other common transforms, so you don't have to hand-write the logic. Assign the helper once at the top of the template, then call its methods:
<#assign UtilClass=statics['com.amplitude.integrations.connector.utils.FtlUtils']>
{
"user_properties": ${UtilClass.toJson(input.user_properties)}
}
| Method | Returns | Description |
|---|---|---|
toJson(value) | string | Serializes a value to JSON. Maps, lists, and JSON values become JSON; scalars become a quoted JSON string (for example, 42 becomes "42"). |
toJsonString(value) | string | Alias of toJson. |
dateStringToIso(dateTime) | string | Converts an Amplitude datetime string to ISO 8601. |
dateStringToEpochSeconds(dateTime) | number | Converts an Amplitude datetime string to epoch seconds. |
dateStringToEpochMillis(dateTime) | number | Converts an Amplitude datetime string to epoch milliseconds. |
epochSecondsToIso(seconds) | string | Converts epoch seconds to ISO 8601. |
epochSecondsTextToIso(text) | string | Converts epoch seconds provided as text to ISO 8601. |
epochMillisToIso(millis) | string | Converts epoch milliseconds to ISO 8601. |
isValidEmail(value) | boolean | Returns whether the value is a valid email address. |
sha256(value) | string | Returns the SHA-256 hash of the value. If the value is already a 64-character hash, returns it unchanged. |
cleanJsonPropertyName(name) | string | Replaces whitespace with underscores and removes other non-alphanumeric characters. |
getDisjointMap(source, filter) | map | Returns the entries of source whose keys aren't present in filter. |
Destination-specific helpers
Some destinations provide additional helper methods tailored to that destination. You can use those helpers only in that destination's template.
Examples
Each example shows the input event Amplitude forwards, the template you write, and the result Amplitude sends to your destination.
Simple: rename and pick fields
Map a few input fields into the shape your destination expects.
Input event:
{
"event_type": "Song Played",
"user_id": "user-123",
"event_time": "2024-01-15T09:30:00.000",
"event_properties": {
"song_id": "abc-987"
}
}
Template:
{
"external_id": "${input.user_id}",
"name": "${input.event_type}",
"song": "${input.event_properties.song_id}"
}
Result:
{
"external_id": "user-123",
"name": "Song Played",
"song": "abc-987"
}
Complex: guards, bracket notation, and iteration
Guard an optional field with ??, read a property whose name has a space with bracket notation, format a number with ?c, and serialize all user properties by looping with <#list>, emitting numbers and booleans raw and everything else through toJsonString.
Input event:
{
"event_type": "Purchase Completed",
"user_id": "user-123",
"revenue": 12.5,
"event_properties": {
"Item Name": "Pro Plan"
},
"user_properties": {
"email": "alex@example.com",
"plan": "pro",
"age": 30
}
}
Template:
<#assign UtilClass=statics['com.amplitude.integrations.connector.utils.FtlUtils']>
{
"event": "${input.event_type}",
<#if input.user_id??>
"user_id": "${input.user_id}",
</#if>
"item": "${input.event_properties["Item Name"]}",
"revenue": ${input.revenue?c},
"traits": {
<#list input.user_properties?keys as key>
<#assign value = input.user_properties[key]>
"${key}": <#if value?is_number || value?is_boolean>${value}<#else>${UtilClass.toJsonString(value)}</#if><#sep>,</#sep>
</#list>
}
}
Result:
{
"event": "Purchase Completed",
"user_id": "user-123",
"item": "Pro Plan",
"revenue": 12.5,
"traits": {
"email": "alex@example.com",
"plan": "pro",
"age": 30
}
}
Supported custom events
Amplitude supports streaming custom events that meet specific criteria. When you create custom events in the Amplitude taxonomy, you can select them for event streaming if they have:
- Supported properties: User properties and event properties only.
- Supported operators:
is,is not,contains, anddoes not contain.
Custom events that use other property types or operators aren't available for selection in event streaming configurations.
Limitations
Streaming transformations from Amplitude has some limitations:
- When you rename custom events or derived properties, update any existing sync configurations that reference them. Syncs require current event and property names to work properly. Note that changing the underlying definition of a transformation doesn't affect syncing. Only name changes require sync updates.
- Lookup properties: You can stream lookup properties by requesting access from Amplitude. Lookup properties map existing event or user properties to new properties using a CSV file upload and can enrich already-ingested events at query time.
- Lookup property files with over 1000 rows don't display in the streaming setup.
- After you save a lookup property file, it can take up to one hour to populate into the streaming system.
- Channel classifiers: To stream channel classifiers, request access from Amplitude. Channels act like derived properties applied in real time during queries. Marketers primarily use channels to define acquisition channels based on UTM and referrer data. By default, you can't select channel properties in sync configuration (event filters or additional properties) unless Amplitude has already enabled this feature for your organization.
- You can stream transformations to all streaming destinations except Data Warehouse destinations.
- The streaming setup doesn't support the following transformation types:
- Custom events that don't meet the criteria in the Supported custom events section.
- Group properties.
- Cart properties.
- Nested properties (for example, derived properties that rely on other derived properties). Exception: the UI allows selecting nested properties based on merged or cart properties, but they don't work in practice.
FAQ
Was this helpful?