Custom Events Guide
Complete guide for tracking custom business events with the OIR SDK.
- Overview
- Event Structure
- Naming Conventions
- Event Categories
- Basic Events
- Advanced Events
- Common Use Cases
- Framework Integration
- Validation
- Best Practices
Overview
Custom events allow you to track business-specific interactions that are important to your application. The SDK captures the data with the original structure and delivers it as-is to the destination configured in your dashboard.
You can define any custom events using the standard syntax, including events not listed in this documentation. You can also modify the events listed here to fit your needs—just ensure alignment on the data format with your team.
- Unlimited custom events: Define any event that fits your business needs
- Standard compliance: Follows industry-standard event structure
- Original data structure: Data is preserved exactly as sent
- Flexible delivery: Configure destinations in your dashboard
- Traffic filtering: Custom event names can be used as traffic filters in connections
_oirtrk.push([
'event',
{
event: 'event_name',
payload: {
// Event properties
},
},
]);
_oirtrk.push([
'event',
{
event: 'button_click',
payload: {
event_category: 'engagement',
event_action: 'click',
event_label: 'hero_cta',
button_text: 'Get Started',
page_url: window.location.href,
},
},
]);
Use [object]_[action] pattern with snake_case:
button_click
form_submit
video_play
file_download
page_exit
modal_open
search_performed
filter_applied
| ✅ Do | ❌ Don't |
|---|---|
button_click |
buttonClick |
form_submit |
form-submit |
video_play |
VideoPlay |
user_registration |
userRegistration |
Organize events into logical categories for better analytics and filtering:
| Category | Purpose | Examples |
|---|---|---|
| engagement | User interactions | button_click, link_click, tab_switch |
| conversion | Goal completions | signup_complete, trial_started, lead_generated |
| navigation | Page/section changes | page_scroll, section_view, menu_opened |
| media | Video, audio, image interaction | video_play, video_pause, audio_complete |
| forms | Form interactions | form_start, form_submit, form_error |
| errors | Error events | javascript_error, api_error, validation_failed |
| search | Search interactions | search_performed, filter_applied, sort_changed |
| social | Social sharing | share_facebook, share_twitter, share_email |
Track user interactions with buttons and clickable elements.
Properties:
| Property | Type | Req | Description |
|---|---|---|---|
| event | string | ✅ | Event name (e.g., button_click) |
| payload | object | ✅ | Event attributes |
| payload.event_category | string | ❌ | Event category (e.g., 'engagement') |
| payload.event_action | string | ❌ | Action performed (e.g., 'click') |
| payload.event_label | string | ❌ | Button identifier or label |
| payload.button_text | string | ❌ | Text displayed on the button |
| payload.button_id | string | ❌ | Button DOM ID or unique identifier |
| payload.page_url | string | ❌ | Current page URL |
| payload.custom_properties | object | ❌ | Additional custom data |
Example:
_oirtrk.push([
'event',
{
event: 'button_click',
payload: {
event_category: 'engagement',
event_action: 'click',
event_label: 'hero_cta',
button_text: 'Get Started',
button_id: 'cta-button',
page_url: window.location.href,
},
},
]);
Track when users complete registration or sign-up.
Properties:
| Property | Type | Req | Description |
|---|---|---|---|
| event | string | ✅ | Event name (e.g., user_registration) |
| payload | object | ✅ | Event attributes |
| payload.method | string | ❌ | Registration method (email, social, etc.) |
| payload.source | string | ❌ | Where registration was initiated |
| payload.user_type | string | ❌ | User tier or type (free, premium, etc.) |
| payload.campaign | string | ❌ | Marketing campaign identifier |
| payload.custom_properties | object | ❌ | Additional custom data |
Example:
_oirtrk.push([
'event',
{
event: 'user_registration',
payload: {
method: 'email',
source: 'landing_page',
user_type: 'premium',
campaign: 'summer_2024',
},
},
]);
Track when users interact with specific features in your application.
Properties:
| Property | Type | Req | Description |
|---|---|---|---|
| event | string | ✅ | Event name (e.g., feature_used) |
| payload | object | ✅ | Event attributes |
| payload.feature_name | string | ❌ | Name of the feature used |
| payload.usage_duration | integer | ❌ | Time spent using feature (seconds) |
| payload.success | boolean | ❌ | Whether feature usage was successful |
| payload.user_segment | string | ❌ | User segment or category |
| payload.custom_properties | object | ❌ | Additional custom data |
Example:
_oirtrk.push([
'event',
{
event: 'feature_used',
payload: {
feature_name: 'advanced_search',
usage_duration: 45,
success: true,
user_segment: 'power_user',
},
},
]);
Track user engagement with content (articles, videos, etc.).
Properties:
| Property | Type | Req | Description |
|---|---|---|---|
| event | string | ✅ | Event name (e.g., content_engagement) |
| payload | object | ✅ | Event attributes |
| payload.content_type | string | ❌ | Type of content (article, video, etc.) |
| payload.content_id | string | ❌ | Unique content identifier |
| payload.engagement_time | integer | ❌ | Time engaged with content (seconds) |
| payload.scroll_depth | integer | ❌ | Scroll depth percentage (0-100) |
| payload.custom_properties | object | ❌ | Additional custom data |
Example:
_oirtrk.push([
'event',
{
event: 'content_engagement',
payload: {
content_type: 'article',
content_id: 'article_123',
engagement_time: 120,
scroll_depth: 75,
},
},
]);
Track search queries and results within your application.
Properties:
| Property | Type | Req | Description |
|---|---|---|---|
| event | string | ✅ | Event name (e.g., product_search) |
| payload | object | ✅ | Event attributes |
| payload.search_term | string | ❌ | User's search query |
| payload.results_count | integer | ❌ | Number of results returned |
| payload.filters_applied | array | ❌ | Array of filters applied |
| payload.search_duration | float | ❌ | Time to complete search (seconds) |
| payload.user_segment | string | ❌ | User segment or category |
| payload.custom_properties | object | ❌ | Additional custom data |
| payload.custom_properties.search_source | string | ❌ | Where search was initiated (header, page) |
| payload.custom_properties.autocomplete_used | boolean | ❌ | Whether autocomplete was used |
Example:
_oirtrk.push([
'event',
{
event: 'product_search',
payload: {
search_term: 'wireless headphones',
filters_applied: ['brand', 'price_range'],
results_count: 24,
search_duration: 3.2,
user_segment: 'premium',
custom_properties: {
search_source: 'header',
autocomplete_used: true,
suggestions_shown: 5,
},
},
},
]);
Track users progressing through multi-step flows (checkout, onboarding, etc.).
Step Started Properties:
| Property | Type | Req | Description |
|---|---|---|---|
| event | string | ✅ | Event name (e.g., checkout_process) |
| payload | object | ✅ | Event attributes |
| payload.step | string | ❌ | Name of current step |
| payload.step_number | integer | ❌ | Current step number (1-based) |
| payload.total_steps | integer | ❌ | Total number of steps in process |
| payload.process_id | string | ❌ | Unique identifier for this process |
| payload.started_at | string | ❌ | ISO timestamp when step started |
| payload.completed_at | string | ❌ | ISO timestamp when step completed |
| payload.step_duration | integer | ❌ | Time spent on step (seconds) |
| payload.custom_properties | object | ❌ | Additional custom data |
Example - Step Started:
_oirtrk.push([
'event',
{
event: 'checkout_process',
payload: {
step: 'shipping_info',
step_number: 1,
total_steps: 4,
process_id: 'checkout_123',
started_at: new Date().toISOString(),
},
},
]);
Example - Step Completed:
_oirtrk.push([
'event',
{
event: 'checkout_process',
payload: {
step: 'payment_method',
step_number: 2,
total_steps: 4,
process_id: 'checkout_123',
step_duration: 45,
completed_at: new Date().toISOString(),
},
},
]);
Process Abandoned Properties:
| Property | Type | Req | Description |
|---|---|---|---|
| event | string | ✅ | Event name (e.g., checkout_abandoned) |
| payload | object | ✅ | Event attributes |
| payload.last_step | string | ❌ | Name of last completed step |
| payload.step_number | integer | ❌ | Last step number reached |
| payload.total_steps | integer | ❌ | Total steps in process |
| payload.process_id | string | ❌ | Unique process identifier |
| payload.abandonment_reason | string | ❌ | Reason for abandonment (timeout, etc.) |
| payload.custom_properties | object | ❌ | Additional custom data |
Example - Process Abandoned:
_oirtrk.push([
'event',
{
event: 'checkout_abandoned',
payload: {
last_step: 'payment_method',
step_number: 2,
total_steps: 4,
process_id: 'checkout_123',
abandonment_reason: 'timeout',
},
},
]);
_oirtrk.push([
'event',
{
event: 'page_scroll',
payload: {
scroll_depth: 75,
page: '/products',
time_on_page: 120,
max_scroll_reached: 75,
},
},
]);
_oirtrk.push([
'event',
{
event: 'time_spent',
payload: {
section: 'product_details',
duration: 45,
page: '/product/123',
interactions_count: 8,
},
},
]);
_oirtrk.push([
'event',
{
event: 'video_play',
payload: {
video_id: 'demo_video_001',
video_title: 'Product Demo',
video_duration: 180,
current_time: 0,
source: 'product_page',
},
},
]);
_oirtrk.push([
'event',
{
event: 'lead_generated',
payload: {
lead_type: 'newsletter_signup',
source: 'popup',
campaign: 'summer_promotion',
conversion_value: 5.0,
},
},
]);
_oirtrk.push([
'event',
{
event: 'trial_started',
payload: {
plan: 'premium',
trial_duration: 14,
source: 'pricing_page',
initial_value: 29.99,
},
},
]);
_oirtrk.push([
'event',
{
event: 'file_download',
payload: {
file_name: 'product_catalog.pdf',
file_type: 'pdf',
file_size_kb: 2048,
download_source: 'resources_page',
},
},
]);
_oirtrk.push([
'event',
{
event: 'javascript_error',
payload: {
error_message: 'Cannot read property of undefined',
error_file: 'app.js',
error_line: 42,
error_column: 15,
user_agent: navigator.userAgent,
page_url: window.location.href,
},
},
]);
_oirtrk.push([
'event',
{
event: 'api_error',
payload: {
endpoint: '/api/users',
method: 'GET',
status_code: 500,
error_message: 'Internal Server Error',
response_time: 1200,
retry_count: 2,
},
},
]);
_oirtrk.push([
'event',
{
event: 'form_validation_error',
payload: {
form_id: 'checkout_form',
field_name: 'email',
error_type: 'invalid_format',
error_message: 'Please enter a valid email',
},
},
]);
_oirtrk.push([
'event',
{
event: 'search_performed',
payload: {
search_term: 'wireless headphones',
results_count: 24,
search_location: 'header',
filters_active: false,
},
},
]);
_oirtrk.push([
'event',
{
event: 'filter_applied',
payload: {
filter_type: 'price_range',
filter_value: '50-100',
results_before: 100,
results_after: 24,
page: '/products',
},
},
]);
_oirtrk.push([
'event',
{
event: 'added_to_wishlist',
payload: {
item_id: 'ITM-12345',
item_name: 'Awesome T-Shirt',
item_brand: 'Nike',
item_category: 'Apparel',
price: 29.99,
custom_attributes: {
wishlist_id: 'wishlist_001',
user_segment: 'premium',
},
},
},
]);
import { useEffect } from 'react';
function ProductCard({ product }) {
const handleView = () => {
window._oirtrk.push([
'event',
{
event: 'product_viewed',
payload: {
product_id: product.id,
product_name: product.name,
category: product.category,
price: product.price,
view_source: 'product_card',
},
},
]);
};
const handleAddToCart = () => {
window._oirtrk.push([
'event',
{
event: 'add_to_cart_clicked',
payload: {
product_id: product.id,
product_name: product.name,
price: product.price,
quantity: 1,
},
},
]);
};
useEffect(() => {
// Track card impression
window._oirtrk.push([
'event',
{
event: 'product_card_impression',
payload: {
product_id: product.id,
position: product.index,
list_name: 'product_grid',
},
},
]);
}, [product.id]);
return (
<div onClick={handleView}>
<h3>{product.name}</h3>
<p>${product.price}</p>
<button onClick={handleAddToCart}>Add to Cart</button>
</div>
);
}
<template>
<div class="product-card">
<button @click="trackClick">
</button>
<div @mouseenter="trackHover">
<img :src="product.image" @load="trackImageLoad" />
</div>
</div>
</template>
<script>
export default {
props: {
product: Object,
buttonText: String,
},
methods: {
trackClick() {
window._oirtrk.push([
'event',
{
event: 'button_click',
payload: {
button_id: this.buttonId,
page: this.$route.path,
component: 'CustomButton',
product_id: this.product.id,
},
},
]);
},
trackHover() {
window._oirtrk.push([
'event',
{
event: 'product_hover',
payload: {
product_id: this.product.id,
product_name: this.product.name,
hover_location: 'product_card',
},
},
]);
},
trackImageLoad() {
window._oirtrk.push([
'event',
{
event: 'product_image_loaded',
payload: {
product_id: this.product.id,
image_url: this.product.image,
load_time: performance.now(),
},
},
]);
},
},
mounted() {
// Track component mount
window._oirtrk.push([
'event',
{
event: 'component_mounted',
payload: {
component_name: 'ProductCard',
product_id: this.product.id,
},
},
]);
},
};
</script>
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-product-card',
templateUrl: './product-card.component.html',
})
export class ProductCardComponent implements OnInit {
@Input() product: any;
ngOnInit() {
this.trackImpression();
}
trackImpression() {
(window as any)._oirtrk.push([
'event',
{
event: 'product_impression',
payload: {
product_id: this.product.id,
product_name: this.product.name,
category: this.product.category,
},
},
]);
}
trackClick() {
(window as any)._oirtrk.push([
'event',
{
event: 'product_click',
payload: {
product_id: this.product.id,
click_source: 'product_card',
},
},
]);
}
}
function validateCustomEvent(event, data) {
if (!event || typeof event !== 'string') {
throw new Error('Event name is required and must be a string');
}
if (!data || typeof data !== 'object') {
throw new Error('Event data must be an object');
}
// Validate common properties
if (data.event_category && typeof data.event_category !== 'string') {
throw new Error('event_category must be a string');
}
if (data.value !== undefined && typeof data.value !== 'number') {
throw new Error('value must be a number');
}
// Validate naming convention
if (!/^[a-z][a-z0-9_]*$/.test(event)) {
console.warn(`Event name "${event}" does not follow naming convention (snake_case)`);
}
return true;
}
try {
const eventName = 'button_click';
const eventData = {
event_category: 'engagement',
event_action: 'click',
value: 1,
};
validateCustomEvent(eventName, eventData);
window._oirtrk.push([
'event',
{
event: eventName,
payload: eventData,
},
]);
} catch (error) {
console.error('Custom event validation failed:', error);
}
- Use
snake_casefor all event names - Be descriptive but concise
- Follow a consistent pattern across your application
- Start with the object/noun, followed by the action/verb
- Include relevant context in your payload
- Use appropriate data types
- Avoid including sensitive personal information
// ❌ BAD - Unnecessary data
{
event: 'button_click',
payload: {
unnecessary_field_1: null,
unnecessary_field_2: undefined,
unused_metadata: {}
}
}
// ✅ GOOD - Clean payload
{
event: 'button_click',
payload: {
button_id: 'cta-button',
page: '/home'
}
}
// Use the same property names everywhere
{
event: 'product_viewed',
payload: {
product_id: product.sku, // Always use product_id
product_name: product.title, // Always use product_name
price: product.price,
quantity: product.qty
}
}
- Don't include PII in event data—use the identify function instead
- Follow GDPR, CCPA regulations
- Only track necessary data
- Ecommerce Events - Ecommerce event tracking
- User Identification Guide - Identify users
- Installation Guide - Install and configure the SDK