Custom events
12 min
a custom event is any event chord receives that is not part of the chord event tracking docid\ gefjsxos276n6gs40xabr — either an event whose name is not one of the canonical events ( order completed , product viewed , checkout started , product added , …) or a canonical event carrying non standard properties this document explains what happens to those events how they are processed, what transformation (if any) is applied, and which destinations forward them the short answer chord does not validate events against the tracking plan, and it does not reject, quarantine, or strip events for being non compliant every event that is ingested flows through the same pipeline custom events are processed as is , with only the minimal, optional transformations described below, and are forwarded to destinations what a custom event ultimately looks like at a destination depends entirely on that destination, because each destination decides how to map an event to its own schema some forward custom events verbatim; some only act on a fixed set of canonical events and silently ignore everything else the tracking plan is a convention, not a schema validator it defines a set of canonical event names and snake case field names ( email , phone , first name , address1 , zipcode , …) destination code uses these canonical names as fallback lookups when extracting properties there is no component anywhere in ingest, rotor, or the destination functions that checks an incoming event against the plan and blocks it for failing to match how an event is processed every event — canonical or custom — travels the same path chord sdk / s2s → ingest → kafka → rotor (function chain) → destination inside rotor, the function chain applies a series of transformations none of these steps inspect whether an event complies with the tracking plan they treat a custom event exactly as they treat a canonical one step what it does applies to custom events? geo enrichment resolves context ip to context geo (browser events only) yes, identically user recognition merges anonymous events with known identity (if configured) yes, identically connection filters drop/allow rules matching on event type, name, or property values yes — can drop a custom event if a rule names it, but not because it is custom property mappings customer defined field remapping ( @jitsu/mapping engine ) yes, identically consent filtering drops events lacking required consent categories yes, identically deduplication skips repeat messages within a redis backed window (if enabled) yes, identically udfs custom javascript transforms that can modify, drop, or fan out events yes, identically destination function maps the event to the destination's api/schema destination specific — see below the only places an event is ever dropped are a connection filter rule, consent enforcement, a udf returning null / "drop" , deduplication, or a destination deliberately not firing a custom event is never dropped merely for being absent from the tracking plan how destinations handle custom events this is where behavior diverges there are two delivery modes, and within each, destinations fall into a few distinct patterns cloud mode destinations (server side) these run as functions in rotor the dominant pattern is passthrough the event name and properties are forwarded with little or no transformation, so custom events arrive intact pattern behavior for custom events examples passthrough event forwarded as is; the event name is used directly webhook, segment, mongodb, data warehouse / bulker, june, intercom, heap, mixpanel, posthog canonical mapping + fallback canonical events are translated to the destination's standard event; unmapped (custom) events still go through via a default fallback ga4 (custom name is handelized/kebab cased, standard props filtered), facebook conversions (custom name passed through unchanged) configurable mapping the destination acts only on events the customer has explicitly mapped amplitude (supports a $drop mapping), braze (purchase event list, otherwise generic track) identity / fixed purpose built around identify / group or a small set of events; track style custom events get generic treatment or none klaviyo (special cases order completed , all other tracks become generic metrics), hubspot, salesforce data warehouse destinations are the most permissive a custom track event simply gets its own table named after the event (e g my custom event ), or its raw document inserted (mongodb) even canonical mapping destinations like ga4 and facebook do not drop custom events — they fall through to a default branch that still sends the event under its own (optionally normalized) name device mode destinations (browser plugins) device mode plugins ( libs/jitsu js/src/destination plugins/ ) fire directly from the browser via each vendor's pixel/sdk per the forwarding policy above, every plugin handles the unrecognized event case explicitly — it either forwards the event or logs an explicit skip pattern behavior for custom events examples passthrough an unrecognized event name is forwarded to the vendor sdk under its own name (or as a generic "custom event") facebook pixel ( tracksinglecustom ), tiktok pixel, pinterest tag, insider pixel, bing ads, stackadapt pixel fixed taxonomy (explicit skip) only the platform's recognized events fire; unrecognized events are skipped with a console warn snap pixel, northbeam pixel, retention com, postie, spotify ad analytics pixel config driven fires only for events the customer mapped in the plugin's configuration; everything else (including unmapped canonical events) is skipped twitter ads ( eventmapping ), google ads ( clickconversions / pageloadconversions ) if you need to send high cardinality or custom named events to an ad platform, choose a passthrough plugin fixed taxonomy plugins (snap, spotify, etc ) are scoped to the conversion events the vendor's backend recognizes; they will skip anything outside that set and log a warning so the skip is visible custom property mappings custom property mappings are a separate mechanism from custom event names when a customer maps a property in hub (e g properties customer type → customer type ), the dispatcher writes it as a top level key on the event a plugin only carries it into the outgoing payload if the plugin spreads getcustommappedproperties / getcustomdataproperties — in which case the custom property reaches the destination even on a fixed event set not every plugin does this plugins that build a hardcoded payload from formatters ( formatorderdata , etc ) without that spread silently drop custom mapped properties today the plugins that forward them are facebook pixel , snap pixel , tiktok pixel , pinterest tag , twitter ads , bing ads , and google ads ; fixed taxonomy plugins such as spotify ad analytics pixel , postie , retention , northbeam pixel , sfmc collect , thetradedesk pixel , and yahoo pixel do not (pii fields like email and phone are excluded from this passthrough regardless ) custom event forwarding policy across destinations, the behaviors above are not all intentional — some are genuine vendor constraints and some are implementation gaps to keep current and future destinations consistent, chord follows one rule every destination must handle the unrecognized event case explicitly it either forwards the event or logs an explicit skip — it must never fall through silently a silent fall through (a switch with no default ) is the thing to avoid it drops events with no signal in the logs, so a custom event that never arrives at a destination looks identical to one that was delivered that is the worst outcome for debugging two buckets each destination belongs to one of two buckets, chosen by what the receiving platform actually does with an unrecognized event — not by whether the event can be transmitted bucket default for unrecognized events why passthrough capable — general purpose collectors and platforms with a real custom event api forward the event under its own name (with custom mapped properties) the platform stores or acts on arbitrary events, so forwarding is useful fixed taxonomy — attribution pixels whose backend only recognizes a closed set of events skip with an explicit console warn the platform discards or rejects unrecognized events, so forwarding would be a no op that falsely reads as "delivered" examples of passthrough capable webhook, data warehouse / bulker, mongodb, mixpanel, posthog, ga4, facebook (pixel via tracksinglecustom / conversions api via a custom event name ), tiktok, pinterest, insider, bing examples of fixed taxonomy snap (closed enum + five custom event n slots), spotify ad analytics ( view / purchase / lead only), postie (snowplow schemas it has registered), retention com (fixed case sensitive "reclaim" names), northbeam (custom goals must be pre registered vendor side) decide by platform, not by transport a destination's bucket is a property of the platform , and the pixel (device mode) and conversions api (server side) implementations for the same platform must make the same choice server side / capi delivery is permissive by construction — event name is just a string field in an http body, with no client sdk enum to satisfy — so it is tempting to forward everything server side but transmitting an event the platform ignores is still a no op; it just happens over http instead of a js call facebook accepts custom events on both its pixel and its conversions api, so both forward a fixed taxonomy platform like snap would skip on both there is no case where the capi side should forward what the pixel side drops for the same platform why custom mapped properties are always forwarded regardless of bucket the bucket decision is about the event name custom properties mapped by the customer are merged into every outgoing call (see below), because a fixed set platform still accepts extra fields on the events it does recognize practical guidance sending a custom event to a warehouse or a passthrough destination? it will arrive with its original name and properties no tracking plan compliance is required sending a custom event to an ad platform via a device mode pixel? confirm the plugin is passthrough or that you have mapped the event in the plugin's configuration fixed taxonomy plugins will skip it and log a console warn want a custom event to look like a canonical one at a destination? use a property mapping or a udf in the connection to reshape it before it reaches the destination function want to stop a custom event from reaching a destination? add a connection filter rule, or drop it in a udf — there is no automatic rejection to rely on summary chord is a flexible, schema less pipeline it accepts any well formed event, transforms it only as explicitly configured, and forwards it the tracking plan provides canonical names that destinations prefer when present, but compliance is never enforced custom events are first class citizens of the pipeline; the only meaningful variation is how individual destinations choose to map — or ignore — events they were not built to recognize