Skip to content

Instantly share code, notes, and snippets.

@Enalmada
Created March 17, 2025 23:26
Show Gist options
  • Save Enalmada/6ee99ff040b74d068e498f8bd568450c to your computer and use it in GitHub Desktop.
Save Enalmada/6ee99ff040b74d068e498f8bd568450c to your computer and use it in GitHub Desktop.
OpenPanel Custom Function
/**
* Handle track event
* @param {SegmentTrackEvent} event
* @param {FunctionSettings} settings
*/
async function onTrack(event, settings) {
// Get client ID and secret from settings
const clientId = settings.clientId;
const clientSecret = settings.clientSecret;
// Special handling for screen_view events to match OpenPanel's format
let properties = event.properties || {};
// Prepare the event data for OpenPanel
const payload = {
type: "track",
payload: {
name: event.event,
properties: properties,
userId: event.userId || event.anonymousId,
timestamp: event.timestamp || new Date().toISOString()
}
};
// Add SDK information to match OpenPanel's format
payload.payload.sdk = "segment";
payload.payload.sdkVersion = "1.0.0";
// Prepare headers with authentication
const headers = {
'Content-Type': 'application/json',
'openpanel-client-id': clientId,
'openpanel-client-secret': clientSecret
};
// Add IP address for geo-location tracking if available
if (event.context?.ip) {
headers['x-client-ip'] = event.context.ip;
}
// Add user-agent for device information tracking if available
if (event.context?.userAgent) {
headers['user-agent'] = event.context.userAgent;
}
try {
const response = await fetch('https://api.openpanel.dev/track', {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
if (response.status >= 500 || response.status === 429) {
// Retry on 5xx (server errors) and 429s (rate limits)
throw new RetryError(`Failed with ${response.status}`);
}
if (!response.ok) {
throw new Error(`OpenPanel API responded with status: ${response.status}`);
}
} catch (error) {
if (error instanceof RetryError) {
throw error;
}
// Retry on connection error
throw new RetryError(error.message);
}
}
/**
* Handle identify event
* @param {SegmentIdentifyEvent} event
* @param {FunctionSettings} settings
*/
async function onIdentify(event, settings) {
const clientId = settings.clientId;
const clientSecret = settings.clientSecret;
// Prepare the identify data for OpenPanel
const payload = {
type: "identify",
payload: {
userId: event.userId,
traits: event.traits || {},
timestamp: event.timestamp || new Date().toISOString(),
sdk: "segment",
sdkVersion: "1.0.0"
}
};
// Prepare headers with authentication
const headers = {
'Content-Type': 'application/json',
'openpanel-client-id': clientId,
'openpanel-client-secret': clientSecret
};
// Add IP address for geo-location tracking if available
if (event.context?.ip) {
headers['x-client-ip'] = event.context.ip;
}
// Add user-agent for device information tracking if available
if (event.context?.userAgent) {
headers['user-agent'] = event.context.userAgent;
}
try {
const response = await fetch('https://api.openpanel.dev/track', {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
if (response.status >= 500 || response.status === 429) {
// Retry on 5xx (server errors) and 429s (rate limits)
throw new RetryError(`Failed with ${response.status}`);
}
if (!response.ok) {
throw new Error(`OpenPanel API responded with status: ${response.status}`);
}
} catch (error) {
if (error instanceof RetryError) {
throw error;
}
// Retry on connection error
throw new RetryError(error.message);
}
}
/**
* Handle page event
* @param {SegmentPageEvent} event
* @param {FunctionSettings} settings
*/
async function onPage(event, settings) {
const clientId = settings.clientId;
const clientSecret = settings.clientSecret;
// Get the page URL and title from the event
const path = event.properties?.url || event.context?.page?.url || '';
const title = event.properties?.title || event.context?.page?.title || '';
const referrer = event.properties?.referrer || event.context?.page?.referrer || '';
// OpenPanel uses a track event with name "screen_view" for page views
const payload = {
type: "track",
payload: {
name: "screen_view",
properties: {
...event.properties,
__path: path,
__title: title,
__referrer: referrer
},
userId: event.userId || event.anonymousId,
timestamp: event.timestamp || new Date().toISOString(),
sdk: "segment",
sdkVersion: "1.0.0"
}
};
// Prepare headers with authentication
const headers = {
'Content-Type': 'application/json',
'openpanel-client-id': clientId,
'openpanel-client-secret': clientSecret
};
// Add IP address for geo-location tracking if available
if (event.context?.ip) {
headers['x-client-ip'] = event.context.ip;
}
// Add user-agent for device information tracking if available
if (event.context?.userAgent) {
headers['user-agent'] = event.context.userAgent;
}
try {
const response = await fetch('https://api.openpanel.dev/track', {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
if (response.status >= 500 || response.status === 429) {
// Retry on 5xx (server errors) and 429s (rate limits)
throw new RetryError(`Failed with ${response.status}`);
}
if (!response.ok) {
throw new Error(`OpenPanel API responded with status: ${response.status}`);
}
} catch (error) {
if (error instanceof RetryError) {
throw error;
}
// Retry on connection error
throw new RetryError(error.message);
}
}
/**
* Handle group event
* @param {SegmentGroupEvent} event
* @param {FunctionSettings} settings
*/
async function onGroup(event, settings) {
const clientId = settings.clientId;
const clientSecret = settings.clientSecret;
// Prepare the group data for OpenPanel
const payload = {
type: "group",
payload: {
groupId: event.groupId,
traits: event.traits || {},
userId: event.userId || event.anonymousId,
timestamp: event.timestamp || new Date().toISOString(),
sdk: "segment",
sdkVersion: "1.0.0"
}
};
// Prepare headers with authentication
const headers = {
'Content-Type': 'application/json',
'openpanel-client-id': clientId,
'openpanel-client-secret': clientSecret
};
// Add IP address for geo-location tracking if available
if (event.context?.ip) {
headers['x-client-ip'] = event.context.ip;
}
// Add user-agent for device information tracking if available
if (event.context?.userAgent) {
headers['user-agent'] = event.context.userAgent;
}
try {
const response = await fetch('https://api.openpanel.dev/track', {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
if (response.status >= 500 || response.status === 429) {
// Retry on 5xx (server errors) and 429s (rate limits)
throw new RetryError(`Failed with ${response.status}`);
}
if (!response.ok) {
throw new Error(`OpenPanel API responded with status: ${response.status}`);
}
} catch (error) {
if (error instanceof RetryError) {
throw error;
}
// Retry on connection error
throw new RetryError(error.message);
}
}
/**
* Handle screen event
* @param {SegmentScreenEvent} event
* @param {FunctionSettings} settings
*/
async function onScreen(event, settings) {
// For mobile apps - similar to page for web
const clientId = settings.clientId;
const clientSecret = settings.clientSecret;
// Get the screen name
const path = event.name || '';
// OpenPanel uses a track event with name "screen_view" for screen views too
const payload = {
type: "track",
payload: {
name: "screen_view",
properties: {
...event.properties,
__path: path,
__title: path
},
userId: event.userId || event.anonymousId,
timestamp: event.timestamp || new Date().toISOString(),
sdk: "segment",
sdkVersion: "1.0.0"
}
};
// Prepare headers with authentication
const headers = {
'Content-Type': 'application/json',
'openpanel-client-id': clientId,
'openpanel-client-secret': clientSecret
};
// Add IP address for geo-location tracking if available
if (event.context?.ip) {
headers['x-client-ip'] = event.context.ip;
}
// Add user-agent for device information tracking if available
if (event.context?.userAgent) {
headers['user-agent'] = event.context.userAgent;
}
try {
const response = await fetch('https://api.openpanel.dev/track', {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
if (response.status >= 500 || response.status === 429) {
// Retry on 5xx (server errors) and 429s (rate limits)
throw new RetryError(`Failed with ${response.status}`);
}
if (!response.ok) {
throw new Error(`OpenPanel API responded with status: ${response.status}`);
}
} catch (error) {
if (error instanceof RetryError) {
throw error;
}
// Retry on connection error
throw new RetryError(error.message);
}
}
/**
* Handle alias event
* @param {SegmentAliasEvent} event
* @param {FunctionSettings} settings
*/
async function onAlias(event, settings) {
const clientId = settings.clientId;
const clientSecret = settings.clientSecret;
// Prepare the alias data for OpenPanel
// Note: OpenPanel's API expects profileId and alias, while Segment uses userId and previousId
const payload = {
type: "alias",
payload: {
profileId: event.userId,
alias: event.previousId,
timestamp: event.timestamp || new Date().toISOString(),
sdk: "segment",
sdkVersion: "1.0.0"
}
};
// Prepare headers with authentication
const headers = {
'Content-Type': 'application/json',
'openpanel-client-id': clientId,
'openpanel-client-secret': clientSecret
};
// Add IP address for geo-location tracking if available
if (event.context?.ip) {
headers['x-client-ip'] = event.context.ip;
}
// Add user-agent for device information tracking if available
if (event.context?.userAgent) {
headers['user-agent'] = event.context.userAgent;
}
try {
const response = await fetch('https://api.openpanel.dev/track', {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
if (response.status >= 500 || response.status === 429) {
// Retry on 5xx (server errors) and 429s (rate limits)
throw new RetryError(`Failed with ${response.status}`);
}
if (!response.ok) {
throw new Error(`OpenPanel API responded with status: ${response.status}`);
}
} catch (error) {
if (error instanceof RetryError) {
throw error;
}
// Retry on connection error
throw new RetryError(error.message);
}
}
/**
* Handle delete event
* @param {SegmentDeleteEvent} event
* @param {FunctionSettings} settings
*/
async function onDelete(event, settings) {
// OpenPanel doesn't appear to have a specific delete API
throw new EventNotSupported('delete is not supported');
}
@Enalmada
Copy link
Author

OpenPanel Integration for Segment

This gist contains code for a custom Segment Destination Function that forwards analytics data from Segment to OpenPanel. This allows you to continue using Segment's infrastructure while sending your data to OpenPanel instead of more expensive analytics platforms like Mixpanel.

Setup Instructions

1. Create a Destination Function in Segment

  1. Log in to your Segment dashboard
  2. Navigate to Destinations > Add Destination > Functions
  3. Click Create Function
  4. Give your function a name (e.g., "OpenPanel Integration")

2. Configure Settings

In the Settings section of the function creation form, add the following JSON to define the required configuration parameters:
clientId string
clientSecret sensitive

3. Add the Function Code

In the Code section, paste the entire function code from this gist.

4. Save and Configure the Function

  1. Click Create Function
  2. You'll need to enter values for the settings you defined:
    • clientId: Your OpenPanel client ID (found in your OpenPanel dashboard)
    • clientSecret: Your OpenPanel client secret (found in your OpenPanel dashboard)
  3. Select which sources should send data to this destination

5. Test the Integration

  1. Generate some test events in your Segment sources
  2. Check the Segment debugger to ensure events are being sent to your function
  3. Verify in your OpenPanel dashboard that events are being received correctly

What This Integration Supports

This custom destination function supports forwarding the following Segment event types to OpenPanel:

  • Track - Standard event tracking
  • Identify - User identification
  • Page - Page view tracking (converted to OpenPanel's screen_view events)
  • Screen - Mobile screen view tracking (converted to OpenPanel's screen_view events)
  • Group - Group/account association
  • Alias - User identity merging

The integration also passes IP addresses and user agents to OpenPanel for proper geo-location and device tracking.

Troubleshooting

If events aren't appearing in OpenPanel:

  1. Check the Segment debugger to ensure events are being sent to the function
  2. Verify your OpenPanel client ID and secret are correct
  3. Look for any error messages in the Segment logs
  4. Ensure your Segment sources are properly configured to send data to this destination

Additional Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment