diff --git a/packages/frontend/editor-ui/src/features/credentials/components/NodeCredentials.test.ts b/packages/frontend/editor-ui/src/features/credentials/components/NodeCredentials.test.ts
index 538c10386f3..b4c751f7598 100644
--- a/packages/frontend/editor-ui/src/features/credentials/components/NodeCredentials.test.ts
+++ b/packages/frontend/editor-ui/src/features/credentials/components/NodeCredentials.test.ts
@@ -26,6 +26,12 @@ import {
createWorkflowDocumentId,
} from '@/app/stores/workflowDocument.store';
+const trackMock = vi.hoisted(() => vi.fn());
+
+vi.mock('@/app/composables/useTelemetry', () => ({
+ useTelemetry: () => ({ track: trackMock }),
+}));
+
vi.mock('@/app/composables/useAiGateway', () => ({
useAiGateway: vi.fn(() => ({
isEnabled: ref(false),
@@ -1289,6 +1295,76 @@ describe('NodeCredentials', () => {
expect((payload.properties.credentials['googlePalmApi'] as { id: string }).id).toBe('cred-1');
});
+ describe('telemetry', () => {
+ const toggleOnStub = {
+ template: '',
+ props: ['aiGatewayEnabled'],
+ emits: ['toggle'],
+ };
+
+ const toggleOffStub = {
+ template:
+ '',
+ props: ['aiGatewayEnabled'],
+ emits: ['toggle'],
+ };
+
+ it('should track telemetry with mode "n8n_connect" when toggled ON by user', async () => {
+ ndvStore.activeNode = googleAiNode;
+
+ renderComponent({
+ props: { node: googleAiNode, overrideCredType: 'googlePalmApi' },
+ global: { stubs: { AiGatewaySelector: toggleOnStub } },
+ });
+
+ await userEvent.click(screen.getByTestId('ai-gateway-toggle-on'));
+
+ expect(trackMock).toHaveBeenCalledWith('User toggled n8n connect credential', {
+ credential_type: 'googlePalmApi',
+ node_type: googleAiNode.type,
+ mode: 'n8n_connect',
+ workflow_id: expect.any(String),
+ });
+ });
+
+ it('should track telemetry with mode "own" when toggled OFF by user', async () => {
+ const nodeWithGateway: INodeUi = {
+ ...googleAiNode,
+ credentials: { googlePalmApi: { id: null, name: '', __aiGatewayManaged: true } },
+ };
+ ndvStore.activeNode = nodeWithGateway;
+
+ renderComponent({
+ props: { node: nodeWithGateway, overrideCredType: 'googlePalmApi' },
+ global: { stubs: { AiGatewaySelector: toggleOffStub } },
+ });
+
+ await userEvent.click(screen.getByTestId('ai-gateway-toggle-off'));
+
+ expect(trackMock).toHaveBeenCalledWith('User toggled n8n connect credential', {
+ credential_type: 'googlePalmApi',
+ node_type: googleAiNode.type,
+ mode: 'own',
+ workflow_id: expect.any(String),
+ });
+ });
+
+ it('should not track telemetry when toggled ON automatically on mount', () => {
+ // No credentials — auto-select path calls onAiGatewaySelector with isUserAction=false
+ ndvStore.activeNode = googleAiNode;
+
+ renderComponent({
+ props: { node: googleAiNode, overrideCredType: 'googlePalmApi' },
+ global: { stubs: { AiGatewaySelector: toggleOnStub } },
+ });
+
+ expect(trackMock).not.toHaveBeenCalledWith(
+ 'User toggled n8n connect credential',
+ expect.anything(),
+ );
+ });
+ });
+
it('should emit credentialSelected removing credentials when toggled OFF with no available credentials', async () => {
credentialsStore.state.credentials = {};
diff --git a/packages/frontend/editor-ui/src/features/credentials/components/NodeCredentials.vue b/packages/frontend/editor-ui/src/features/credentials/components/NodeCredentials.vue
index 1cfd443b18d..03627fb8b47 100644
--- a/packages/frontend/editor-ui/src/features/credentials/components/NodeCredentials.vue
+++ b/packages/frontend/editor-ui/src/features/credentials/components/NodeCredentials.vue
@@ -224,7 +224,7 @@ watch(
if (aiGateway.isEnabled.value) {
for (const { type } of types) {
if (aiGateway.isCredentialTypeSupported(type.name)) {
- onAiGatewaySelector(type.name, true);
+ onAiGatewaySelector(type.name, true, false);
}
}
}
@@ -542,7 +542,7 @@ function showAiGatewaySelector(credentialType: string): boolean {
return true;
}
-function onAiGatewaySelector(credentialType: string, enable: boolean): void {
+function onAiGatewaySelector(credentialType: string, enable: boolean, isUserAction = true): void {
const credentials = { ...(props.node.credentials ?? {}) };
if (enable) {
@@ -564,6 +564,15 @@ function onAiGatewaySelector(credentialType: string, enable: boolean): void {
}
}
+ if (isUserAction) {
+ telemetry.track('User toggled n8n connect credential', {
+ credential_type: credentialType,
+ node_type: props.node.type,
+ mode: enable ? 'n8n_connect' : 'own',
+ workflow_id: props.standalone ? '' : workflowsStore.workflowId,
+ });
+ }
+
emit('credentialSelected', {
name: props.node.name,
properties: { credentials },