SDK reference

Migrate from PostHog

If your product already calls PostHog for events, identify, and feature flags, you can move it to Databuddy without rewriting those call sites. The @databuddy/sdk package ships adapters that keep PostHog's method names and argument shapes, so most of the migration is changing three import paths.

Change the imports

bash
bun remove posthog-js posthog-node
bun add @databuddy/sdk
PostHog importDatabuddy import
posthog-js@databuddy/sdk/posthog
posthog-js/react@databuddy/sdk/posthog/react
posthog-node@databuddy/sdk/posthog/node

Browser

Pass your Databuddy client ID where PostHog expected a project token. PostHog transport options such as api_host, defaults, and person_profiles still compile and are ignored, because events always go to Databuddy.

tsx
import posthog from '@databuddy/sdk/posthog';

posthog.init(process.env.NEXT_PUBLIC_DATABUDDY_CLIENT_ID!, {
capture_exceptions: true,
trackWebVitals: true,
});

posthog.identify(user.id, { email: user.email, plan: user.plan });
posthog.capture('checkout_completed', { total: 49 });

posthog.onFeatureFlags(() => {
if (posthog.isFeatureEnabled('new-checkout')) {
  showNewCheckout();
}
});

The second argument also accepts every Databuddy configuration option, so you can turn on Web Vitals, outgoing link tracking, or path masking in the same call.

Feature flags load asynchronously, so read them inside onFeatureFlags(), which runs once flags are ready and again after every reload. Calling identify() or group() re-evaluates flags for that user and group, and the callback runs again with the new values, so targeting rules on traits such as plan apply as soon as the user signs in.

React

tsx
import {
PostHogProvider,
useFeatureFlagEnabled,
} from '@databuddy/sdk/posthog/react';

export function Providers({ children }: { children: React.ReactNode }) {
return (
  <PostHogProvider apiKey={process.env.NEXT_PUBLIC_DATABUDDY_CLIENT_ID!}>
    {children}
  </PostHogProvider>
);
}

function CheckoutButton() {
const newCheckout = useFeatureFlagEnabled('new-checkout');
return newCheckout ? <NewCheckout /> : <Checkout />;
}

usePostHog, useFeatureFlagVariantKey, useFeatureFlagPayload, and PostHogFeature are available with the same signatures.

Server

The server adapter needs a Databuddy API key with the track:events scope and the website ID the events belong to.

tsx
import { PostHog } from '@databuddy/sdk/posthog/node';
import { waitUntil } from '@vercel/functions';

const posthog = new PostHog(process.env.DATABUDDY_API_KEY!, {
websiteId: process.env.DATABUDDY_WEBSITE_ID!,
waitUntil,
});

posthog.capture({ distinctId: user.id, event: 'invoice_paid' });

const variant = await posthog.getFeatureFlag('pricing-page', user.id, {
personProperties: { plan: user.plan },
});

In serverless functions, either pass waitUntil so each capture is delivered before the function freezes, or call await posthog.shutdown() before returning. flushAt, flushInterval, requestTimeout, and disabled keep their PostHog meaning. getAllFlags and getAllFlagsAndPayloads return every flag for a user when you bootstrap flags during server rendering.

What changes

Most calls behave the same, but a few PostHog concepts map differently or have no Databuddy equivalent yet.

  • Pageviews are automatic. The tracker records pageviews and page exits on every route change, so capture('$pageview') and capture('$pageleave') are ignored to avoid counting them twice.
  • Traits are flat values. Databuddy profiles store strings, numbers, booleans, and null. Nested objects and arrays in $set or identify() are dropped, and $set_once is ignored because profiles have no set-once semantics.
  • Groups become properties. group('company', 'databuddy') adds group_company: "databuddy" to later events and to flag targeting. Group analytics and groupIdentify are not available.
  • Some calls differ or are unavailable. alias logs a warning and returns. The server adapter's groupIdentify also warns and returns; the browser adapter has no groupIdentify, so remove those browser calls. In the browser, startSessionRecording() warns and returns, but stopSessionRecording() stops Databuddy replay. Turn recordings on through Databuddy's replay option instead. Databuddy does not run in-app surveys.
  • Privacy options do not compile. Options that change what is collected, such as before_send, property_denylist, mask_all_text, persistence, and opt_out_capturing_by_default, are rejected by the types so a migration cannot quietly collect more than your PostHog setup did. Set up the matching Databuddy controls before you remove them. skipPatterns and maskPatterns skip or mask URL paths, replay: { maskText } controls page text in recordings (masked by default), and disabled keeps tracking off until a visitor consents. Databuddy has no equivalent of property_denylist or persistence, so leave sensitive properties out of the events you send.

To keep your history, open Import history in your website settings and upload a PostHog batch export of your events as JSON lines. Databuddy rebuilds pageviews and sessions from the $pageview events.

How is this guide?