Learn how the integration between Singular and PostHog works.
Overview
The PostHog attribution integration allows Singular to send install, re-engagement, and custom event postbacks directly to your PostHog project in real time. Singular uses PostHog's capture endpoint to deliver individual events, enabling you to analyze attribution data alongside your product analytics in PostHog.
Because PostHog requires a distinct_id on every event, you must pass your PostHog user identifier to the Singular SDK via Global Properties before events are sent.
Prerequisites
- Ensure you have access to your PostHog project.
- Obtain your PostHog Project API Key (also called the Environment API Key). Find it in your PostHog account under Settings > Environment Details > Variables at us.posthog.com.
- Install and initialize the PostHog SDK in your app before the Singular SDK so that the distinct ID is available at startup.
- Retrieve the PostHog
distinct_idfor the current user usingposthog.get_distinct_id(). See PostHog's documentation. - Pass the
distinct_idvalue to the Singular SDK as a Global Property with the keyposthog_distinct_id(see Passing the PostHog Distinct ID below).
Important: Use a stable, persistent user identifier as the posthog_distinct_id — such as a user account ID or hashed email. Avoid device identifiers (GAID, IDFA, IDFV, etc.) as they can change when users reset their advertising ID, reinstall the app, or switch devices, which would cause inflated user counts in PostHog.
Passing the PostHog Distinct ID via Global Properties
The Singular SDK's Global Properties feature lets you attach a custom key-value pair to every session and event automatically. You must set posthog_distinct_id so Singular can populate the distinct_id field in all postbacks to PostHog.
Option 1: Set at SDK Initialization (Recommended)
Set the property before calling Singular.init() so it is included in the very first session.
val distinctId = PostHog.getDistinctId() // Retrieve from PostHog SDK
val config = SingularConfig("YOUR_SDK_KEY", "YOUR_SDK_SECRET")
.withGlobalProperty("posthog_distinct_id", distinctId, true)
Singular.init(this, config)
let distinctId = PostHogSDK.shared.getDistinctId() // Retrieve from PostHog SDK
let config = SingularConfig(apiKey: "YOUR_SDK_KEY", andSecret: "YOUR_SDK_SECRET")!
config.setGlobalProperty("posthog_distinct_id", withValue: distinctId, overrideExisting: true)
Singular.start(config)
// Retrieve from PostHog SDK
string distinctId = PostHog.GetDistinctId();
// Set global property before SDK initialization
// Disable "Initialize On Awake" in the SingularSDKObject Inspector and call manually
SingularSDK.SetGlobalProperty("posthog_distinct_id", distinctId, true);
SingularSDK.InitializeSingularSDK();
// New Architecture (React Native 0.76+)
import NativeSingular from 'singular-react-native/js/NativeSingular';
async function initializeSDK() {
const distinctId = posthog.get_distinct_id(); // Retrieve from PostHog SDK
const config: SingularConfig = {
apikey: 'YOUR_SDK_KEY',
secret: 'YOUR_SDK_SECRET',
globalProperties: {
posthog_distinct_id: {
Key: 'posthog_distinct_id',
Value: distinctId,
OverrideExisting: true
}
}
};
NativeSingular.init(config);
}
// Old Architecture
// import { Singular, SingularConfig } from 'singular-react-native';
//
// async function initializeSDK() {
// const distinctId = posthog.get_distinct_id();
// const config = new SingularConfig('YOUR_SDK_KEY', 'YOUR_SDK_SECRET')
// .withGlobalProperty('posthog_distinct_id', distinctId, true);
// Singular.init(config);
// }
import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
import 'package:singular_flutter_sdk/singular_global_property.dart';
void initializeSDK() {
final String distinctId = posthog.getDistinctId(); // Retrieve from PostHog SDK
SingularConfig config = SingularConfig('YOUR_SDK_KEY', 'YOUR_SDK_SECRET');
config.globalProperties = [
SingularGlobalProperty('posthog_distinct_id', distinctId, true)
];
Singular.start(config);
}
var distinctId = posthog.get_distinct_id(); // Retrieve from PostHog SDK
var config = new cordova.plugins.SingularCordovaSdk.SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
);
config.withGlobalProperty('posthog_distinct_id', distinctId, true);
cordova.plugins.SingularCordovaSdk.init(config);
Option 2: Set After SDK Initialization
If the PostHog distinct ID is not yet available at initialization (e.g., it is resolved asynchronously after login), set the property after initialization using setGlobalProperty().
val success = Singular.setGlobalProperty("posthog_distinct_id", PostHog.getDistinctId(), true)
if (!success) {
Log.e("Singular", "Failed to set posthog_distinct_id — check if the 5-property limit has been reached.")
}
let success = Singular.setGlobalProperty("posthog_distinct_id", andValue: PostHogSDK.shared.getDistinctId(), overrideExisting: true)
if !success {
print("Failed to set posthog_distinct_id — check if the 5-property limit has been reached.")
}
// Call this as soon as the distinct ID is available (e.g., after user login)
string distinctId = PostHog.GetDistinctId();
bool success = SingularSDK.SetGlobalProperty("posthog_distinct_id", distinctId, true);
if (!success)
{
Debug.LogError("Failed to set posthog_distinct_id — check if the 5-property limit has been reached.");
}
// New Architecture (React Native 0.76+)
import NativeSingular from 'singular-react-native/js/NativeSingular';
// Call this as soon as the distinct ID is available (e.g., after user login)
const distinctId = posthog.get_distinct_id();
const success = await NativeSingular.setGlobalProperty('posthog_distinct_id', distinctId, true);
if (!success) {
console.error('Failed to set posthog_distinct_id — check if the 5-property limit has been reached.');
}
// Old Architecture
// import { Singular } from 'singular-react-native';
// const success = await Singular.setGlobalProperty('posthog_distinct_id', distinctId, true);
// if (!success) {
// console.error('Failed to set posthog_distinct_id — check if the 5-property limit has been reached.');
// }
import 'package:singular_flutter_sdk/singular.dart';
// Call this as soon as the distinct ID is available (e.g., after user login)
final String distinctId = posthog.getDistinctId();
Singular.setGlobalProperty('posthog_distinct_id', distinctId, true);
// Call this as soon as the distinct ID is available (e.g., after user login)
var distinctId = posthog.get_distinct_id();
cordova.plugins.SingularCordovaSdk.setGlobalProperty(
'posthog_distinct_id',
distinctId,
true,
function(success) {
if (!success) {
console.error('Failed to set posthog_distinct_id — check if the 5-property limit has been reached.');
}
}
);
Verifying the property is set: Go to Reports > Export Logs > Events in the Singular platform and include the Global Properties dimension. If posthog_distinct_id appears there, Singular will automatically populate distinct_id in the postbacks to PostHog.
Setup Instructions
- Integrate the Singular SDK into your app (see Singular SDK Integration Guide).
- Complete the prerequisites above, including passing the PostHog distinct ID via Global Properties.
- In the Singular platform, go to Attribution > Partner Configuration.
- Click Add a Partner. In the search box, type and select PostHog. See Partner Configuration for general instructions.
-
Provide these details in the PostHog Configuration shelf and click Save.
Field Description PostHog Project API Key Your PostHog project's API key (Environment API Key). Found under Settings > Environment Details > Variables in your PostHog account. Install or Re-engagement Choose whether to send re-engagement event data in the postback.
After the integration is complete, install and event data from Singular will appear in your PostHog project. See PostHog's documentation on user identification for more details.
Singular sends events to PostHog using the PostHog capture endpoint (https://us.i.posthog.com/i/v0/e/). Each postback includes the event name, timestamp, platform, device identifiers, source, and any other configured macros. If posthog_distinct_id is not set, PostHog will reject the postback with a 400 error because distinct_id is a required field.
Comments
Please sign in to leave a comment.