) => void;
+};
+
+export function PostHogDeploymentField({
+ values,
+ onChange,
+}: PostHogDeploymentFieldProps) {
+ const { __ } = useTranslate();
+
+ const region = values.region ?? "";
+ let deployment = "";
+ if (region === "US" || region === "EU") {
+ deployment = region;
+ } else if ("instanceUrl" in values) {
+ deployment = "SELF_HOSTED";
+ }
+
+ return (
+ <>
+
+
+
+
+ {deployment === "SELF_HOSTED" && (
+ ) =>
+ onChange({ instanceUrl: e.target.value })}
+ required
+ />
+ )}
+ >
+ );
+}
+
+// isPostHogDeploymentSelected reports whether a valid PostHog deployment has
+// been chosen: a Cloud region (us/eu) or a non-empty self-hosted instance URL.
+export function isPostHogDeploymentSelected(
+ values: Record,
+): boolean {
+ return (
+ values.region === "US"
+ || values.region === "EU"
+ || !!values.instanceUrl?.trim()
+ );
+}
diff --git a/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx b/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx
index 661d311fd..69292f5b0 100644
--- a/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx
+++ b/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx
@@ -78,7 +78,6 @@ const thirdParties: Record>> = {
OPENAI: OpenAI,
PAGERDUTY: PagerDuty,
POSTHOG: PostHog,
- POSTHOG_SELF_HOSTED: PostHog,
RESEND: Resend,
SENTRY: Sentry,
SLACK: Slack,
diff --git a/pkg/accessreview/drivers/posthog_test.go b/pkg/accessreview/drivers/posthog_test.go
index eb0e82e55..b285fe296 100644
--- a/pkg/accessreview/drivers/posthog_test.go
+++ b/pkg/accessreview/drivers/posthog_test.go
@@ -34,7 +34,7 @@ func TestPostHogDriverListAccounts(t *testing.T) {
rec := newRecorder(t, "testdata/posthog", "POSTHOG_PERSONAL_API_KEY")
client := newVCRClient(rec, bearerAuth(os.Getenv("POSTHOG_PERSONAL_API_KEY")))
- records, err := NewPostHogDriver(client, "https://app.posthog.com").ListAccounts(context.Background())
+ records, err := NewPostHogDriver(client, "https://us.posthog.com").ListAccounts(context.Background())
require.NoError(t, err)
require.Len(t, records, 3)
@@ -165,7 +165,7 @@ func TestPostHogNameResolver(t *testing.T) {
client := &http.Client{Transport: &hostRewriter{target: srv.URL}}
- got, err := NewPostHogNameResolver(client, "https://app.posthog.com").ResolveInstanceName(context.Background())
+ got, err := NewPostHogNameResolver(client, "https://us.posthog.com").ResolveInstanceName(context.Background())
if tc.wantErr {
require.Error(t, err)
return
diff --git a/pkg/accessreview/drivers/testdata/posthog.yaml b/pkg/accessreview/drivers/testdata/posthog.yaml
index 56d98e90a..fff611f80 100644
--- a/pkg/accessreview/drivers/testdata/posthog.yaml
+++ b/pkg/accessreview/drivers/testdata/posthog.yaml
@@ -7,7 +7,7 @@ interactions:
proto_major: 1
proto_minor: 1
content_length: 0
- host: app.posthog.com
+ host: us.posthog.com
form:
limit:
- "100"
@@ -16,7 +16,7 @@ interactions:
headers:
Accept:
- application/json
- url: https://app.posthog.com/api/organizations/@current/members/?limit=100&order=-joined_at
+ url: https://us.posthog.com/api/organizations/@current/members/?limit=100&order=-joined_at
method: GET
response:
proto: HTTP/2.0
diff --git a/pkg/connector/provider/builtin.go b/pkg/connector/provider/builtin.go
index 1f680b761..cb0128dd1 100644
--- a/pkg/connector/provider/builtin.go
+++ b/pkg/connector/provider/builtin.go
@@ -46,7 +46,6 @@ func NewBuiltinRegistry() *Registry {
onePasswordRegistration(),
openaiRegistration(),
posthogRegistration(),
- posthogSelfHostedRegistration(),
pagerdutyRegistration(),
resendRegistration(),
sentryRegistration(),
diff --git a/pkg/connector/provider/posthog.go b/pkg/connector/provider/posthog.go
index 465087fd4..aa016e40d 100644
--- a/pkg/connector/provider/posthog.go
+++ b/pkg/connector/provider/posthog.go
@@ -24,13 +24,15 @@ import (
"go.probo.inc/probo/pkg/coredata"
)
-// posthogRegistration is PostHog Cloud (US + EU). OAuth is the preferred
-// path: oauth.posthog.com is PostHog's region-agnostic OAuth + API gateway,
-// so one app serves both regions and the driver reaches the customer's data
-// through it without a per-connection host. An API-key fallback is also
-// supported, but personal API keys are region-pinned, so it requires the
-// customer to pick their region (us/eu). Self-hosted instances are a separate
-// provider (POSTHOG_SELF_HOSTED).
+// posthogRegistration is PostHog — Cloud (US + EU) and self-hosted, under one
+// provider. OAuth (CIMD public client) is the preferred path for Cloud: the
+// region-agnostic oauth.posthog.com gateway handles the handshake for both
+// regions, after which the driver resolves the data region (us/eu) itself,
+// since that gateway does not serve the data API. An API-key fallback covers
+// both deployments: Cloud personal API keys are region-pinned (the customer
+// picks us/eu) and self-hosted connections carry an instance URL. Both store a
+// single data-host BaseURL; cloud OAuth connections leave it empty for lazy
+// region probing.
func posthogRegistration() *Registration {
return &Registration{
Provider: coredata.ConnectorProviderPostHog,
@@ -56,8 +58,13 @@ func posthogRegistration() *Registration {
// token surfaces on the first ListAccounts.
SupportsAPIKey: true,
+ // API-key connections are either PostHog Cloud (a region, us/eu) or
+ // self-hosted (an instance URL). The two are mutually exclusive, so
+ // neither is individually Required; apiKeyConnectorSettings enforces
+ // that exactly one is supplied.
ExtraSettings: []ExtraSetting{
- {Key: "region", Label: "Region", Required: true},
+ {Key: "region", Label: "Region"},
+ {Key: "instanceUrl", Label: "Instance URL"},
},
NewDriver: func(_ context.Context, c *http.Client, conn *coredata.Connector, _ *log.Logger) (drivers.Driver, error) {
diff --git a/pkg/connector/provider/posthog_self_hosted.go b/pkg/connector/provider/posthog_self_hosted.go
deleted file mode 100644
index 2ab535ae4..000000000
--- a/pkg/connector/provider/posthog_self_hosted.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2026 Probo Inc .
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-package provider
-
-import (
- "context"
- "fmt"
- "net/http"
-
- "go.gearno.de/kit/log"
- "go.probo.inc/probo/pkg/accessreview/drivers"
- "go.probo.inc/probo/pkg/coredata"
-)
-
-// posthogSelfHostedRegistration is customer-hosted PostHog: API-key plus an
-// operator-supplied instance URL (Metabase/Grafana style). It shares the
-// PostHog driver and name resolver, pointed at the instance's BaseURL. OAuth
-// is deliberately not offered here — a single static authorization URL cannot
-// serve arbitrary per-customer instances, so self-hosted OAuth is a separate
-// future effort. Cloud PostHog (POSTHOG) owns the OAuth path.
-func posthogSelfHostedRegistration() *Registration {
- return &Registration{
- Provider: coredata.ConnectorProviderPostHogSelfHosted,
- DisplayName: "PostHog (Self-Hosted)",
-
- SupportsAPIKey: true,
- ExtraSettings: []ExtraSetting{
- {Key: "instanceUrl", Label: "Instance URL", Required: true},
- },
-
- NewDriver: func(_ context.Context, c *http.Client, conn *coredata.Connector, _ *log.Logger) (drivers.Driver, error) {
- s, err := coredata.ConnectorSettings[coredata.PostHogConnectorSettings](conn)
- if err != nil {
- return nil, fmt.Errorf("cannot read posthog self-hosted connector settings: %w", err)
- }
-
- // Never fall back to the cloud gateway for a self-hosted
- // connector — the instance URL is required at creation time.
- if s.BaseURL == "" {
- return nil, fmt.Errorf("cannot create posthog self-hosted driver: instance URL is required")
- }
-
- return drivers.NewPostHogDriver(c, s.BaseURL), nil
- },
- NewNameResolver: func(ctx context.Context, c *http.Client, conn *coredata.Connector, logger *log.Logger) drivers.NameResolver {
- s, err := coredata.ConnectorSettings[coredata.PostHogConnectorSettings](conn)
- if err != nil {
- logger.ErrorCtx(ctx, "cannot read posthog self-hosted connector settings", log.Error(err))
- return nil
- }
-
- if s.BaseURL == "" {
- return nil
- }
-
- return drivers.NewPostHogNameResolver(c, s.BaseURL)
- },
- }
-}
diff --git a/pkg/coredata/connector_provider.go b/pkg/coredata/connector_provider.go
index df0ffe073..3dc6915e8 100644
--- a/pkg/coredata/connector_provider.go
+++ b/pkg/coredata/connector_provider.go
@@ -26,41 +26,35 @@ const (
ConnectorProviderGoogleWorkspace ConnectorProvider = "GOOGLE_WORKSPACE"
ConnectorProviderLinear ConnectorProvider = "LINEAR"
// _ ConnectorProvider = "FIGMA" — formerly Figma; removed (no driver, no OAuth config, no usage)
- ConnectorProviderOnePassword ConnectorProvider = "ONE_PASSWORD"
- ConnectorProviderHubSpot ConnectorProvider = "HUBSPOT"
- ConnectorProviderDocuSign ConnectorProvider = "DOCUSIGN"
- ConnectorProviderNotion ConnectorProvider = "NOTION"
- ConnectorProviderBrex ConnectorProvider = "BREX"
- ConnectorProviderTally ConnectorProvider = "TALLY"
- ConnectorProviderCloudflare ConnectorProvider = "CLOUDFLARE"
- ConnectorProviderGrafana ConnectorProvider = "GRAFANA"
- ConnectorProviderOpenAI ConnectorProvider = "OPENAI"
- ConnectorProviderPostHog ConnectorProvider = "POSTHOG"
- // ConnectorProviderPostHogSelfHosted is a distinct provider for
- // customer-hosted PostHog instances: API-key + operator-supplied
- // instance URL. Cloud PostHog (POSTHOG) carries the OAuth path; a
- // separate provider keeps the per-connection instance URL out of the
- // shared OAuth flow.
- ConnectorProviderPostHogSelfHosted ConnectorProvider = "POSTHOG_SELF_HOSTED"
- ConnectorProviderSentry ConnectorProvider = "SENTRY"
- ConnectorProviderSupabase ConnectorProvider = "SUPABASE"
- ConnectorProviderGitHub ConnectorProvider = "GITHUB"
- ConnectorProviderIntercom ConnectorProvider = "INTERCOM"
- ConnectorProviderResend ConnectorProvider = "RESEND"
- ConnectorProviderMicrosoft365 ConnectorProvider = "MICROSOFT_365"
- ConnectorProviderGitLab ConnectorProvider = "GITLAB"
- ConnectorProviderBitbucket ConnectorProvider = "BITBUCKET"
- ConnectorProviderHeroku ConnectorProvider = "HEROKU"
- ConnectorProviderPagerDuty ConnectorProvider = "PAGERDUTY"
- ConnectorProviderAsana ConnectorProvider = "ASANA"
- ConnectorProviderNetlify ConnectorProvider = "NETLIFY"
- ConnectorProviderClickUp ConnectorProvider = "CLICKUP"
- ConnectorProviderVercel ConnectorProvider = "VERCEL"
- ConnectorProviderMonday ConnectorProvider = "MONDAY"
- ConnectorProviderMetabase ConnectorProvider = "METABASE"
- ConnectorProviderTailscale ConnectorProvider = "TAILSCALE"
- ConnectorProviderAnthropic ConnectorProvider = "ANTHROPIC"
- ConnectorProviderCursor ConnectorProvider = "CURSOR"
+ ConnectorProviderOnePassword ConnectorProvider = "ONE_PASSWORD"
+ ConnectorProviderHubSpot ConnectorProvider = "HUBSPOT"
+ ConnectorProviderDocuSign ConnectorProvider = "DOCUSIGN"
+ ConnectorProviderNotion ConnectorProvider = "NOTION"
+ ConnectorProviderBrex ConnectorProvider = "BREX"
+ ConnectorProviderTally ConnectorProvider = "TALLY"
+ ConnectorProviderCloudflare ConnectorProvider = "CLOUDFLARE"
+ ConnectorProviderGrafana ConnectorProvider = "GRAFANA"
+ ConnectorProviderOpenAI ConnectorProvider = "OPENAI"
+ ConnectorProviderPostHog ConnectorProvider = "POSTHOG"
+ ConnectorProviderSentry ConnectorProvider = "SENTRY"
+ ConnectorProviderSupabase ConnectorProvider = "SUPABASE"
+ ConnectorProviderGitHub ConnectorProvider = "GITHUB"
+ ConnectorProviderIntercom ConnectorProvider = "INTERCOM"
+ ConnectorProviderResend ConnectorProvider = "RESEND"
+ ConnectorProviderMicrosoft365 ConnectorProvider = "MICROSOFT_365"
+ ConnectorProviderGitLab ConnectorProvider = "GITLAB"
+ ConnectorProviderBitbucket ConnectorProvider = "BITBUCKET"
+ ConnectorProviderHeroku ConnectorProvider = "HEROKU"
+ ConnectorProviderPagerDuty ConnectorProvider = "PAGERDUTY"
+ ConnectorProviderAsana ConnectorProvider = "ASANA"
+ ConnectorProviderNetlify ConnectorProvider = "NETLIFY"
+ ConnectorProviderClickUp ConnectorProvider = "CLICKUP"
+ ConnectorProviderVercel ConnectorProvider = "VERCEL"
+ ConnectorProviderMonday ConnectorProvider = "MONDAY"
+ ConnectorProviderMetabase ConnectorProvider = "METABASE"
+ ConnectorProviderTailscale ConnectorProvider = "TAILSCALE"
+ ConnectorProviderAnthropic ConnectorProvider = "ANTHROPIC"
+ ConnectorProviderCursor ConnectorProvider = "CURSOR"
)
var (
@@ -84,7 +78,6 @@ func ConnectorProviders() []ConnectorProvider {
ConnectorProviderGrafana,
ConnectorProviderOpenAI,
ConnectorProviderPostHog,
- ConnectorProviderPostHogSelfHosted,
ConnectorProviderSentry,
ConnectorProviderSupabase,
ConnectorProviderGitHub,
@@ -123,7 +116,6 @@ func (v ConnectorProvider) IsValid() bool {
ConnectorProviderGrafana,
ConnectorProviderOpenAI,
ConnectorProviderPostHog,
- ConnectorProviderPostHogSelfHosted,
ConnectorProviderSentry,
ConnectorProviderSupabase,
ConnectorProviderGitHub,
diff --git a/pkg/coredata/connector_settings.go b/pkg/coredata/connector_settings.go
index e0af16c28..3036bfe4c 100644
--- a/pkg/coredata/connector_settings.go
+++ b/pkg/coredata/connector_settings.go
@@ -92,12 +92,13 @@ type (
InstanceURL string `json:"instance_url"`
}
- // PostHogConnectorSettings carries the data-API base host for both the
- // cloud (POSTHOG) and self-hosted (POSTHOG_SELF_HOSTED) providers. It
- // is the region-pinned host for API-key connections
- // (https://us.posthog.com / https://eu.posthog.com / a self-hosted
- // instance URL). It is empty for cloud OAuth connections — the driver
- // then defaults to the region-agnostic gateway https://oauth.posthog.com.
+ // PostHogConnectorSettings carries the data-API base host for the
+ // PostHog provider, which spans cloud and self-hosted deployments. For
+ // API-key connections it is the region-pinned host
+ // (https://us.posthog.com / https://eu.posthog.com) or a self-hosted
+ // instance URL. It is empty for cloud OAuth connections — the driver
+ // then discovers the region (us/eu) by probing, since the
+ // region-agnostic oauth.posthog.com gateway does not serve the data API.
PostHogConnectorSettings struct {
BaseURL string `json:"base_url"`
}
diff --git a/pkg/coredata/migrations/20260529T617403Z.sql b/pkg/coredata/migrations/20260529T617403Z.sql
deleted file mode 100644
index 3c526722e..000000000
--- a/pkg/coredata/migrations/20260529T617403Z.sql
+++ /dev/null
@@ -1,15 +0,0 @@
--- Copyright (c) 2026 Probo Inc .
---
--- Permission to use, copy, modify, and/or distribute this software for any
--- purpose with or without fee is hereby granted, provided that the above
--- copyright notice and this permission notice appear in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
--- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
--- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
--- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
--- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
--- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
--- PERFORMANCE OF THIS SOFTWARE.
-
-ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'POSTHOG_SELF_HOSTED';
diff --git a/pkg/server/api/console/v1/connector_settings.go b/pkg/server/api/console/v1/connector_settings.go
index 45c3d8e60..e32b5fa57 100644
--- a/pkg/server/api/console/v1/connector_settings.go
+++ b/pkg/server/api/console/v1/connector_settings.go
@@ -95,27 +95,38 @@ func apiKeyConnectorSettings(input types.CreateAPIKeyConnectorInput) (json.RawMe
return json.Marshal(&coredata.MetabaseConnectorSettings{InstanceURL: *input.MetabaseInstanceURL})
case coredata.ConnectorProviderPostHog:
- if input.PosthogRegion == nil || *input.PosthogRegion == "" {
- return nil, fmt.Errorf("cannot create posthog connector: posthogRegion is required")
+ region := ""
+ if input.PosthogRegion != nil {
+ region = *input.PosthogRegion
}
- baseURL, ok := drivers.PostHogRegionBaseURL(*input.PosthogRegion)
- if !ok {
- return nil, fmt.Errorf("cannot create posthog connector: posthogRegion must be US or EU")
+ instanceURL := ""
+ if input.PosthogInstanceURL != nil {
+ instanceURL = *input.PosthogInstanceURL
}
- return json.Marshal(&coredata.PostHogConnectorSettings{BaseURL: baseURL})
- case coredata.ConnectorProviderPostHogSelfHosted:
- if input.PosthogInstanceURL == nil || *input.PosthogInstanceURL == "" {
- return nil, fmt.Errorf("cannot create posthog self-hosted connector: posthogInstanceUrl is required")
- }
+ // Cloud (region) and self-hosted (instance URL) are mutually
+ // exclusive; exactly one identifies the connection's data host.
+ switch {
+ case region != "" && instanceURL != "":
+ return nil, fmt.Errorf("cannot create posthog connector: set either posthogRegion or posthogInstanceUrl, not both")
+ case region != "":
+ baseURL, ok := drivers.PostHogRegionBaseURL(region)
+ if !ok {
+ return nil, fmt.Errorf("cannot create posthog connector: posthogRegion must be US or EU")
+ }
- u, err := url.Parse(*input.PosthogInstanceURL)
- if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" {
- return nil, fmt.Errorf("cannot create posthog self-hosted connector: posthogInstanceUrl must be an http(s) URL")
- }
+ return json.Marshal(&coredata.PostHogConnectorSettings{BaseURL: baseURL})
+ case instanceURL != "":
+ u, err := url.Parse(instanceURL)
+ if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" {
+ return nil, fmt.Errorf("cannot create posthog connector: posthogInstanceUrl must be an http(s) URL")
+ }
- return json.Marshal(&coredata.PostHogConnectorSettings{BaseURL: *input.PosthogInstanceURL})
+ return json.Marshal(&coredata.PostHogConnectorSettings{BaseURL: instanceURL})
+ default:
+ return nil, fmt.Errorf("cannot create posthog connector: posthogRegion or posthogInstanceUrl is required")
+ }
}
return nil, nil
diff --git a/pkg/server/api/console/v1/graphql/connector.graphql b/pkg/server/api/console/v1/graphql/connector.graphql
index d7fcff6af..841daf5c7 100644
--- a/pkg/server/api/console/v1/graphql/connector.graphql
+++ b/pkg/server/api/console/v1/graphql/connector.graphql
@@ -23,10 +23,6 @@ enum ConnectorProvider
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderCloudflare")
OPENAI @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderOpenAI")
POSTHOG @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderPostHog")
- POSTHOG_SELF_HOSTED
- @goEnum(
- value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderPostHogSelfHosted"
- )
SENTRY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSentry")
SUPABASE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSupabase")