diff --git a/apps/mobile/src/features/inbox/components/FilterSheet.tsx b/apps/mobile/src/features/inbox/components/FilterSheet.tsx
index 83163b2547..5ad7d2cce3 100644
--- a/apps/mobile/src/features/inbox/components/FilterSheet.tsx
+++ b/apps/mobile/src/features/inbox/components/FilterSheet.tsx
@@ -77,6 +77,7 @@ const SOURCE_PRODUCT_OPTIONS: { value: SourceProduct; label: string }[] = [
{ value: "zendesk", label: "Zendesk" },
{ value: "conversations", label: "Conversations" },
{ value: "signals_scout", label: "Scout" },
+ { value: "health_checks", label: "Health checks" },
];
function SectionHeader({ title }: { title: string }) {
diff --git a/apps/mobile/src/features/inbox/components/SignalCard.tsx b/apps/mobile/src/features/inbox/components/SignalCard.tsx
index a32ff002d1..0bf1a595f8 100644
--- a/apps/mobile/src/features/inbox/components/SignalCard.tsx
+++ b/apps/mobile/src/features/inbox/components/SignalCard.tsx
@@ -8,6 +8,7 @@ import {
CheckCircle,
Code,
Compass,
+ FirstAid,
GithubLogo,
LinkSimple,
Question,
@@ -53,6 +54,8 @@ function sourceLine(signal: Signal): string {
)
return "Scout · Cross-source issue";
if (source_product === "signals_scout") return "Scout";
+ if (source_product === "health_checks" && source_type === "health_issue")
+ return "Health checks · Issue";
const product = source_product.replace(/_/g, " ");
const type = source_type.replace(/_/g, " ");
return `${product} · ${type}`;
@@ -82,6 +85,8 @@ function SourceIcon({
return ;
case "signals_scout":
return ;
+ case "health_checks":
+ return ;
default:
return ;
}
diff --git a/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts b/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts
index ab97b29525..acad97721c 100644
--- a/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts
+++ b/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts
@@ -15,14 +15,15 @@ type SortField = Extract<
type SortDirection = "asc" | "desc";
export type SourceProduct =
- | "session_replay"
+ | "conversations"
| "error_tracking"
- | "llm_analytics"
| "github"
+ | "health_checks"
| "linear"
- | "zendesk"
- | "conversations"
- | "signals_scout";
+ | "llm_analytics"
+ | "session_replay"
+ | "signals_scout"
+ | "zendesk";
export const DEFAULT_STATUS_FILTER: SignalReportStatus[] = [
"ready",
diff --git a/packages/api-client/src/posthog-client.ts b/packages/api-client/src/posthog-client.ts
index 8a90ff2861..37a283636d 100644
--- a/packages/api-client/src/posthog-client.ts
+++ b/packages/api-client/src/posthog-client.ts
@@ -7,6 +7,8 @@ import type {
CloudRunSource,
ExecutionMode,
PrAuthorshipMode,
+ SourceProduct,
+ SourceType,
StoredLogEntry,
TaskRunArtifactMetadata,
} from "@posthog/shared";
@@ -244,59 +246,8 @@ export interface LlmSkillFileInput {
export interface SignalSourceConfig {
id: string;
- source_product:
- | "session_replay"
- | "llm_analytics"
- | "github"
- | "linear"
- | "jira"
- | "zendesk"
- | "conversations"
- | "error_tracking"
- | "pganalyze"
- | "signals_scout"
- | "freshdesk"
- | "freshservice"
- | "front"
- | "gorgias"
- | "kustomer"
- | "dixa"
- | "plain"
- | "gitlab"
- | "gitea"
- | "shortcut"
- | "sentry"
- | "rollbar"
- | "bugsnag"
- | "honeybadger"
- | "raygun"
- | "snyk"
- | "sonarqube"
- | "semgrep"
- | "rapid7_insightvm"
- | "featurebase"
- | "frill"
- | "aha"
- | "uservoice"
- | "productboard"
- | "canny"
- | "asknicely"
- | "retently"
- | "appfigures"
- | "appfollow"
- | "judgeme_reviews";
- source_type:
- | "session_analysis_cluster"
- | "evaluation"
- | "issue"
- | "ticket"
- | "issue_created"
- | "issue_reopened"
- | "issue_spiking"
- | "cross_source_issue"
- | "scanner_finding"
- | "feedback"
- | "review";
+ source_product: SourceProduct;
+ source_type: SourceType;
enabled: boolean;
config: Record;
created_at: string;
diff --git a/packages/core/src/inbox/signalSourceService.test.ts b/packages/core/src/inbox/signalSourceService.test.ts
index c5e4e7bb58..1d7c0c9d87 100644
--- a/packages/core/src/inbox/signalSourceService.test.ts
+++ b/packages/core/src/inbox/signalSourceService.test.ts
@@ -60,6 +60,13 @@ describe("computeSourceValues", () => {
const values = computeSourceValues([config("github", "issue", true)]);
expect(values.github).toBe(true);
});
+
+ it("enables health_checks when its config is enabled", () => {
+ const values = computeSourceValues([
+ config("health_checks", "health_issue", true),
+ ]);
+ expect(values.health_checks).toBe(true);
+ });
});
describe("deriveSourceStates", () => {
@@ -106,6 +113,17 @@ describe("SignalSourceService.toggleSource", () => {
expect(client.createSignalSourceConfig).toHaveBeenCalledTimes(1);
});
+ it("creates a health_checks config with the health_issue source type", async () => {
+ const client = fakeClient();
+ const service = new SignalSourceService();
+ await service.toggleSource(client, 1, "health_checks", true, [], []);
+ expect(client.createSignalSourceConfig).toHaveBeenCalledWith(1, {
+ source_product: "health_checks",
+ source_type: "health_issue",
+ enabled: true,
+ });
+ });
+
it("ensures the issues table syncs with full_refresh for github before enabling", async () => {
const client = fakeClient();
const service = new SignalSourceService();
diff --git a/packages/core/src/inbox/signalSourceService.ts b/packages/core/src/inbox/signalSourceService.ts
index b13abdead3..92fb48ce63 100644
--- a/packages/core/src/inbox/signalSourceService.ts
+++ b/packages/core/src/inbox/signalSourceService.ts
@@ -34,8 +34,9 @@ type SourceType = SignalSourceConfig["source_type"];
// Non-warehouse toggles are hard-wired; warehouse sources are derived from the shared
// EXTERNAL_INBOX_SOURCES registry (kept in sync with the UI hook).
const SOURCE_TYPE_MAP: Partial> = {
- session_replay: "session_analysis_cluster",
conversations: "ticket",
+ health_checks: "health_issue",
+ session_replay: "session_analysis_cluster",
...Object.fromEntries(
EXTERNAL_INBOX_SOURCES.map((s) => [s.product, s.recordKind]),
),
@@ -62,10 +63,11 @@ const DATA_WAREHOUSE_SOURCES: Record<
);
const ALL_SOURCE_PRODUCTS: SignalSourceProduct[] = [
- "session_replay",
- "error_tracking",
"conversations",
- ...EXTERNAL_INBOX_SOURCES.map((s) => s.product as SignalSourceProduct),
+ "error_tracking",
+ "health_checks",
+ "session_replay",
+ ...EXTERNAL_INBOX_SOURCES.map((s) => s.product),
];
function isWarehouseSource(product: SignalSourceProduct): boolean {
diff --git a/packages/shared/src/analytics-events.ts b/packages/shared/src/analytics-events.ts
index 20ddd33cae..ff8a96421e 100644
--- a/packages/shared/src/analytics-events.ts
+++ b/packages/shared/src/analytics-events.ts
@@ -1,6 +1,7 @@
// Analytics event types and properties
import type { Adapter } from "./adapter";
+import type { SourceProduct } from "./inbox-types";
export interface PromptHistoryOpenedProperties {
entry_count: number;
@@ -793,47 +794,7 @@ export interface ScoutActionProperties {
}
export interface SignalSourceConnectedProperties {
- source_product:
- | "session_replay"
- | "error_tracking"
- | "signals_scout"
- | "github"
- | "linear"
- | "jira"
- | "zendesk"
- | "conversations"
- | "pganalyze"
- | "llm_analytics"
- | "freshdesk"
- | "freshservice"
- | "front"
- | "gorgias"
- | "kustomer"
- | "dixa"
- | "plain"
- | "gitlab"
- | "gitea"
- | "shortcut"
- | "sentry"
- | "rollbar"
- | "bugsnag"
- | "honeybadger"
- | "raygun"
- | "snyk"
- | "sonarqube"
- | "semgrep"
- | "rapid7_insightvm"
- | "featurebase"
- | "frill"
- | "aha"
- | "uservoice"
- | "productboard"
- | "canny"
- | "asknicely"
- | "retently"
- | "appfigures"
- | "appfollow"
- | "judgeme_reviews";
+ source_product: SourceProduct;
/** True when this is a brand-new createSignalSourceConfig, false for re-enable of an existing config. */
is_first_connection: boolean;
/** True when the connection went through the DataSourceSetup wizard (warehouse OAuth path). */
diff --git a/packages/shared/src/inbox-types.ts b/packages/shared/src/inbox-types.ts
index c590219b0d..7a6aae7ef9 100644
--- a/packages/shared/src/inbox-types.ts
+++ b/packages/shared/src/inbox-types.ts
@@ -5,94 +5,6 @@ export interface AvailableSuggestedReviewer {
github_login: string;
}
-export type SourceProduct =
- | "session_replay"
- | "error_tracking"
- | "llm_analytics"
- | "github"
- | "linear"
- | "jira"
- | "zendesk"
- | "conversations"
- | "pganalyze"
- | "signals_scout"
- // Warehouse-backed self-driving inbox sources (see EXTERNAL_INBOX_SOURCES).
- | "freshdesk"
- | "freshservice"
- | "front"
- | "gorgias"
- | "kustomer"
- | "dixa"
- | "plain"
- | "gitlab"
- | "gitea"
- | "shortcut"
- | "sentry"
- | "rollbar"
- | "bugsnag"
- | "honeybadger"
- | "raygun"
- | "snyk"
- | "sonarqube"
- | "semgrep"
- | "rapid7_insightvm"
- | "featurebase"
- | "frill"
- | "aha"
- | "uservoice"
- | "productboard"
- | "canny"
- | "asknicely"
- | "retently"
- | "appfigures"
- | "appfollow"
- | "judgeme_reviews";
-
-/**
- * Products that render as a toggle in the Self-driving sources modal: the three PostHog-data
- * inputs plus every warehouse source in EXTERNAL_INBOX_SOURCES. Excludes non-toggle products
- * (`llm_analytics`, `signals_scout`) that appear only as signal origins.
- */
-export type ToggleableSourceProduct =
- | "session_replay"
- | "error_tracking"
- | "conversations"
- | "github"
- | "linear"
- | "jira"
- | "gitlab"
- | "gitea"
- | "shortcut"
- | "sentry"
- | "rollbar"
- | "bugsnag"
- | "honeybadger"
- | "raygun"
- | "zendesk"
- | "freshdesk"
- | "freshservice"
- | "front"
- | "gorgias"
- | "kustomer"
- | "dixa"
- | "plain"
- | "pganalyze"
- | "snyk"
- | "sonarqube"
- | "semgrep"
- | "rapid7_insightvm"
- | "featurebase"
- | "frill"
- | "aha"
- | "uservoice"
- | "productboard"
- | "canny"
- | "asknicely"
- | "retently"
- | "appfigures"
- | "appfollow"
- | "judgeme_reviews";
-
/** Signal record kind (backend `source_type`) for a warehouse-backed inbox source. */
export type SignalRecordKind =
| "issue"
@@ -103,14 +15,18 @@ export type SignalRecordKind =
/**
* A warehouse data source the Self-driving inbox can watch. This is the single source of
- * truth the toggle grid, setup switch, source-type map, and DWH connection map derive from —
- * adding a warehouse source is one entry here (plus its backend emitter and, if it uses OAuth,
- * a bespoke setup form). `setup: "dynamic"` renders the generic credential form; the three
- * legacy special-cased flows keep their own key.
+ * truth the toggle grid, setup switch, source-type map, DWH connection map, and the
+ * `SourceProduct` union derive from — adding a warehouse source is one entry here (plus its
+ * backend emitter and, if it uses OAuth, a bespoke setup form). `setup: "dynamic"` renders
+ * the generic credential form; the three legacy special-cased flows keep their own key.
*/
export interface ExternalInboxSource {
- /** Backend `source_product` (lowercase). */
- product: SourceProduct;
+ /**
+ * Backend `source_product` (lowercase). Declared as `string` here so the registry can
+ * define the universe: `ExternalInboxSourceProduct` is derived from the literal values
+ * in EXTERNAL_INBOX_SOURCES and feeds the `SourceProduct` union.
+ */
+ product: string;
/** Display label for the toggle card / filter. */
label: string;
/** One-line card description. */
@@ -118,7 +34,7 @@ export interface ExternalInboxSource {
/** Capitalized DWH `source_type` used to match/create the external data source. */
dwSourceType: string;
/** Warehouse table(s) that must be syncing for signals to flow. */
- requiredTables: string[];
+ requiredTables: readonly string[];
/** Backend signal `source_type` this source emits. */
recordKind: SignalRecordKind;
/** Setup flow: the generic dynamic credential form, or a legacy special case. */
@@ -133,8 +49,18 @@ const FINDING = "Surface new security and code-quality findings";
const FEEDBACK = "Turn product feedback and feature requests into inputs";
const REVIEW = "Monitor new app and product reviews";
-export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
+/** Registry of warehouse-backed inbox sources, alphabetical within each category. */
+export const EXTERNAL_INBOX_SOURCES = [
// Issue trackers
+ {
+ product: "gitea",
+ label: "Gitea",
+ description: ISSUE,
+ dwSourceType: "Gitea",
+ requiredTables: ["issues"],
+ recordKind: "issue",
+ setup: "dynamic",
+ },
{
product: "github",
label: "GitHub Issues",
@@ -145,10 +71,10 @@ export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
setup: "github",
},
{
- product: "linear",
- label: "Linear",
+ product: "gitlab",
+ label: "GitLab",
description: ISSUE,
- dwSourceType: "Linear",
+ dwSourceType: "GitLab",
requiredTables: ["issues"],
recordKind: "issue",
setup: "dynamic",
@@ -163,19 +89,10 @@ export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
setup: "dynamic",
},
{
- product: "gitlab",
- label: "GitLab",
- description: ISSUE,
- dwSourceType: "GitLab",
- requiredTables: ["issues"],
- recordKind: "issue",
- setup: "dynamic",
- },
- {
- product: "gitea",
- label: "Gitea",
+ product: "linear",
+ label: "Linear",
description: ISSUE,
- dwSourceType: "Gitea",
+ dwSourceType: "Linear",
requiredTables: ["issues"],
recordKind: "issue",
setup: "dynamic",
@@ -190,24 +107,6 @@ export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
setup: "dynamic",
},
// Error tracking
- {
- product: "sentry",
- label: "Sentry",
- description: ERROR,
- dwSourceType: "Sentry",
- requiredTables: ["issues"],
- recordKind: "issue",
- setup: "dynamic",
- },
- {
- product: "rollbar",
- label: "Rollbar",
- description: ERROR,
- dwSourceType: "Rollbar",
- requiredTables: ["items"],
- recordKind: "issue",
- setup: "dynamic",
- },
{
product: "bugsnag",
label: "Bugsnag",
@@ -235,15 +134,33 @@ export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
recordKind: "issue",
setup: "dynamic",
},
+ {
+ product: "rollbar",
+ label: "Rollbar",
+ description: ERROR,
+ dwSourceType: "Rollbar",
+ requiredTables: ["items"],
+ recordKind: "issue",
+ setup: "dynamic",
+ },
+ {
+ product: "sentry",
+ label: "Sentry",
+ description: ERROR,
+ dwSourceType: "Sentry",
+ requiredTables: ["issues"],
+ recordKind: "issue",
+ setup: "dynamic",
+ },
// Support / helpdesk
{
- product: "zendesk",
- label: "Zendesk",
- description: TICKET,
- dwSourceType: "Zendesk",
- requiredTables: ["tickets"],
+ product: "dixa",
+ label: "Dixa",
+ description: CONVERSATION,
+ dwSourceType: "Dixa",
+ requiredTables: ["conversations"],
recordKind: "ticket",
- setup: "zendesk",
+ setup: "dynamic",
},
{
product: "freshdesk",
@@ -290,15 +207,6 @@ export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
recordKind: "ticket",
setup: "dynamic",
},
- {
- product: "dixa",
- label: "Dixa",
- description: CONVERSATION,
- dwSourceType: "Dixa",
- requiredTables: ["conversations"],
- recordKind: "ticket",
- setup: "dynamic",
- },
{
product: "plain",
label: "Plain",
@@ -308,6 +216,15 @@ export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
recordKind: "ticket",
setup: "dynamic",
},
+ {
+ product: "zendesk",
+ label: "Zendesk",
+ description: TICKET,
+ dwSourceType: "Zendesk",
+ requiredTables: ["tickets"],
+ recordKind: "ticket",
+ setup: "zendesk",
+ },
// Database / infra performance
{
product: "pganalyze",
@@ -321,20 +238,11 @@ export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
},
// Security scanners
{
- product: "snyk",
- label: "Snyk",
- description: FINDING,
- dwSourceType: "Snyk",
- requiredTables: ["issues"],
- recordKind: "scanner_finding",
- setup: "dynamic",
- },
- {
- product: "sonarqube",
- label: "SonarQube",
+ product: "rapid7_insightvm",
+ label: "Rapid7 InsightVM",
description: FINDING,
- dwSourceType: "Sonarqube",
- requiredTables: ["issues"],
+ dwSourceType: "Rapid7Insightvm",
+ requiredTables: ["vulnerabilities"],
recordKind: "scanner_finding",
setup: "dynamic",
},
@@ -348,75 +256,75 @@ export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
setup: "dynamic",
},
{
- product: "rapid7_insightvm",
- label: "Rapid7 InsightVM",
+ product: "snyk",
+ label: "Snyk",
description: FINDING,
- dwSourceType: "Rapid7Insightvm",
- requiredTables: ["vulnerabilities"],
+ dwSourceType: "Snyk",
+ requiredTables: ["issues"],
recordKind: "scanner_finding",
setup: "dynamic",
},
- // Product feedback / feature requests
{
- product: "featurebase",
- label: "Featurebase",
- description: FEEDBACK,
- dwSourceType: "Featurebase",
- requiredTables: ["posts"],
- recordKind: "feedback",
+ product: "sonarqube",
+ label: "SonarQube",
+ description: FINDING,
+ dwSourceType: "Sonarqube",
+ requiredTables: ["issues"],
+ recordKind: "scanner_finding",
setup: "dynamic",
},
+ // Product feedback / feature requests
{
- product: "frill",
- label: "Frill",
+ product: "aha",
+ label: "Aha",
description: FEEDBACK,
- dwSourceType: "Frill",
+ dwSourceType: "Aha",
requiredTables: ["ideas"],
recordKind: "feedback",
setup: "dynamic",
},
{
- product: "aha",
- label: "Aha",
+ product: "asknicely",
+ label: "AskNicely",
description: FEEDBACK,
- dwSourceType: "Aha",
- requiredTables: ["ideas"],
+ dwSourceType: "Asknicely",
+ requiredTables: ["responses"],
recordKind: "feedback",
setup: "dynamic",
},
{
- product: "uservoice",
- label: "UserVoice",
+ product: "canny",
+ label: "Canny",
description: FEEDBACK,
- dwSourceType: "Uservoice",
- requiredTables: ["suggestions"],
+ dwSourceType: "Canny",
+ requiredTables: ["posts"],
recordKind: "feedback",
setup: "dynamic",
},
{
- product: "productboard",
- label: "Productboard",
+ product: "featurebase",
+ label: "Featurebase",
description: FEEDBACK,
- dwSourceType: "Productboard",
- requiredTables: ["notes"],
+ dwSourceType: "Featurebase",
+ requiredTables: ["posts"],
recordKind: "feedback",
setup: "dynamic",
},
{
- product: "canny",
- label: "Canny",
+ product: "frill",
+ label: "Frill",
description: FEEDBACK,
- dwSourceType: "Canny",
- requiredTables: ["posts"],
+ dwSourceType: "Frill",
+ requiredTables: ["ideas"],
recordKind: "feedback",
setup: "dynamic",
},
{
- product: "asknicely",
- label: "AskNicely",
+ product: "productboard",
+ label: "Productboard",
description: FEEDBACK,
- dwSourceType: "Asknicely",
- requiredTables: ["responses"],
+ dwSourceType: "Productboard",
+ requiredTables: ["notes"],
recordKind: "feedback",
setup: "dynamic",
},
@@ -429,6 +337,15 @@ export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
recordKind: "feedback",
setup: "dynamic",
},
+ {
+ product: "uservoice",
+ label: "UserVoice",
+ description: FEEDBACK,
+ dwSourceType: "Uservoice",
+ requiredTables: ["suggestions"],
+ recordKind: "feedback",
+ setup: "dynamic",
+ },
// Reviews
{
product: "appfigures",
@@ -457,7 +374,47 @@ export const EXTERNAL_INBOX_SOURCES: ExternalInboxSource[] = [
recordKind: "review",
setup: "dynamic",
},
-];
+] as const satisfies readonly ExternalInboxSource[];
+
+/** Warehouse-backed source products, derived from the registry above. */
+export type ExternalInboxSourceProduct =
+ (typeof EXTERNAL_INBOX_SOURCES)[number]["product"];
+
+/**
+ * Every backend `source_product`: the PostHog-native products (alphabetical) plus every
+ * warehouse source in EXTERNAL_INBOX_SOURCES.
+ */
+export type SourceProduct =
+ | "conversations"
+ | "error_tracking"
+ | "health_checks"
+ | "llm_analytics"
+ | "session_replay"
+ | "signals_scout"
+ | ExternalInboxSourceProduct;
+
+/**
+ * Products that render as a toggle in the Self-driving sources modal. Excludes non-toggle
+ * products (`llm_analytics`, `signals_scout`) that appear only as signal origins.
+ */
+export type ToggleableSourceProduct = Exclude<
+ SourceProduct,
+ "llm_analytics" | "signals_scout"
+>;
+
+/**
+ * Every backend signal `source_type`: the PostHog-native types (alphabetical) plus the
+ * warehouse record kinds.
+ */
+export type SourceType =
+ | "cross_source_issue"
+ | "evaluation"
+ | "health_issue"
+ | "issue_created"
+ | "issue_reopened"
+ | "issue_spiking"
+ | "session_analysis_cluster"
+ | SignalRecordKind;
/** Issue-like records mutate (status/votes change), so their table needs full-refresh sync. */
export function sourceNeedsFullRefresh(recordKind: SignalRecordKind): boolean {
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index e76f88cd53..a67e774369 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -148,8 +148,10 @@ export { buildDiscussReportPrompt } from "./inbox-prompts";
export type {
AvailableSuggestedReviewer,
ExternalInboxSource,
+ ExternalInboxSourceProduct,
SignalRecordKind,
SourceProduct,
+ SourceType,
ToggleableSourceProduct,
} from "./inbox-types";
export {
diff --git a/packages/ui/src/features/inbox/components/DataSourceSetup.tsx b/packages/ui/src/features/inbox/components/DataSourceSetup.tsx
index a9eb7a88b9..0107db0b38 100644
--- a/packages/ui/src/features/inbox/components/DataSourceSetup.tsx
+++ b/packages/ui/src/features/inbox/components/DataSourceSetup.tsx
@@ -22,7 +22,7 @@ import { useCallback, useEffect, useState } from "react";
/** PostHog DWH: full table replication (non-incremental); API enum value `full_refresh`. */
const FULL_TABLE_REPLICATION = "full_refresh" as const;
-function schemasPayload(tables: string[]) {
+function schemasPayload(tables: readonly string[]) {
return tables.map((name) => ({
name,
should_sync: true,
diff --git a/packages/ui/src/features/inbox/components/SignalSourceToggles.tsx b/packages/ui/src/features/inbox/components/SignalSourceToggles.tsx
index 2ed3e65672..a7d9816c3a 100644
--- a/packages/ui/src/features/inbox/components/SignalSourceToggles.tsx
+++ b/packages/ui/src/features/inbox/components/SignalSourceToggles.tsx
@@ -4,6 +4,7 @@ import {
BugIcon,
ChatsIcon,
CircleNotchIcon,
+ FirstAidIcon,
PlugIcon,
VideoIcon,
} from "@phosphor-icons/react";
@@ -325,6 +326,10 @@ export function SignalSourceToggles({
(checked: boolean) => onToggle("conversations", checked),
[onToggle],
);
+ const toggleHealthChecks = useCallback(
+ (checked: boolean) => onToggle("health_checks", checked),
+ [onToggle],
+ );
return (
@@ -345,6 +350,17 @@ export function SignalSourceToggles({
docsUrl="https://posthog.com/docs/error-tracking"
docsLabel="Error Tracking"
/>
+ }
+ label="Health checks"
+ description="Surface instrumentation problems — missing events, proxy gaps, outdated SDKs"
+ checked={value.health_checks}
+ onCheckedChange={toggleHealthChecks}
+ disabled={disabled}
+ syncStatus={sourceStates?.health_checks?.syncStatus}
+ docsUrl="https://posthog.com/docs/sdk-health"
+ docsLabel="Health checks"
+ />
}
label="Support"
@@ -387,7 +403,7 @@ export function SignalSourceToggles({
{EXTERNAL_INBOX_SOURCES.map((source) => {
- const product = source.product as ToggleableSourceProduct;
+ const product = source.product;
return (
,
},
{ value: "signals_scout", label: "Scouts", icon: },
+ {
+ value: "health_checks",
+ label: "Health checks",
+ icon: ,
+ },
// Warehouse-backed sources, derived from the shared registry.
...EXTERNAL_INBOX_SOURCES.map((source) => {
const meta = getSourceProductMeta(source.product);
diff --git a/packages/ui/src/features/inbox/hooks/useSignalSourceToggles.ts b/packages/ui/src/features/inbox/hooks/useSignalSourceToggles.ts
index ef23cac047..f47c0d2177 100644
--- a/packages/ui/src/features/inbox/hooks/useSignalSourceToggles.ts
+++ b/packages/ui/src/features/inbox/hooks/useSignalSourceToggles.ts
@@ -21,8 +21,9 @@ type SourceKey = keyof SignalSourceValues;
// Non-warehouse toggles are hard-wired; every warehouse source is derived from the shared
// EXTERNAL_INBOX_SOURCES registry so adding a source is a one-line change there.
const SOURCE_TYPE_MAP: Partial> = {
- session_replay: "session_analysis_cluster",
conversations: "ticket",
+ health_checks: "health_issue",
+ session_replay: "session_analysis_cluster",
...Object.fromEntries(
EXTERNAL_INBOX_SOURCES.map((s) => [s.product, s.recordKind]),
),
@@ -35,9 +36,10 @@ const ERROR_TRACKING_SOURCE_TYPES: SourceType[] = [
];
const SOURCE_LABELS: Partial> = {
- session_replay: "Session replay",
- error_tracking: "Error tracking",
conversations: "PostHog Support",
+ error_tracking: "Error tracking",
+ health_checks: "Health checks",
+ session_replay: "Session replay",
...Object.fromEntries(
EXTERNAL_INBOX_SOURCES.map((s) => [s.product, s.label]),
),
@@ -58,10 +60,11 @@ const DATA_WAREHOUSE_SOURCES: Record<
);
const ALL_SOURCE_PRODUCTS: SourceKey[] = [
- "session_replay",
- "error_tracking",
"conversations",
- ...EXTERNAL_INBOX_SOURCES.map((s) => s.product as SourceKey),
+ "error_tracking",
+ "health_checks",
+ "session_replay",
+ ...EXTERNAL_INBOX_SOURCES.map((s) => s.product),
];
function isSetupSourceProduct(product: SourceKey): boolean {