Chord CDP
CDP Functions
22 min
due to the technical nature of this feature, functions are only available to chord users who are assigned a team admin or developer platform role for additional support, please reach out to mailto\ help\@chord co introduction functions are customizable data workflows that process, transform, or enrich the raw event data flowing through the cdp functions are written in javascript and run before data is sent to destinations they let you tailor how data is handled—whether it's standardizing event names, enriching user profiles, or formatting data for downstream tools—so you can keep your insights clean, consistent, and actionable in other words for marketers , cdp functions are more like custom built control panels they give your team the flexibility to reshape data in real time, enrich it with external info, or even decide what gets sent where—perfect for brands needing more control, precision, and adaptability for developers , cdp functions offer broader execution power they act like real time serverless functions with support for external api calls, persistent key value storage, and conditional logic you can reshape event streams on the fly, enrich payloads, or build highly dynamic routing logic—ideal for engineering teams looking for full control over their pipeline behavior some additional use cases for functions include filtering exclude unwanted events, such as dropping events from test sources or internal traffic normalizing standardize event data (e g , "page view", "pageview", "pageview" → "page") to keep downstream schemas clean enrichment add geo location data, parse user agents, or pull additional data from external apis routing dynamically change which table or destination an event is sent to based on event properties function anatomy every cdp function follows this structure export default async function(event, context) { // your transformation logic here return event; // or "drop" to filter out the event } parameters event (object) the event object being processed this is the raw event data coming from your sources common properties include event (string) the event name (e g , "page viewed", "product added") userid (string) the unique identifier for the user anonymousid (string) anonymous identifier if userid is not available timestamp (string) iso 8601 timestamp of when the event occurred properties (object) custom properties associated with the event context (object) contextual information about the event (ip, user agent, etc ) type (string) the event type (e g , "track", "identify", "page") context (object) the function context provides access to various services logging context log debug(message) debug level logging context log info(message) info level logging context log warn(message) warning level logging context log error(message) error level logging storage context store persistent key value storage that maintains data between function calls network & external services context fetch(url, options) standard fetch api for making http requests to external services return values the function's return value controls what happens to the event return an object the returned object becomes the new event sent to destinations retun an array of objects only the final function in a function chain can return an array of objects each object becomes a new event sent to destinations return "drop" the event is filtered out and won't be forwarded to the downstream destination return nothing/undefined the original event (or its modified copy) is used create a function to create a function, follow the steps below navigate to cdp navigate to the functions tab click the "+ new function" button click into the function name field at the top left and give your function a name click the ✔️ icon to save the function name add your function code into the wysiwyg editor click the "save" button to save your function debugging functions chord cdp comes with a debugger and editor that allows you to run a function on sample data within a function, you have access to logs which will emit debug / information messages here, you can instrument your javascript to print out event contents, branching logic, and intermediate values to help trace what the function is doing additionally, you can check the last run result tab within a specific function to see what the function returned whether it dropped the event, modified it, or forwarded it this tab will include logs, errors, or additional metadata about that run you will be able to see exactly how the function behaved for the latest event and catch transform/filter mistakes edit functions navigate to cdp navigate to the functions tab find the existing function you'd like to edit click the ✏️ icon under the actions column next to the function you want to edit delete functions navigate to cdp navigate to the functions tab find the existing function you'd like to delete click on the three vertical dots under the actions column next to the function you want to delete click the 🗑️ icon to delete the desired function connecting / environment variables functions chains are created on connections between sources and destinations navigate to cdp for a given destination, click the three dots next to the destination title then select "view connected streams" select the ✏️ icon under actions to edit the connection functions can be added to the pipeline from the functions library functions will run in the order specified in this interface click "save changes" environment variables are configured on a per connection basis define envrionment variable names and values for the connection below the function library in the "edit connection" drawer environment variables may be referenced in code like so process env variable name persistent storage for custom functions persistent storage allows functions to cache data between invocations, enabling use cases like rate limiting, deduplication, enrichment caching, and maintaining state across events when enabled, your cdp functions have access to a store object that provides a key value cache with ttl (time to live) support the store is backed by an in memory cache and persists data across function invocations within the same workspace using the store in custom functions basic operations the store provides the following methods // get a value (returns undefined if not found or expired) const value = await store get("my key"); // set a value with default ttl (31 days) await store set("my key", { foo "bar" }); // set a value with custom ttl await store set("my key", { foo "bar" }, "1h"); // 1 hour await store set("my key", { foo "bar" }, "7d"); // 7 days await store set("my key", { foo "bar" }, 3600); // 3600 seconds await store set("my key", { foo "bar" }, "inf"); // no expiration // delete a value await store del("my key"); // get ttl remaining (in seconds) const ttl = await store ttl("my key"); // returns positive number = seconds remaining, 1 = no expiry, 2 = key doesn't exist // get value with ttl info const result = await store getwithttl("my key"); // returns { value any, ttl number } or undefined complete example export default async function(event, { log, fetch, store }) { let key = "my key"; let ttl = "10s"; // retrieve the value from the cache let value = await store get(key); // if the value was cached, display it // else, put the value in the cache that will expire in 10s if (value) { log info(`cache hit! value is ${value}`); } else { let newvalue = math random() tostring(36) substring(2) log info(`cache miss! adding ${newvalue} to cache; ttl ${ttl} `); await store set(key, newvalue, ttl); } } ttl format the ttl parameter accepts several formats format example description number 3600 seconds duration string "30s" , "5m" , "2h" , "7d" human readable duration infinite "inf" no expiration default (omitted) 31 days maximum ttl is approximately 68 years (2,147,483,647 seconds) cache availability and graceful degradation the persistent storage feature is designed to be resilient if the cache becomes temporarily unavailable functions continue to work your custom functions will not fail or be retried due to cache issues cache operations degrade gracefully get() returns undefined (treated as a cache miss) set() silently fails (data won't be cached) del() silently fails (key may not be deleted) ttl() returns 2 (key doesn't exist) warnings are logged cache failures are logged for monitoring, but don't interrupt event processing this means your functions should be written to handle cache misses gracefully, as they may occur due to key not existing (normal cache miss) key expired (ttl reached) cache temporarily unavailable (graceful degradation) writing resilient functions always write your functions assuming the cache might be empty // good handles cache miss gracefully const cached = await ctx store get("my key"); if (!cached) { // regenerate or fetch the data const freshdata = await fetchdata(); await ctx store set("my key", freshdata, "1h"); return processevent(event, freshdata); } return processevent(event, cached); // avoid assuming cache always has data const cached = await ctx store get("my key"); return processevent(event, cached someproperty); // may fail if cached is undefined key naming and uniqueness important storage keys are shared across all functions in your cdp workspace this means a key set by one function can be read, modified, or deleted by any other function in the same workspace you must ensure your keys are unique to avoid unintended collisions between functions best practices for key naming use function specific prefixes to namespace your keys // good namespaced keys await store set("rate limiter\ user 123", count); await store set("enrichment cache\ company 456", data); // avoid generic keys that might collide await store set("user 123", count); await store set("cache 456", data); use environment variables when you have multiple versions of the same function (e g , development and production) // set function env as an environment variable "dev" or "prod" const keyprefix = process env function env || "dev"; await store set(`${keyprefix}\ rate limit ${userid}`, count); // results in "dev\ rate limit 123" or "prod\ rate limit 123" code examples basic filter drop events without user ids export default async function(event, context) { // filter out any events that don't have a userid if (!event userid) { context log debug(`dropped event without userid ${event event}`); return "drop"; } return event; } allowlist filter only forward specific events export default async function(event, context) { // define which events should be forwarded const allowedevents = \[ "order completed", "product viewed", "cart updated" ]; if (!allowedevents includes(event event)) { context log debug(`dropped disallowed event ${event event}`); return "drop"; } return event; } external api enrichment export default async function(event, context) { // enrich user data with information from an external api // uses environment variables for secure credential management try { const userid = event userid; if (!userid) { return event; // skip enrichment if no userid } // access environment variables via context props // these are configured in the chord cdp ui for each function const apikey = process env api key; const apibaseurl = process env api base url; if (!apikey) { context log warn('api key not configured, skipping enrichment'); return event; } const response = await context fetch( `${apibaseurl}/users/${userid}`, { headers { 'authorization' `bearer ${apikey}`, 'content type' 'application/json' } } ); if (response ok) { const userdata = await response json(); // add enriched data to event properties event properties lifetimevalue = userdata ltv; context log info(`enriched user ${userid} with external data`); } else { context log warn(`api returned ${response status} ${response statustext}`); } } catch (error) { context log error(`failed to enrich event ${error message}`); // continue processing even if enrichment fails } return event; } support for questions or assistance with cdp functions, please contact mailto\ help\@chord co