diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx
index fd03be2d91a..e9e90fdd3e9 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx
@@ -2478,11 +2478,10 @@ function ServiceAccountConnectDisplay({
() => (data.provider ? resolveServiceAccountIntegration(data.provider) : null),
[data.provider]
)
- const service = useMemo(() => (match ? resolveOAuthServiceForSlug(match.slug) : null), [match])
const target = useServiceAccountConnectTarget({
serviceAccountProviderId: match?.serviceAccountProviderId,
serviceName: match?.serviceName,
- serviceIcon: service?.serviceIcon,
+ serviceIcon: match?.serviceIcon,
})
// A credentialId reconnects (rotates the secret on) that existing service
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx
index e4508e8d8a9..c8a1e0ca1e2 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx
@@ -5,13 +5,14 @@ import { INTEGRATION_METADATA } from '@sim/deployment-config/integration-metadat
import { ArrowRight, cn, OverflowText } from '@sim/emcn'
import { Table } from '@sim/emcn/icons'
import { stripVersionSuffix } from '@sim/utils/string'
-import { useParams } from 'next/navigation'
+import { useParams, useRouter } from 'next/navigation'
import { usePostHog } from 'posthog-js/react'
import { HomeSection } from '@/components/home/home-section'
import { GmailIcon, SlackIcon } from '@/components/icons'
import {
resolveOAuthServiceForIntegration,
resolveOAuthServiceForSlug,
+ resolveServiceAccountServiceForIntegration,
} from '@/lib/integrations/oauth-service'
import { captureEvent } from '@/lib/posthog/client'
import { ConnectOAuthModal } from '@/app/workspace/[workspaceId]/components/connect-oauth-modal'
@@ -21,6 +22,10 @@ import type {
OAuthConnectTarget,
} from '@/app/workspace/[workspaceId]/home/components/suggested-actions/types'
import { weightedSample } from '@/app/workspace/[workspaceId]/home/components/suggested-actions/weighted-sample'
+import {
+ CONNECT_MODE,
+ CONNECT_QUERY_PARAM,
+} from '@/app/workspace/[workspaceId]/integrations/connect-route'
import { BrandIcon } from '@/blocks/brand-icon'
import { getAllBlockMeta } from '@/blocks/registry'
import type { ModuleTag } from '@/blocks/types'
@@ -240,6 +245,7 @@ interface SuggestedActionsProps {
export function SuggestedActions({ onSelectPrompt, organizationId }: SuggestedActionsProps) {
const params = useParams<{ workspaceId?: string }>()
const workspaceId = organizationId ? undefined : params.workspaceId
+ const router = useRouter()
const posthog = usePostHog()
const { data: credentials = EMPTY_CREDENTIALS } = useWorkspaceCredentials({
@@ -312,7 +318,23 @@ export function SuggestedActions({ onSelectPrompt, organizationId }: SuggestedAc
return
}
const target = resolveOAuthServiceForSlug(action.slug)
- if (target) setOAuthTarget(target)
+ if (target) {
+ setOAuthTarget(target)
+ return
+ }
+ /**
+ * The row names an integration this surface cannot connect inline: one
+ * authenticated by a stored service account, or one whose OAuth service the
+ * catalog does not carry. Both used to drop the click silently. Hand off to
+ * the detail page instead — with the service-account deep link when that is
+ * the flow it offers, so the modal still opens in one click.
+ */
+ const integration = INTEGRATION_METADATA.find((entry) => entry.slug === action.slug)
+ const connectSuffix =
+ integration && resolveServiceAccountServiceForIntegration(integration)
+ ? `?${CONNECT_QUERY_PARAM}=${CONNECT_MODE.serviceAccount}`
+ : ''
+ router.push(`/workspace/${workspaceId}/integrations/${action.slug}${connectSuffix}`)
}
const handleToggleExpanded = () => {
diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.test.tsx b/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.test.tsx
new file mode 100644
index 00000000000..4cad4049067
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.test.tsx
@@ -0,0 +1,186 @@
+/**
+ * @vitest-environment jsdom
+ */
+
+import { act } from 'react'
+import { deploymentShapeMock } from '@sim/testing/mocks/deployment-shape.mock'
+import { nextNavigationMock } from '@sim/testing/mocks/next-navigation.mock'
+import { createRoot, type Root } from 'react-dom/client'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+import type { Integration } from '@/lib/integrations/types'
+
+const { availabilityState } = vi.hoisted(() => ({
+ /** `null` stands for an availability answer that has not arrived. */
+ availabilityState: {
+ availability: null as { state: string; oauthAvailable: boolean } | null,
+ isLoading: false,
+ },
+}))
+
+vi.mock('next/navigation', () => nextNavigationMock)
+vi.mock('nuqs', () => ({ useQueryState: () => [null, vi.fn()] }))
+vi.mock('@/hooks/use-oauth-return', () => ({ useOAuthReturnRouter: () => {} }))
+vi.mock('@/hooks/queries/credentials', () => ({
+ useWorkspaceCredentials: () => ({ data: [], isPending: false }),
+}))
+vi.mock('@/app/workspace/[workspaceId]/integrations/hooks/use-scroll-restoration', () => ({
+ useScrollRestoration: () => {},
+}))
+vi.mock('@/lib/core/config/deployment-shape', () => deploymentShapeMock)
+vi.mock('@/hooks/use-permission-config', () => ({
+ usePermissionConfig: () => ({
+ integrationAvailability: new Map(
+ availabilityState.availability
+ ? [
+ ['snowflake', availabilityState.availability],
+ ['jira', availabilityState.availability],
+ ]
+ : []
+ ),
+ isLoading: availabilityState.isLoading,
+ }),
+}))
+
+/** Heavy leaf sections carry their own coverage; the header is what is under test. */
+vi.mock('@/app/workspace/[workspaceId]/integrations/[block]/integration-skills-section', () => ({
+ IntegrationSkillsSection: () => null,
+}))
+vi.mock('@/app/workspace/[workspaceId]/integrations/components/integration-section', () => ({
+ IntegrationSection: () => null,
+}))
+vi.mock('@/app/workspace/[workspaceId]/integrations/components/integrations-showcase', () => ({
+ IntegrationTile: () => null,
+}))
+vi.mock(
+ '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section',
+ () => ({
+ SettingsSection: () => null,
+ })
+)
+vi.mock('@/app/workspace/[workspaceId]/components/connect-oauth-modal', () => ({
+ ConnectOAuthModal: () =>
,
+}))
+vi.mock(
+ '@/app/workspace/[workspaceId]/integrations/components/connect-personal-token-modal',
+ () => ({
+ ConnectPersonalTokenModal: () => null,
+ })
+)
+
+import { getServiceAccountConnectNoun } from '@/lib/credentials/service-account-provider-ids'
+import { INTEGRATIONS } from '@/lib/integrations'
+import { IntegrationBlockDetail } from '@/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail'
+
+/** Snowflake authenticates only with a stored service account; Jira also offers OAuth. */
+const SERVICE_ACCOUNT_ONLY = INTEGRATIONS.find((i) => i.slug === 'snowflake') as Integration
+const OAUTH_WITH_SERVICE_ACCOUNT = INTEGRATIONS.find((i) => i.slug === 'jira') as Integration
+
+/**
+ * Derived rather than written out: the vendor-accurate noun is owned by
+ * `getServiceAccountConnectNoun`, so hardcoding it here would make this test
+ * fail on a copy change that is none of its business.
+ */
+const STORED_CREDENTIAL_LABEL = `Add ${getServiceAccountConnectNoun('snowflake-service-account')}`
+
+let root: Root | null = null
+let container: HTMLDivElement | null = null
+
+function mount(integration: Integration) {
+ ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
+ container = document.createElement('div')
+ document.body.appendChild(container)
+ root = createRoot(container)
+ act(() =>
+ root?.render()
+ )
+}
+
+/**
+ * The header's primary action, tagged by control kind. The tag matters: a
+ * `ChipDropdown` trigger renders the same "Add to Sim" placeholder as the plain
+ * chip, so comparing label text alone cannot tell one connect option from two.
+ * Radix marks its trigger with `aria-haspopup`; a bare `Chip` carries none.
+ */
+function headerAction(): string {
+ const bar = container?.firstElementChild?.firstElementChild
+ const buttons = Array.from(bar?.querySelectorAll('button') ?? [])
+ return buttons
+ .map((b) => `${b.hasAttribute('aria-haspopup') ? 'dropdown' : 'chip'}:${b.textContent?.trim()}`)
+ .join('|')
+}
+
+beforeEach(() => {
+ availabilityState.availability = { state: 'ready', oauthAvailable: false }
+ availabilityState.isLoading = false
+})
+
+afterEach(() => {
+ if (root) act(() => root?.unmount())
+ container?.remove()
+ root = null
+ container = null
+})
+
+describe('IntegrationBlockDetail header action', () => {
+ it('offers the stored service account for an integration with no OAuth path', () => {
+ mount(SERVICE_ACCOUNT_ONLY)
+
+ expect(headerAction()).toContain(`chip:${STORED_CREDENTIAL_LABEL}`)
+ })
+
+ it('keeps offering it while the availability answer is still in flight', () => {
+ availabilityState.availability = null
+ availabilityState.isLoading = true
+ mount(SERVICE_ACCOUNT_ONLY)
+
+ expect(headerAction()).toContain(`chip:${STORED_CREDENTIAL_LABEL}`)
+ })
+
+ /**
+ * A failed request leaves availability unresolved once loading ends. Hiding the
+ * control there strands a user who has a valid stored account behind a fetch
+ * they cannot retry, so it fails open — the server still refuses a provider the
+ * deployment does not offer.
+ */
+ it('fails open when the availability request settles with no answer', () => {
+ availabilityState.availability = null
+ availabilityState.isLoading = false
+ mount(SERVICE_ACCOUNT_ONLY)
+
+ expect(headerAction()).toContain(`chip:${STORED_CREDENTIAL_LABEL}`)
+ })
+
+ /**
+ * The OAuth path already defaults to available while unknown, so relaxing the
+ * service-account one too would widen this header from a chip to a dropdown and
+ * collapse it again as the config lands.
+ */
+ it('does not add a second option to an OAuth integration while loading', () => {
+ availabilityState.availability = null
+ availabilityState.isLoading = true
+ mount(OAUTH_WITH_SERVICE_ACCOUNT)
+
+ expect(headerAction()).toBe('chip:Add to Sim')
+ })
+
+ /**
+ * "Unavailable" is a verdict about a connection the deployment grants. An
+ * integration authenticated by a stored service account still runs on the
+ * user's own API key, so it keeps the ordinary call to action instead.
+ */
+ it('never calls a stored-credential integration unavailable', () => {
+ availabilityState.availability = { state: 'unavailable', oauthAvailable: false }
+ mount(SERVICE_ACCOUNT_ONLY)
+
+ const action = headerAction()
+ expect(action).not.toContain('Unavailable')
+ expect(action).toContain('chip:Add to Sim')
+ })
+
+ it('still calls an OAuth integration unavailable when its client is missing', () => {
+ availabilityState.availability = { state: 'unavailable', oauthAvailable: false }
+ mount(OAUTH_WITH_SERVICE_ACCOUNT)
+
+ expect(headerAction()).toContain('chip:Unavailable')
+ })
+})
diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx b/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx
index af31043f573..4af97190c70 100644
--- a/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx
@@ -12,6 +12,7 @@ import {
type Integration,
resolveCredentialDisplay,
resolveOAuthServiceForIntegration,
+ resolveServiceAccountServiceForIntegration,
} from '@/lib/integrations'
import { credentialProviderMatchesService } from '@/lib/oauth'
import { ConnectOAuthModal } from '@/app/workspace/[workspaceId]/components/connect-oauth-modal'
@@ -69,6 +70,7 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration
const matchingTemplates = getTemplatesForBlock(integration.type)
const suggestedSkills = getSuggestedSkillsForBlock(integration.type)
const oauthService = resolveOAuthServiceForIntegration(integration)
+ const serviceAccountService = resolveServiceAccountServiceForIntegration(integration)
const { integrationAvailability, isLoading: permissionConfigLoading } = usePermissionConfig()
const { chatEnabled } = useDeploymentShape()
const availability = integrationAvailability.get(integration.type.toLowerCase())
@@ -94,22 +96,39 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration
const connectedCredentials = useMemo(() => {
if (integration.type === 'gitlab')
return credentials.filter((c) => c.type === 'personal_token' && c.providerId === 'gitlab')
- if (!oauthService) return []
+ const credentialService = oauthService ?? serviceAccountService
+ if (!credentialService) return []
return credentials.filter(
(c) =>
(c.type === 'oauth' || c.type === 'service_account') &&
c.providerId &&
- credentialProviderMatchesService(c.providerId, oauthService)
+ credentialProviderMatchesService(c.providerId, credentialService)
)
- }, [credentials, oauthService, integration.type])
+ }, [credentials, oauthService, serviceAccountService, integration.type])
const [serviceAccountOpen, setServiceAccountOpen] = useState(false)
const serviceAccountTarget = useServiceAccountConnectTarget({
- serviceAccountProviderId: oauthService?.serviceAccountProviderId,
- serviceName: oauthService?.serviceName,
- serviceIcon: oauthService?.serviceIcon,
+ serviceAccountProviderId: serviceAccountService?.serviceAccountProviderId,
+ serviceName: serviceAccountService?.serviceName,
+ serviceIcon: serviceAccountService?.serviceIcon,
})
- const serviceAccountDeploymentAvailable =
- availability?.state === 'ready' || availability?.state === 'limited'
+ /**
+ * Unknown availability means two different things, and they want opposite
+ * defaults.
+ *
+ * While the permission config is still in flight the answer is imminent, so
+ * only an integration whose *sole* path is a stored service account offers the
+ * control — for one that also has OAuth, a `true` here would widen the header
+ * from a chip to a dropdown and then collapse it again as the config lands.
+ *
+ * Once both queries have settled and still produced nothing, the request
+ * failed, and withholding the control strands a user who has a perfectly good
+ * stored account behind a fetch they cannot retry. Fail open there, matching
+ * the `?? true` that `oauthAvailable` above already applies to the OAuth path;
+ * the server still refuses a provider the deployment does not offer.
+ */
+ const serviceAccountDeploymentAvailable = availability
+ ? availability.state === 'ready' || availability.state === 'limited'
+ : !oauthService || !permissionConfigLoading
const hasServiceAccount =
serviceAccountDeploymentAvailable &&
Boolean(serviceAccountTarget) &&
@@ -147,28 +166,29 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration
setConnectMode,
])
- const connectOptions = oauthService
- ? [
- ...(oauthAvailable
- ? [
- {
- value: CONNECT_MODE.oauth,
- label: 'Connect with OAuth',
- icon: oauthService.serviceIcon,
- },
- ]
- : []),
- ...(hasServiceAccount
- ? [
- {
- value: CONNECT_MODE.serviceAccount,
- label: serviceAccountConnectLabel,
- icon: serviceAccountTarget?.serviceIcon ?? oauthService.serviceIcon,
- },
- ]
- : []),
- ]
- : []
+ const connectOptions =
+ oauthService || serviceAccountService
+ ? [
+ ...(oauthAvailable && oauthService
+ ? [
+ {
+ value: CONNECT_MODE.oauth,
+ label: 'Connect with OAuth',
+ icon: oauthService.serviceIcon,
+ },
+ ]
+ : []),
+ ...(hasServiceAccount
+ ? [
+ {
+ value: CONNECT_MODE.serviceAccount,
+ label: serviceAccountConnectLabel,
+ icon: serviceAccountTarget?.serviceIcon ?? serviceAccountService?.serviceIcon,
+ },
+ ]
+ : []),
+ ]
+ : []
const handleSelectConnectOption = (value: string) => {
if (value === CONNECT_MODE.oauth) setOAuthOpen(true)
@@ -180,6 +200,21 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration
router.push(`/workspace/${workspaceId}/home`)
}
+ /**
+ * Shown when no connect flow is on offer. "Unavailable" is a verdict about a
+ * connection the deployment grants, so it belongs only to an integration with
+ * an OAuth path. One authenticated by a stored service account still runs on
+ * the user's own API key, and so keeps the catalog's ordinary call to action —
+ * the same fallback an integration with no credential service at all gets.
+ */
+ const connectFallback = oauthService ? (
+ Unavailable
+ ) : chatEnabled ? (
+
+ Add to Sim
+
+ ) : null
+
return (
@@ -191,7 +226,7 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration
setPersonalTokenOpen(true)}>
Add personal token
- ) : oauthService ? (
+ ) : oauthService || serviceAccountService ? (
connectOptions.length > 1 ? (
) : (
- Unavailable
+ connectFallback
)
- ) : chatEnabled ? (
-
- Add to Sim
-
- ) : null}
+ ) : (
+ connectFallback
+ )}
{personalTokenAvailable && (
@@ -247,7 +280,9 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration
onOpenChange={setServiceAccountOpen}
workspaceId={workspaceId}
serviceAccountProviderId={serviceAccountTarget.serviceAccountProviderId}
- atlassianProduct={oauthService?.providerId === 'confluence' ? 'confluence' : 'jira'}
+ atlassianProduct={
+ serviceAccountService?.providerId === 'confluence' ? 'confluence' : 'jira'
+ }
serviceName={serviceAccountTarget.serviceName}
serviceIcon={serviceAccountTarget.serviceIcon}
/>
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/integration-search-items.test.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/integration-search-items.test.ts
new file mode 100644
index 00000000000..0708d243445
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/integration-search-items.test.ts
@@ -0,0 +1,76 @@
+/**
+ * @vitest-environment node
+ */
+import { describe, expect, it } from 'vitest'
+import { CONNECT_MODE } from '@/app/workspace/[workspaceId]/integrations/connect-route'
+import { buildIntegrationSearchItems } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/integration-search-items'
+
+/** An OAuth integration, a stored-service-account one, and one with no credential. */
+const OAUTH_SLUG = 'airtable'
+const SERVICE_ACCOUNT_SLUG = 'claude-managed-agents'
+const NO_CREDENTIAL_SLUG = '1password'
+
+function hrefFor(slug: string, items: ReturnType): string {
+ const item = items.find((candidate) => candidate.id === slug)
+ if (!item) throw new Error(`Missing search item for ${slug}`)
+ return item.href
+}
+
+describe('buildIntegrationSearchItems', () => {
+ it('deep-links each integration to the connect flow its catalog entry describes', () => {
+ const items = buildIntegrationSearchItems('workspace-1')
+
+ expect(hrefFor(OAUTH_SLUG, items)).toBe(
+ `/workspace/workspace-1/integrations/${OAUTH_SLUG}?connect=${CONNECT_MODE.oauth}`
+ )
+ /**
+ * The default is what the sidebar falls back to while deployment
+ * availability is unknown. Assuming OAuth here would send this integration
+ * to a detail page with no OAuth flow to open, and the deep link would
+ * silently do nothing.
+ */
+ expect(hrefFor(SERVICE_ACCOUNT_SLUG, items)).toBe(
+ `/workspace/workspace-1/integrations/${SERVICE_ACCOUNT_SLUG}?connect=${CONNECT_MODE.serviceAccount}`
+ )
+ expect(hrefFor(NO_CREDENTIAL_SLUG, items)).toBe(
+ `/workspace/workspace-1/integrations/${NO_CREDENTIAL_SLUG}`
+ )
+ })
+
+ it('offers the catalog flow to the resolver and never consults it without a credential', () => {
+ const seen: Record = {}
+ const items = buildIntegrationSearchItems(
+ 'workspace-1',
+ undefined,
+ (blockType, catalogMode) => {
+ seen[blockType] = catalogMode
+ return catalogMode
+ }
+ )
+
+ expect(seen.airtable).toBe(CONNECT_MODE.oauth)
+ expect(seen.managed_agent).toBe(CONNECT_MODE.serviceAccount)
+ expect(seen.onepassword).toBeUndefined()
+ expect(hrefFor(NO_CREDENTIAL_SLUG, items)).toBe(
+ `/workspace/workspace-1/integrations/${NO_CREDENTIAL_SLUG}`
+ )
+ })
+
+ it('drops the deep link when the deployment offers no connect flow', () => {
+ const items = buildIntegrationSearchItems('workspace-1', undefined, () => null)
+
+ expect(hrefFor(OAUTH_SLUG, items)).toBe(`/workspace/workspace-1/integrations/${OAUTH_SLUG}`)
+ expect(hrefFor(SERVICE_ACCOUNT_SLUG, items)).toBe(
+ `/workspace/workspace-1/integrations/${SERVICE_ACCOUNT_SLUG}`
+ )
+ })
+
+ it('applies the block allowlist', () => {
+ const items = buildIntegrationSearchItems(
+ 'workspace-1',
+ (blockType) => blockType === 'managed_agent'
+ )
+
+ expect(items.map((item) => item.id)).toEqual([SERVICE_ACCOUNT_SLUG])
+ })
+})
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/integration-search-items.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/integration-search-items.ts
index 1d82af1f806..1ca8ae631b7 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/integration-search-items.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/integration-search-items.ts
@@ -3,6 +3,7 @@ import { blockTypeToIconMap, INTEGRATIONS, resolveCredentialDisplay } from '@/li
import {
CONNECT_MODE,
CONNECT_QUERY_PARAM,
+ type ConnectMode,
} from '@/app/workspace/[workspaceId]/integrations/connect-route'
import type { IntegrationSearchItem } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/utils'
import type { WorkspaceCredential } from '@/hooks/queries/credentials'
@@ -21,7 +22,16 @@ const INTEGRATION_BASES: readonly {
icon: ComponentType<{ className?: string }>
bgColor: string
slug: string
- authType: string
+ /**
+ * The connect flow this integration would offer knowing only the catalog, or
+ * `null` when it has no credential at all. Derived from the credential
+ * services rather than `authType`, because an integration can be `api-key`
+ * there and still authenticate with a stored service account (NetSuite,
+ * Snowflake, Harmonic, Claude Platform). Used as the deep link while
+ * deployment availability is unknown, where assuming OAuth would send those
+ * four to a page that has no OAuth flow to open.
+ */
+ catalogConnectMode: ConnectMode | null
blockType: string
}[] = INTEGRATIONS.flatMap((integration) => {
const icon = blockTypeToIconMap[integration.type]
@@ -33,7 +43,11 @@ const INTEGRATION_BASES: readonly {
icon,
bgColor: integration.bgColor,
slug: integration.slug,
- authType: integration.authType,
+ catalogConnectMode: integration.oauthServiceId
+ ? CONNECT_MODE.oauth
+ : integration.serviceAccountServiceId
+ ? CONNECT_MODE.serviceAccount
+ : null,
blockType: integration.type,
},
]
@@ -41,19 +55,27 @@ const INTEGRATION_BASES: readonly {
/**
* Builds the full integration catalog as search items for a given workspace.
- * OAuth integrations link directly to the detail page with `?connect=oauth` so
- * the connect modal auto-opens (via the detail page's `useEffect` on
- * `CONNECT_QUERY_PARAM`). Non-OAuth integrations link to the plain detail page.
+ * An integration with a credential links to the detail page carrying the connect
+ * mode `getConnectMode` picks for it, so that modal auto-opens (via the detail
+ * page's `useEffect` on `CONNECT_QUERY_PARAM`). Everything else — and anything
+ * with no connect flow currently on offer — links to the plain detail page.
+ *
+ * `getConnectMode` receives the catalog's own answer as its second argument, to
+ * return verbatim when deployment availability cannot be read; returning `null`
+ * means the deployment offers no connect flow, which is not the same thing.
*/
export function buildIntegrationSearchItems(
workspaceId: string,
isBlockAllowed: (blockType: string) => boolean = () => true,
- getConnectMode: (
- blockType: string
- ) => (typeof CONNECT_MODE)[keyof typeof CONNECT_MODE] | null = () => CONNECT_MODE.oauth
+ getConnectMode: (blockType: string, catalogConnectMode: ConnectMode) => ConnectMode | null = (
+ _blockType,
+ catalogConnectMode
+ ) => catalogConnectMode
): IntegrationSearchItem[] {
return INTEGRATION_BASES.filter((base) => isBlockAllowed(base.blockType)).map((base) => {
- const connectMode = base.authType === 'oauth' ? getConnectMode(base.blockType) : null
+ const connectMode = base.catalogConnectMode
+ ? getConnectMode(base.blockType, base.catalogConnectMode)
+ : null
const connectSuffix = connectMode ? `?${CONNECT_QUERY_PARAM}=${connectMode}` : ''
return {
id: base.id,
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx
index 683439f271b..c29fae78bdc 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx
@@ -1056,11 +1056,28 @@ export const Sidebar = memo(function Sidebar({ organizationHref }: SidebarProps)
() =>
permissionConfig.hideIntegrationsTab
? []
- : buildIntegrationSearchItems(workspaceId, isBlockAllowed, (blockType) => {
+ : buildIntegrationSearchItems(workspaceId, isBlockAllowed, (blockType, catalogMode) => {
const availability = integrationAvailability.get(blockType.toLowerCase())
- if (!availability) return CONNECT_MODE.oauth
- if (availability?.oauthAvailable) return CONNECT_MODE.oauth
- if (availability?.state === 'limited') return CONNECT_MODE.serviceAccount
+ /**
+ * Availability is unknown while it loads and after a failed fetch,
+ * so keep the catalog's own flow rather than assuming OAuth — that
+ * assumption sends a service-account-only integration to a page with
+ * no OAuth modal to open, costing the search result its one click.
+ */
+ if (!availability) return catalogMode
+ if (availability.oauthAvailable) return CONNECT_MODE.oauth
+ /**
+ * Anything still connectable once OAuth is out is the stored
+ * service account, which is the detail page's own test for offering
+ * it. Matching only `limited` misses an integration whose *only*
+ * credential is a service account: it is plain `ready`, and would
+ * open the detail page with no modal. The caller only asks about
+ * integrations that have a credential service, so an ordinary
+ * API-key block never reaches this.
+ */
+ if (availability.state === 'ready' || availability.state === 'limited') {
+ return CONNECT_MODE.serviceAccount
+ }
return null
}),
[workspaceId, permissionConfig.hideIntegrationsTab, isBlockAllowed, integrationAvailability]
diff --git a/apps/sim/lib/integrations/availability.server.test.ts b/apps/sim/lib/integrations/availability.server.test.ts
index b976b30ef18..7f9a7d22865 100644
--- a/apps/sim/lib/integrations/availability.server.test.ts
+++ b/apps/sim/lib/integrations/availability.server.test.ts
@@ -12,6 +12,7 @@ import { afterAll, describe, expect, it } from 'vitest'
import {
getIntegrationTypesForOAuthServiceId,
type IntegrationAvailability,
+ isDeploymentGatedIntegrationType,
isOAuthServiceAllowedByIntegrationTypes,
resolveIntegrationAvailability,
resolveIntegrationAvailabilityStateForVisibility,
@@ -34,6 +35,19 @@ afterAll(resetEnvMock)
const integrations = integrationsJson.integrations as readonly Integration[]
+/**
+ * Integrations whose only credential is a stored service account, each paired
+ * with the block type the catalog gates it by. The two differ for Claude
+ * Platform (`claude-platform` / `managed_agent`), so a parameterization keyed
+ * on the service id alone silently omits it.
+ */
+const STORED_CREDENTIAL_INTEGRATIONS = [
+ { serviceId: 'netsuite', blockType: 'netsuite' },
+ { serviceId: 'snowflake', blockType: 'snowflake' },
+ { serviceId: 'harmonic', blockType: 'harmonic' },
+ { serviceId: 'claude-platform', blockType: 'managed_agent' },
+]
+
function availabilityFor(
type: string,
values: Parameters[0] = {}
@@ -44,6 +58,31 @@ function availabilityFor(
}
describe('integration availability', () => {
+ it.each(STORED_CREDENTIAL_INTEGRATIONS)(
+ 'makes $serviceId stored credentials available without configuring an OAuth client',
+ ({ serviceId, blockType }) => {
+ expect(availabilityFor(blockType)).toMatchObject({
+ state: 'ready',
+ oauthAvailable: false,
+ serviceAccountAvailable: true,
+ missingFields: [],
+ })
+ expect(getIntegrationTypesForOAuthServiceId(serviceId)).toEqual([blockType])
+ /**
+ * Joining the deployment-gated set cannot hide these blocks: `isBlockAllowed`
+ * drops a gated type only at `unavailable`/`misconfigured`, and the `ready`
+ * asserted above holds for every deployment because none of these services
+ * carries a `deploymentRequirement`.
+ */
+ expect(isDeploymentGatedIntegrationType(blockType)).toBe(true)
+ expect(isOAuthServiceAllowedByIntegrationTypes(serviceId, new Set([blockType]))).toBe(true)
+ expect(isOAuthServiceAllowedByIntegrationTypes(serviceId, new Set(['jira']))).toBe(false)
+ expect(SERVICE_ACCOUNT_METADATA_BY_OAUTH_SERVICE_ID[serviceId]?.providerId).toBe(
+ `${serviceId}-service-account`
+ )
+ expect(CREDENTIAL_CONFIGURED_OAUTH_SERVICE_IDS).not.toContain(serviceId)
+ }
+ )
it('does not infer GitHub repository OAuth readiness from its API-key workflow block', () => {
expect(availabilityFor('github_v2')).toMatchObject({ state: 'ready', oauthAvailable: false })
expect(
@@ -190,24 +229,26 @@ describe('integration availability', () => {
})
it('keeps service-account metadata in parity with canonical OAuth services', () => {
- const oauthServiceIds = [
+ const credentialServiceIds = [
...new Set(
- integrations.flatMap((integration) =>
- integration.authType === 'oauth' && integration.oauthServiceId
- ? [integration.oauthServiceId]
- : []
- )
+ integrations.flatMap((integration) => {
+ const serviceId = integration.serviceAccountServiceId ?? integration.oauthServiceId
+ return serviceId ? [serviceId] : []
+ })
),
]
const expectedServiceAccountIds: Record = {}
const expectedCredentialConfiguredServiceIds: string[] = []
- for (const oauthServiceId of oauthServiceIds) {
+ for (const oauthServiceId of credentialServiceIds) {
const canonical = getServiceConfigByServiceId(oauthServiceId)
if (!canonical) throw new Error(`Missing canonical OAuth service ${oauthServiceId}`)
const projected = SERVICE_ACCOUNT_METADATA_BY_OAUTH_SERVICE_ID[oauthServiceId]
expect(projected?.providerId, oauthServiceId).toBe(canonical.serviceAccountProviderId)
- if (canonical.clientConfiguration) {
+ if (
+ canonical.clientConfiguration &&
+ integrations.some((integration) => integration.oauthServiceId === oauthServiceId)
+ ) {
expectedCredentialConfiguredServiceIds.push(oauthServiceId)
}
if (canonical.serviceAccountProviderId) {
diff --git a/apps/sim/lib/integrations/credential-display.test.ts b/apps/sim/lib/integrations/credential-display.test.ts
index b89865d2899..ac60759feab 100644
--- a/apps/sim/lib/integrations/credential-display.test.ts
+++ b/apps/sim/lib/integrations/credential-display.test.ts
@@ -5,7 +5,10 @@ import {
import integrationsJson from '@sim/deployment-config/integrations.json'
import { describe, expect, it } from 'vitest'
import { getIntegrationsForCredentialProvider } from '@/lib/integrations/credential-display'
-import { resolveOAuthServiceForIntegration } from '@/lib/integrations/oauth-service'
+import {
+ resolveOAuthServiceForIntegration,
+ resolveServiceAccountServiceForIntegration,
+} from '@/lib/integrations/oauth-service'
import type { Integration } from '@/lib/integrations/types'
import { OAUTH_PROVIDERS } from '@/lib/oauth/oauth'
import { credentialProviderMatchesService } from '@/lib/oauth/utils'
@@ -56,21 +59,17 @@ const EXPECTED_COVERAGE: Record = {
'google-tasks',
'google-vault',
],
- 'harmonic-service-account': [],
+ 'harmonic-service-account': ['harmonic'],
'hubspot-service-account': ['hubspot'],
'linear-service-account': ['linear'],
'monday-service-account': ['monday'],
'notion-service-account': ['notion'],
- // NetSuite remains an API-key catalog integration, like Snowflake, while its
- // block uses the shared reusable-credential selector.
- 'netsuite-service-account': [],
+ 'netsuite-service-account': ['oracle-netsuite'],
'pipedrive-service-account': ['pipedrive'],
'salesforce-service-account': ['salesforce'],
'shopify-service-account': ['shopify'],
'slack-custom-bot': ['slack'],
- // Snowflake's catalog entry is api-key (there is no Snowflake OAuth client),
- // so its credential is offered on the block rather than an integration page.
- 'snowflake-service-account': [],
+ 'snowflake-service-account': ['snowflake'],
'trello-service-account': ['trello'],
'wealthbox-service-account': ['wealthbox'],
'webflow-service-account': ['webflow'],
@@ -133,7 +132,9 @@ describe('service-account coverage', () => {
const covered = new Set(getIntegrationsForCredentialProvider(providerId).map((i) => i.slug))
for (const integration of INTEGRATIONS) {
- const service = resolveOAuthServiceForIntegration(integration)
+ const service =
+ resolveOAuthServiceForIntegration(integration) ??
+ resolveServiceAccountServiceForIntegration(integration)
if (!service) continue
expect(
credentialProviderMatchesService(providerId, service),
diff --git a/apps/sim/lib/integrations/credential-visibility.server.test.ts b/apps/sim/lib/integrations/credential-visibility.server.test.ts
index 566ce32c374..fc531fda911 100644
--- a/apps/sim/lib/integrations/credential-visibility.server.test.ts
+++ b/apps/sim/lib/integrations/credential-visibility.server.test.ts
@@ -68,6 +68,24 @@ describe('integration credential visibility', () => {
})
it.each([
+ {
+ serviceId: 'netsuite',
+ providerId: 'netsuite-service-account',
+ blockType: 'netsuite',
+ name: 'NetSuite',
+ },
+ {
+ serviceId: 'snowflake',
+ providerId: 'snowflake-service-account',
+ blockType: 'snowflake',
+ name: 'Snowflake',
+ },
+ {
+ serviceId: 'harmonic',
+ providerId: 'harmonic-service-account',
+ blockType: 'harmonic',
+ name: 'Harmonic',
+ },
{
serviceId: 'coda',
providerId: 'coda-service-account',
diff --git a/apps/sim/lib/integrations/index.ts b/apps/sim/lib/integrations/index.ts
index 463096463bc..3a5f47e20a5 100644
--- a/apps/sim/lib/integrations/index.ts
+++ b/apps/sim/lib/integrations/index.ts
@@ -68,6 +68,7 @@ export {
type OAuthServiceMatch,
resolveOAuthServiceForIntegration,
resolveOAuthServiceForSlug,
+ resolveServiceAccountServiceForIntegration,
} from '@/lib/integrations/oauth-service'
export type { AuthType, FAQItem, Integration, IntegrationSummary } from '@/lib/integrations/types'
export type { BlockMeta, BlockTemplate } from '@/blocks/types'
diff --git a/apps/sim/lib/integrations/oauth-service.test.ts b/apps/sim/lib/integrations/oauth-service.test.ts
index 0f7e18b5621..d088592bae7 100644
--- a/apps/sim/lib/integrations/oauth-service.test.ts
+++ b/apps/sim/lib/integrations/oauth-service.test.ts
@@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest'
import {
resolveOAuthServiceForSlug,
resolveServiceAccountIntegration,
+ resolveServiceAccountServiceForIntegration,
} from '@/lib/integrations/oauth-service'
import type { Integration } from '@/lib/integrations/types'
@@ -63,6 +64,60 @@ describe('resolveOAuthServiceForSlug', () => {
})
describe('resolveServiceAccountIntegration', () => {
+ it.each(['netsuite', 'snowflake', 'harmonic', 'claude-platform'])(
+ 'resolves %s without offering OAuth',
+ (serviceId) => {
+ const integration = INTEGRATIONS.find((entry) => entry.serviceAccountServiceId === serviceId)
+ if (!integration) throw new Error(`Missing service-account integration for ${serviceId}`)
+ expect(integration.authType).toBe('api-key')
+ expect(resolveOAuthServiceForSlug(integration.slug)).toBeNull()
+ expect(
+ resolveServiceAccountServiceForIntegration(integration)?.serviceAccountProviderId
+ ).toBe(`${serviceId}-service-account`)
+ expect(resolveServiceAccountIntegration(serviceId)?.slug).toBe(integration.slug)
+ }
+ )
+
+ /**
+ * The match carries its own icon because the connect control cannot recover
+ * one for these four: `resolveOAuthServiceForSlug` answers `null` for a
+ * non-OAuth catalog entry, and a missing icon makes
+ * `useServiceAccountConnectTarget` return `null` — rendering nothing at all
+ * rather than a broken chip, which is why the gap was invisible.
+ */
+ it('carries a service icon on every service-account match', () => {
+ const matches = INTEGRATIONS.map((entry) => ({
+ slug: entry.slug,
+ match: resolveServiceAccountIntegration(entry.slug),
+ })).filter(({ match }) => match)
+ expect(matches.length).toBeGreaterThan(0)
+ for (const { slug, match } of matches) {
+ expect(typeof match?.serviceIcon, slug).toBe('function')
+ }
+ for (const serviceId of ['netsuite', 'snowflake', 'harmonic', 'claude-platform']) {
+ const integration = INTEGRATIONS.find((entry) => entry.serviceAccountServiceId === serviceId)
+ if (!integration) throw new Error(`Missing service-account integration for ${serviceId}`)
+ expect(resolveServiceAccountIntegration(integration.slug)?.serviceIcon, serviceId).toBe(
+ resolveServiceAccountServiceForIntegration(integration)?.serviceIcon
+ )
+ }
+ })
+
+ it('only offers the OAuth fallback when the canonical service supports stored accounts', () => {
+ const jira = INTEGRATIONS.find((entry) => entry.slug === 'jira')
+ const x = INTEGRATIONS.find((entry) => entry.type === 'x')
+ if (!jira || !x) throw new Error('Missing OAuth integration fixtures')
+ expect(resolveServiceAccountServiceForIntegration(jira)?.serviceAccountProviderId).toBe(
+ 'atlassian-service-account'
+ )
+ expect(resolveServiceAccountServiceForIntegration(x)).toBeNull()
+ expect(
+ resolveServiceAccountServiceForIntegration({
+ ...x,
+ serviceAccountServiceId: 'unknown-service',
+ })
+ ).toBeNull()
+ })
it.concurrent('keeps a named service instead of collapsing to the family default', () => {
// Every Google integration issues the same google-service-account
// credential, so a fuzzy matcher can silently answer Drive for all of
diff --git a/apps/sim/lib/integrations/oauth-service.ts b/apps/sim/lib/integrations/oauth-service.ts
index 36ac1922d7e..eff65d1472f 100644
--- a/apps/sim/lib/integrations/oauth-service.ts
+++ b/apps/sim/lib/integrations/oauth-service.ts
@@ -48,6 +48,27 @@ export function resolveOAuthServiceForIntegration(
}
}
+interface ServiceAccountServiceMatch extends Omit {
+ serviceAccountProviderId: ServiceAccountProviderId
+}
+
+/** Resolves a stored service-account connection independently of the OAuth catalog marker. */
+export function resolveServiceAccountServiceForIntegration(
+ integration: IntegrationMetadata
+): ServiceAccountServiceMatch | null {
+ const serviceId = integration.serviceAccountServiceId ?? integration.oauthServiceId
+ if (!serviceId) return null
+ const service = getServiceConfigByServiceId(serviceId)
+ const serviceAccountProviderId = asServiceAccountProviderId(service?.serviceAccountProviderId)
+ if (!service || !serviceAccountProviderId) return null
+ return {
+ providerId: service.providerId,
+ serviceName: service.name,
+ serviceIcon: service.icon as ComponentType<{ className?: string }>,
+ serviceAccountProviderId,
+ }
+}
+
/**
* Resolves the integration entry for a catalog slug, then derives its OAuth
* service match. Returns `null` when the slug is unknown or the matching
@@ -67,6 +88,7 @@ export interface ServiceAccountIntegrationMatch {
slug: string
serviceAccountProviderId: ServiceAccountProviderId
serviceName: string
+ serviceIcon: ComponentType<{ className?: string }>
providerId: string
}
@@ -92,18 +114,19 @@ export const CANONICAL_SERVICE_ACCOUNT_SLUGS: Readonly> =
/**
* Every integration that offers a service-account flow, in catalog order.
- * Built once — `resolveOAuthServiceForIntegration` walks `OAUTH_PROVIDERS` per
+ * Built once — `resolveServiceAccountServiceForIntegration` walks `OAUTH_PROVIDERS` per
* entry, which is wasted work to repeat on each lookup.
*/
const SERVICE_ACCOUNT_INTEGRATIONS: readonly ServiceAccountIntegrationMatch[] =
INTEGRATION_METADATA.flatMap((integration) => {
- const match = resolveOAuthServiceForIntegration(integration)
+ const match = resolveServiceAccountServiceForIntegration(integration)
if (!match?.serviceAccountProviderId) return []
return [
{
slug: integration.slug,
serviceAccountProviderId: match.serviceAccountProviderId,
serviceName: integration.name,
+ serviceIcon: match.serviceIcon,
providerId: match.providerId,
},
]
diff --git a/apps/sim/vitest.setup.ts b/apps/sim/vitest.setup.ts
index 47691953962..c747c54c273 100644
--- a/apps/sim/vitest.setup.ts
+++ b/apps/sim/vitest.setup.ts
@@ -131,6 +131,8 @@ vi.mock('@/blocks/registry', () => ({
outputs: {},
})),
getAllBlocks: vi.fn(() => []),
+ getTemplatesForBlock: vi.fn(() => []),
+ getSuggestedSkillsForBlock: vi.fn(() => []),
getLatestBlock: vi.fn(() => undefined),
/** Detail-read accessor: version-resolved and projected through the viewer's visibility. */
getLatestBlockForViewer: vi.fn(() => undefined),
diff --git a/packages/deployment-config/src/integration-availability.ts b/packages/deployment-config/src/integration-availability.ts
index 8dcc11a6f4d..f79a5831dcb 100644
--- a/packages/deployment-config/src/integration-availability.ts
+++ b/packages/deployment-config/src/integration-availability.ts
@@ -22,9 +22,9 @@ export interface IntegrationAvailability {
const credentialConfiguredOAuthServiceIds = new Set(CREDENTIAL_CONFIGURED_OAUTH_SERVICE_IDS)
const deploymentGatedIntegrationTypes = new Set(
- INTEGRATION_METADATA.filter((integration) => integration.authType === 'oauth').map(
- (integration) => integration.type.toLowerCase()
- )
+ INTEGRATION_METADATA.filter(
+ (integration) => integration.authType === 'oauth' || integration.serviceAccountServiceId
+ ).map((integration) => integration.type.toLowerCase())
)
const integrationTypesByOAuthServiceId = new Map()
/** Search authorization shares GitHub's integration policy while its workflow tools retain PAT auth. */
@@ -40,11 +40,14 @@ for (const [serviceId, integrationType] of tokenCredentialIntegrationTypes) {
const tokenCredentialTypes = new Set(tokenCredentialIntegrationTypes.values())
const previewServiceAccountProvidersByIntegrationType = new Map()
for (const integration of INTEGRATION_METADATA) {
- if (integration.authType !== 'oauth' || !integration.oauthServiceId) continue
- const serviceId = integration.oauthServiceId.toLowerCase()
+ const credentialServiceId = integration.serviceAccountServiceId ?? integration.oauthServiceId
+ if (!credentialServiceId) continue
+ const serviceId = credentialServiceId.toLowerCase()
const current = integrationTypesByOAuthServiceId.get(serviceId) ?? []
const integrationType = integration.type.toLowerCase()
- integrationTypesByOAuthServiceId.set(serviceId, [...current, integrationType])
+ if (!current.includes(integrationType)) {
+ integrationTypesByOAuthServiceId.set(serviceId, [...current, integrationType])
+ }
const serviceAccount = getServiceAccountMetadata(serviceId)
if (serviceAccount?.deploymentRequirement !== 'preview-gated') continue
@@ -55,7 +58,7 @@ export function isDeploymentGatedIntegrationType(blockType: string): boolean {
return deploymentGatedIntegrationTypes.has(blockType.toLowerCase())
}
-/** Returns the generated integration block types authenticated by one OAuth service entry. */
+/** Returns block types using a canonical credential service, including stored service accounts. */
export function getIntegrationTypesForOAuthServiceId(serviceId: string): readonly string[] {
return integrationTypesByOAuthServiceId.get(serviceId.toLowerCase()) ?? []
}
@@ -147,6 +150,29 @@ export function resolveIntegrationAvailability(
return resolveOAuthIntegrationAvailability(integration, values)
}
+ if (integration.serviceAccountServiceId) {
+ const serviceAccount = getServiceAccountMetadata(integration.serviceAccountServiceId)
+ if (!serviceAccount) {
+ throw new Error(`Integration ${integration.slug} is missing service-account metadata`)
+ }
+ const capabilityId = resolveOAuthClientCapabilityId(integration.serviceAccountServiceId)
+ const serviceAccountAvailable =
+ serviceAccount.deploymentRequirement !== 'preview-gated' &&
+ (serviceAccount.deploymentRequirement !== 'oauth-client' ||
+ Boolean(
+ capabilityId && inspectOAuthClientCapability(capabilityId, values).state === 'ready'
+ ))
+ return {
+ type: integration.type,
+ slug: integration.slug,
+ name: integration.name,
+ state: serviceAccountAvailable ? 'ready' : 'unavailable',
+ oauthAvailable: false,
+ serviceAccountAvailable,
+ missingFields: [],
+ }
+ }
+
return {
type: integration.type,
slug: integration.slug,
diff --git a/packages/deployment-config/src/integration-metadata.ts b/packages/deployment-config/src/integration-metadata.ts
index caf3e718b69..fde6652b9e0 100644
--- a/packages/deployment-config/src/integration-metadata.ts
+++ b/packages/deployment-config/src/integration-metadata.ts
@@ -8,10 +8,11 @@ export interface IntegrationMetadata {
name: string
authType: 'oauth' | 'api-key' | 'none'
oauthServiceId?: string
+ serviceAccountServiceId?: string
bgColor: string
integrationType: string
}
export const INTEGRATION_METADATA: readonly IntegrationMetadata[] = JSON.parse(
- '[{"type":"onepassword","slug":"1password","name":"1Password","authType":"api-key","bgColor":"#FFFFFF","integrationType":"security"},{"type":"affinity","slug":"affinity","name":"Affinity","authType":"api-key","bgColor":"#1B1C1E","integrationType":"sales"},{"type":"agentmail","slug":"agentmail","name":"AgentMail","authType":"api-key","bgColor":"#000000","integrationType":"email"},{"type":"agentphone","slug":"agentphone","name":"AgentPhone","authType":"api-key","bgColor":"linear-gradient(135deg, #1a1a1a 0%, #0a2a14 100%)","integrationType":"communication"},{"type":"agiloft","slug":"agiloft","name":"Agiloft","authType":"api-key","bgColor":"#001028","integrationType":"productivity"},{"type":"ahrefs","slug":"ahrefs","name":"Ahrefs","authType":"api-key","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"airtable","slug":"airtable","name":"Airtable","authType":"oauth","oauthServiceId":"airtable","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"airweave","slug":"airweave","name":"Airweave","authType":"api-key","bgColor":"#6366F1","integrationType":"search"},{"type":"algolia","slug":"algolia","name":"Algolia","authType":"api-key","bgColor":"#003DFF","integrationType":"search"},{"type":"dynamodb","slug":"amazon-dynamodb","name":"Amazon DynamoDB","authType":"api-key","bgColor":"linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)","integrationType":"databases"},{"type":"rds","slug":"amazon-rds","name":"Amazon RDS","authType":"none","bgColor":"linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)","integrationType":"databases"},{"type":"sqs","slug":"amazon-sqs","name":"Amazon SQS","authType":"api-key","bgColor":"linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)","integrationType":"devops"},{"type":"amplitude","slug":"amplitude","name":"Amplitude","authType":"api-key","bgColor":"#13294B","integrationType":"analytics"},{"type":"apify","slug":"apify","name":"Apify","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"apollo","slug":"apollo","name":"Apollo","authType":"api-key","bgColor":"#EBF212","integrationType":"sales"},{"type":"arxiv","slug":"arxiv","name":"ArXiv","authType":"none","bgColor":"#FFFFFF","integrationType":"search"},{"type":"asana","slug":"asana","name":"Asana","authType":"oauth","oauthServiceId":"asana","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"ashby","slug":"ashby","name":"Ashby","authType":"api-key","bgColor":"#5D4ED6","integrationType":"hr"},{"type":"athena","slug":"athena","name":"Athena","authType":"none","bgColor":"linear-gradient(45deg, #4D27A8 0%, #A166FF 100%)","integrationType":"analytics"},{"type":"attio","slug":"attio","name":"Attio","authType":"oauth","oauthServiceId":"attio","bgColor":"#1D1E20","integrationType":"sales"},{"type":"appconfig","slug":"aws-appconfig","name":"AWS AppConfig","authType":"api-key","bgColor":"linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)","integrationType":"devops"},{"type":"iam","slug":"aws-iam","name":"AWS IAM","authType":"api-key","bgColor":"linear-gradient(45deg, #BD0816 0%, #FF5252 100%)","integrationType":"security"},{"type":"identity_center","slug":"aws-identity-center","name":"AWS Identity Center","authType":"api-key","bgColor":"linear-gradient(45deg, #BD0816 0%, #FF5252 100%)","integrationType":"security"},{"type":"secrets_manager","slug":"aws-secrets-manager","name":"AWS Secrets Manager","authType":"none","bgColor":"linear-gradient(45deg, #BD0816 0%, #FF5252 100%)","integrationType":"security"},{"type":"ses","slug":"aws-ses","name":"AWS SES","authType":"api-key","bgColor":"linear-gradient(45deg, #BD0816 0%, #FF5252 100%)","integrationType":"email"},{"type":"sts","slug":"aws-sts","name":"AWS STS","authType":"api-key","bgColor":"linear-gradient(45deg, #BD0816 0%, #FF5252 100%)","integrationType":"security"},{"type":"ssm","slug":"aws-systems-manager","name":"AWS Systems Manager","authType":"api-key","bgColor":"#E7157B","integrationType":"devops"},{"type":"textract_v2","slug":"aws-textract","name":"AWS Textract","authType":"api-key","bgColor":"linear-gradient(135deg, #055F4E 0%, #56C0A7 100%)","integrationType":"ai"},{"type":"microsoft_ad","slug":"azure-ad","name":"Azure AD","authType":"oauth","oauthServiceId":"microsoft-ad","bgColor":"#0078D4","integrationType":"security"},{"type":"azure_data_explorer","slug":"azure-data-explorer","name":"Azure Data Explorer","authType":"api-key","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"azure_devops","slug":"azure-devops","name":"Azure DevOps","authType":"api-key","bgColor":"#0078D4","integrationType":"devops"},{"type":"bitbucket","slug":"bitbucket","name":"Bitbucket","authType":"oauth","oauthServiceId":"bitbucket","bgColor":"#FFFFFF","integrationType":"devops"},{"type":"box_v2","slug":"box","name":"Box","authType":"oauth","oauthServiceId":"box","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"brandfetch","slug":"brandfetch","name":"Brandfetch","authType":"api-key","bgColor":"#000000","integrationType":"sales"},{"type":"brex","slug":"brex","name":"Brex","authType":"api-key","bgColor":"#171717","integrationType":"commerce"},{"type":"brightdata","slug":"bright-data","name":"Bright Data","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"browser_use","slug":"browser-use","name":"Browser Use","authType":"api-key","bgColor":"#181C1E","integrationType":"ai"},{"type":"buffer","slug":"buffer","name":"Buffer","authType":"api-key","bgColor":"#FFFFFF","integrationType":"marketing"},{"type":"calcom","slug":"cal-com","name":"Cal.com","authType":"oauth","oauthServiceId":"calcom","bgColor":"#292929","integrationType":"productivity"},{"type":"calendly","slug":"calendly","name":"Calendly","authType":"api-key","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"cbinsights","slug":"cb-insights","name":"CB Insights","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"circleback","slug":"circleback","name":"Circleback","authType":"api-key","bgColor":"linear-gradient(180deg, #E0F7FA 0%, #FFFFFF 100%)","integrationType":"ai"},{"type":"managed_agent","slug":"claude-managed-agents","name":"Claude Managed Agents","authType":"api-key","bgColor":"#DA7756","integrationType":"ai"},{"type":"clay","slug":"clay","name":"Clay","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"clerk","slug":"clerk","name":"Clerk","authType":"none","bgColor":"#131316","integrationType":"security"},{"type":"clickhouse","slug":"clickhouse","name":"ClickHouse","authType":"none","bgColor":"#f9ff69","integrationType":"databases"},{"type":"clickup","slug":"clickup","name":"ClickUp","authType":"oauth","oauthServiceId":"clickup","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"cloudflare","slug":"cloudflare","name":"Cloudflare","authType":"api-key","bgColor":"#F5F6FA","integrationType":"devops"},{"type":"cloudformation","slug":"cloudformation","name":"CloudFormation","authType":"none","bgColor":"linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)","integrationType":"devops"},{"type":"cloudtrail","slug":"cloudtrail","name":"CloudTrail","authType":"api-key","bgColor":"linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)","integrationType":"security"},{"type":"cloudwatch","slug":"cloudwatch","name":"CloudWatch","authType":"none","bgColor":"linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)","integrationType":"observability"},{"type":"coda","slug":"coda","name":"Coda","authType":"api-key","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"codepipeline","slug":"codepipeline","name":"CodePipeline","authType":"api-key","bgColor":"linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)","integrationType":"devops"},{"type":"confluence_v2","slug":"confluence","name":"Confluence","authType":"oauth","oauthServiceId":"confluence","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"context_dev","slug":"context-dev","name":"Context.dev","authType":"api-key","bgColor":"#ffffff","integrationType":"search"},{"type":"convex","slug":"convex","name":"Convex","authType":"api-key","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"crowdstrike","slug":"crowdstrike","name":"CrowdStrike","authType":"api-key","bgColor":"#E01F3D","integrationType":"security"},{"type":"crunchbase","slug":"crunchbase","name":"Crunchbase","authType":"api-key","bgColor":"#0287D1","integrationType":"sales"},{"type":"cursor_v2","slug":"cursor","name":"Cursor","authType":"api-key","bgColor":"#1E1E1E","integrationType":"devops"},{"type":"dagster","slug":"dagster","name":"Dagster","authType":"api-key","bgColor":"#ffffff","integrationType":"observability"},{"type":"databricks","slug":"databricks","name":"Databricks","authType":"api-key","bgColor":"#F9F7F4","integrationType":"databases"},{"type":"datadog","slug":"datadog","name":"Datadog","authType":"api-key","bgColor":"#632CA6","integrationType":"observability"},{"type":"datagma","slug":"datagma","name":"Datagma","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"daytona","slug":"daytona","name":"Daytona","authType":"api-key","bgColor":"#FFFFFF","integrationType":"devops"},{"type":"devin","slug":"devin","name":"Devin","authType":"api-key","bgColor":"#12141A","integrationType":"devops"},{"type":"discord","slug":"discord","name":"Discord","authType":"api-key","bgColor":"#5865F2","integrationType":"communication"},{"type":"docusign","slug":"docusign","name":"DocuSign","authType":"oauth","oauthServiceId":"docusign","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"downdetector","slug":"downdetector","name":"Downdetector","authType":"api-key","bgColor":"#FFFFFF","integrationType":"observability"},{"type":"dropbox_v2","slug":"dropbox","name":"Dropbox","authType":"oauth","oauthServiceId":"dropbox","bgColor":"#0061FF","integrationType":"documents"},{"type":"dropcontact","slug":"dropcontact","name":"Dropcontact","authType":"api-key","bgColor":"#0ABA9F","integrationType":"sales"},{"type":"dspy","slug":"dspy","name":"DSPy","authType":"api-key","bgColor":"#FFFFFF","integrationType":"ai"},{"type":"dub_v2","slug":"dub","name":"Dub","authType":"api-key","bgColor":"#181C1E","integrationType":"devops"},{"type":"duckduckgo","slug":"duckduckgo","name":"DuckDuckGo","authType":"none","bgColor":"#FFFFFF","integrationType":"search"},{"type":"dynatrace","slug":"dynatrace","name":"Dynatrace","authType":"api-key","bgColor":"#FFFFFF","integrationType":"observability"},{"type":"elasticsearch","slug":"elasticsearch","name":"Elasticsearch","authType":"api-key","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"elevenlabs","slug":"elevenlabs","name":"ElevenLabs","authType":"api-key","bgColor":"#181C1E","integrationType":"ai"},{"type":"emailbison","slug":"email-bison","name":"Email Bison","authType":"api-key","bgColor":"#FB7A22","integrationType":"email"},{"type":"embeddings","slug":"embeddings","name":"Embeddings","authType":"api-key","bgColor":"#7B4DFF","integrationType":"ai"},{"type":"enrich","slug":"enrich","name":"Enrich","authType":"api-key","bgColor":"#E5E5E6","integrationType":"sales"},{"type":"enrow","slug":"enrow","name":"Enrow","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"exa","slug":"exa","name":"Exa","authType":"api-key","bgColor":"#1F40ED","integrationType":"search"},{"type":"extend_v2","slug":"extend","name":"Extend","authType":"api-key","bgColor":"#000000","integrationType":"ai"},{"type":"fathom","slug":"fathom","name":"Fathom","authType":"api-key","bgColor":"#181C1E","integrationType":"analytics"},{"type":"findymail","slug":"findymail","name":"Findymail","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"firecrawl","slug":"firecrawl","name":"Firecrawl","authType":"api-key","bgColor":"#181C1E","integrationType":"search"},{"type":"fireflies_v2","slug":"fireflies","name":"Fireflies","authType":"api-key","bgColor":"#100730","integrationType":"productivity"},{"type":"flint","slug":"flint","name":"Flint","authType":"api-key","bgColor":"#F6F54F","integrationType":"marketing"},{"type":"gamma","slug":"gamma","name":"Gamma","authType":"api-key","bgColor":"#002253","integrationType":"marketing"},{"type":"github_v2","slug":"github","name":"GitHub","authType":"api-key","bgColor":"#181C1E","integrationType":"devops"},{"type":"gitlab","slug":"gitlab","name":"GitLab","authType":"api-key","bgColor":"#FFFFFF","integrationType":"devops"},{"type":"gmail_v2","slug":"gmail","name":"Gmail","authType":"oauth","oauthServiceId":"gmail","bgColor":"#FFFFFF","integrationType":"email"},{"type":"gong","slug":"gong","name":"Gong","authType":"api-key","bgColor":"#8039DF","integrationType":"sales"},{"type":"google_ads","slug":"google-ads","name":"Google Ads","authType":"oauth","oauthServiceId":"google-ads","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"google_appsheet","slug":"google-appsheet","name":"Google AppSheet","authType":"api-key","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"google_bigquery","slug":"google-bigquery","name":"Google BigQuery","authType":"oauth","oauthServiceId":"google-bigquery","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"google_books","slug":"google-books","name":"Google Books","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"google_calendar_v2","slug":"google-calendar","name":"Google Calendar","authType":"oauth","oauthServiceId":"google-calendar","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"google_contacts","slug":"google-contacts","name":"Google Contacts","authType":"oauth","oauthServiceId":"google-contacts","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"google_docs","slug":"google-docs","name":"Google Docs","authType":"oauth","oauthServiceId":"google-docs","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"google_drive","slug":"google-drive","name":"Google Drive","authType":"oauth","oauthServiceId":"google-drive","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"google_forms","slug":"google-forms","name":"Google Forms","authType":"oauth","oauthServiceId":"google-forms","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"google_groups","slug":"google-groups","name":"Google Groups","authType":"oauth","oauthServiceId":"google-groups","bgColor":"#E8F0FE","integrationType":"communication"},{"type":"google_maps","slug":"google-maps","name":"Google Maps","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"google_meet","slug":"google-meet","name":"Google Meet","authType":"oauth","oauthServiceId":"google-meet","bgColor":"#FFFFFF","integrationType":"communication"},{"type":"google_pagespeed","slug":"google-pagespeed","name":"Google PageSpeed","authType":"api-key","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"google_search","slug":"google-search","name":"Google Search","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"google_sheets_v2","slug":"google-sheets","name":"Google Sheets","authType":"oauth","oauthServiceId":"google-sheets","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"google_slides_v2","slug":"google-slides","name":"Google Slides","authType":"oauth","oauthServiceId":"google-drive","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"google_tasks","slug":"google-tasks","name":"Google Tasks","authType":"oauth","oauthServiceId":"google-tasks","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"google_translate","slug":"google-translate","name":"Google Translate","authType":"api-key","bgColor":"#FFFFFF","integrationType":"ai"},{"type":"google_vault","slug":"google-vault","name":"Google Vault","authType":"oauth","oauthServiceId":"google-vault","bgColor":"#E8F0FE","integrationType":"security"},{"type":"grafana","slug":"grafana","name":"Grafana","authType":"api-key","bgColor":"#FFFFFF","integrationType":"observability"},{"type":"grain_v2","slug":"grain","name":"Grain","authType":"api-key","bgColor":"#F6FAF9","integrationType":"productivity"},{"type":"granola","slug":"granola","name":"Granola","authType":"api-key","bgColor":"#B2C147","integrationType":"productivity"},{"type":"greenhouse","slug":"greenhouse","name":"Greenhouse","authType":"api-key","bgColor":"#469776","integrationType":"hr"},{"type":"greptile","slug":"greptile","name":"Greptile","authType":"api-key","bgColor":"#181C1E","integrationType":"devops"},{"type":"harmonic","slug":"harmonic","name":"Harmonic","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"hex","slug":"hex","name":"Hex","authType":"api-key","bgColor":"#14151A","integrationType":"analytics"},{"type":"hubspot","slug":"hubspot","name":"HubSpot","authType":"oauth","oauthServiceId":"hubspot","bgColor":"#FF7A59","integrationType":"sales"},{"type":"huggingface","slug":"hugging-face","name":"Hugging Face","authType":"api-key","bgColor":"#0B0F19","integrationType":"ai"},{"type":"hunter","slug":"hunter-io","name":"Hunter.io","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"icypeas","slug":"icypeas","name":"Icypeas","authType":"api-key","bgColor":"#d4d4d4","integrationType":"sales"},{"type":"incidentio","slug":"incident-io","name":"incident.io","authType":"api-key","bgColor":"#FFFFFF","integrationType":"observability"},{"type":"infisical","slug":"infisical","name":"Infisical","authType":"api-key","bgColor":"#F7FE62","integrationType":"security"},{"type":"instantly","slug":"instantly","name":"Instantly","authType":"api-key","bgColor":"#FFFFFF","integrationType":"email"},{"type":"intercom_v2","slug":"intercom","name":"Intercom","authType":"api-key","bgColor":"#FFFFFF","integrationType":"support"},{"type":"jina","slug":"jina","name":"Jina","authType":"api-key","bgColor":"#333333","integrationType":"search"},{"type":"jira","slug":"jira","name":"Jira","authType":"oauth","oauthServiceId":"jira","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"jira_service_management","slug":"jira-service-management","name":"Jira Service Management","authType":"oauth","oauthServiceId":"jira","bgColor":"#FFFFFF","integrationType":"support"},{"type":"jotform","slug":"jotform","name":"Jotform","authType":"api-key","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"jupyter_v2","slug":"jupyter","name":"Jupyter","authType":"api-key","bgColor":"#FFFFFF","integrationType":"devops"},{"type":"kalshi_v2","slug":"kalshi","name":"Kalshi","authType":"api-key","bgColor":"#09C285","integrationType":"analytics"},{"type":"ketch","slug":"ketch","name":"Ketch","authType":"none","bgColor":"#9B5CFF","integrationType":"security"},{"type":"lambda","slug":"lambda","name":"Lambda","authType":"api-key","bgColor":"linear-gradient(45deg, #C8511B 0%, #FF9900 100%)","integrationType":"devops"},{"type":"langsmith","slug":"langsmith","name":"LangSmith","authType":"api-key","bgColor":"#181C1E","integrationType":"observability"},{"type":"latex","slug":"latex","name":"LaTeX","authType":"none","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"launchdarkly","slug":"launchdarkly","name":"LaunchDarkly","authType":"api-key","bgColor":"#191919","integrationType":"devops"},{"type":"leadmagic","slug":"leadmagic","name":"LeadMagic","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"lemlist","slug":"lemlist","name":"Lemlist","authType":"api-key","bgColor":"#316BFF","integrationType":"email"},{"type":"linear_v2","slug":"linear","name":"Linear","authType":"oauth","oauthServiceId":"linear","bgColor":"#5E6AD2","integrationType":"productivity"},{"type":"linkedin","slug":"linkedin","name":"LinkedIn","authType":"oauth","oauthServiceId":"linkedin","bgColor":"#0072B1","integrationType":"sales"},{"type":"linkup","slug":"linkup","name":"Linkup","authType":"api-key","bgColor":"#D6D3C7","integrationType":"search"},{"type":"linq","slug":"linq","name":"Linq","authType":"api-key","bgColor":"#000000","integrationType":"communication"},{"type":"logfire","slug":"logfire","name":"Logfire","authType":"api-key","bgColor":"#000000","integrationType":"observability"},{"type":"logrocket","slug":"logrocket","name":"LogRocket","authType":"api-key","bgColor":"#764ABC","integrationType":"observability"},{"type":"loops","slug":"loops","name":"Loops","authType":"api-key","bgColor":"#FAFAF9","integrationType":"email"},{"type":"luma","slug":"luma","name":"Luma","authType":"api-key","bgColor":"#000000","integrationType":"productivity"},{"type":"mailchimp","slug":"mailchimp","name":"Mailchimp","authType":"api-key","bgColor":"#FFE01B","integrationType":"email"},{"type":"mailgun","slug":"mailgun","name":"Mailgun","authType":"api-key","bgColor":"#C12126","integrationType":"email"},{"type":"manageengine_sdp","slug":"manageengine-servicedesk-plus","name":"ManageEngine ServiceDesk Plus","authType":"oauth","oauthServiceId":"manageengine-sdp","bgColor":"#FFFFFF","integrationType":"support"},{"type":"mem0","slug":"mem0","name":"Mem0","authType":"api-key","bgColor":"#181C1E","integrationType":"ai"},{"type":"microsoft_dataverse_v2","slug":"microsoft-dataverse","name":"Microsoft Dataverse","authType":"oauth","oauthServiceId":"microsoft-dataverse","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"microsoft_dynamics_365","slug":"microsoft-dynamics-365-crm","name":"Microsoft Dynamics 365 CRM","authType":"oauth","oauthServiceId":"microsoft-dataverse","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"microsoft_excel_v2","slug":"microsoft-excel","name":"Microsoft Excel","authType":"oauth","oauthServiceId":"microsoft-excel","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"microsoft_planner","slug":"microsoft-planner","name":"Microsoft Planner","authType":"oauth","oauthServiceId":"microsoft-planner","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"mssql","slug":"microsoft-sql-server","name":"Microsoft SQL Server","authType":"none","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"microsoft_teams","slug":"microsoft-teams","name":"Microsoft Teams","authType":"oauth","oauthServiceId":"microsoft-teams","bgColor":"#FFFFFF","integrationType":"communication"},{"type":"microsoft_word","slug":"microsoft-word","name":"Microsoft Word","authType":"oauth","oauthServiceId":"microsoft-word","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"millionverifier","slug":"millionverifier","name":"MillionVerifier","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"mintlify","slug":"mintlify","name":"Mintlify","authType":"api-key","bgColor":"#000000","integrationType":"documents"},{"type":"mistral_parse_v3","slug":"mistral-parser","name":"Mistral Parser","authType":"api-key","bgColor":"#000000","integrationType":"ai"},{"type":"modal","slug":"modal","name":"Modal","authType":"api-key","bgColor":"#000000","integrationType":"ai"},{"type":"monday","slug":"monday","name":"Monday","authType":"oauth","oauthServiceId":"monday","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"mongodb","slug":"mongodb","name":"MongoDB","authType":"none","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"mysql","slug":"mysql","name":"MySQL","authType":"none","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"neo4j","slug":"neo4j","name":"Neo4j","authType":"none","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"neverbounce","slug":"neverbounce","name":"NeverBounce","authType":"api-key","bgColor":"#064AF4","integrationType":"sales"},{"type":"new_relic","slug":"new-relic","name":"New Relic","authType":"api-key","bgColor":"#000000","integrationType":"observability"},{"type":"notion_v2","slug":"notion","name":"Notion","authType":"oauth","oauthServiceId":"notion","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"obsidian","slug":"obsidian","name":"Obsidian","authType":"api-key","bgColor":"#0F0F0F","integrationType":"documents"},{"type":"okta","slug":"okta","name":"Okta","authType":"api-key","bgColor":"#191919","integrationType":"security"},{"type":"onedrive","slug":"onedrive","name":"OneDrive","authType":"oauth","oauthServiceId":"onedrive","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"netsuite","slug":"oracle-netsuite","name":"Oracle NetSuite","authType":"api-key","bgColor":"#FFFFFF","integrationType":"commerce"},{"type":"otter","slug":"otter-ai","name":"Otter.ai","authType":"api-key","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"outlook","slug":"outlook","name":"Outlook","authType":"oauth","oauthServiceId":"outlook","bgColor":"#FFFFFF","integrationType":"email"},{"type":"pagerduty","slug":"pagerduty","name":"PagerDuty","authType":"api-key","bgColor":"#06AC38","integrationType":"observability"},{"type":"parallel_ai","slug":"parallel-ai","name":"Parallel AI","authType":"api-key","bgColor":"#1D1C1A","integrationType":"search"},{"type":"peopledatalabs","slug":"people-data-labs","name":"People Data Labs","authType":"api-key","bgColor":"#4831C3","integrationType":"sales"},{"type":"perplexity","slug":"perplexity","name":"Perplexity","authType":"api-key","bgColor":"#20808D","integrationType":"ai"},{"type":"persona","slug":"persona","name":"Persona","authType":"api-key","bgColor":"#FFFFFF","integrationType":"security"},{"type":"pinecone","slug":"pinecone","name":"Pinecone","authType":"api-key","bgColor":"#0D1117","integrationType":"databases"},{"type":"pipedrive","slug":"pipedrive","name":"Pipedrive","authType":"oauth","oauthServiceId":"pipedrive","bgColor":"#2E6936","integrationType":"sales"},{"type":"pitchbook","slug":"pitchbook","name":"PitchBook","authType":"api-key","bgColor":"#1D5080","integrationType":"analytics"},{"type":"planetscale","slug":"planetscale","name":"PlanetScale","authType":"api-key","bgColor":"#111111","integrationType":"databases"},{"type":"polymarket","slug":"polymarket","name":"Polymarket","authType":"none","bgColor":"#4C82FB","integrationType":"analytics"},{"type":"postgresql","slug":"postgresql","name":"PostgreSQL","authType":"none","bgColor":"#336791","integrationType":"databases"},{"type":"posthog","slug":"posthog","name":"PostHog","authType":"api-key","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"powerbi","slug":"power-bi","name":"Power BI","authType":"oauth","oauthServiceId":"microsoft-powerbi","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"profound","slug":"profound","name":"Profound","authType":"api-key","bgColor":"#000000","integrationType":"analytics"},{"type":"prospeo","slug":"prospeo","name":"Prospeo","authType":"api-key","bgColor":"#FF1A26","integrationType":"sales"},{"type":"pulse_v2","slug":"pulse","name":"Pulse","authType":"api-key","bgColor":"#FFFFFF","integrationType":"ai"},{"type":"qdrant","slug":"qdrant","name":"Qdrant","authType":"api-key","bgColor":"#1A223F","integrationType":"databases"},{"type":"quartr","slug":"quartr","name":"Quartr","authType":"api-key","bgColor":"#000000","integrationType":"analytics"},{"type":"quickbooks","slug":"quickbooks","name":"QuickBooks","authType":"oauth","oauthServiceId":"quickbooks","bgColor":"#2CA01C","integrationType":"commerce"},{"type":"quiver_v2","slug":"quiver","name":"Quiver","authType":"api-key","bgColor":"#FFFFFF","integrationType":"ai"},{"type":"rabbitmq","slug":"rabbitmq","name":"RabbitMQ","authType":"api-key","bgColor":"#FFFFFF","integrationType":"devops"},{"type":"railway","slug":"railway","name":"Railway","authType":"api-key","bgColor":"#000000","integrationType":"devops"},{"type":"rb2b","slug":"rb2b","name":"RB2B","authType":"api-key","bgColor":"#51FF00","integrationType":"sales"},{"type":"reddit","slug":"reddit","name":"Reddit","authType":"oauth","oauthServiceId":"reddit","bgColor":"#FF5700","integrationType":"communication"},{"type":"redis","slug":"redis","name":"Redis","authType":"api-key","bgColor":"#FF4438","integrationType":"databases"},{"type":"reducto_v2","slug":"reducto","name":"Reducto","authType":"api-key","bgColor":"#5c0c5c","integrationType":"ai"},{"type":"resend","slug":"resend","name":"Resend","authType":"api-key","bgColor":"#181C1E","integrationType":"email"},{"type":"revenuecat","slug":"revenuecat","name":"RevenueCat","authType":"api-key","bgColor":"#F25A5A","integrationType":"commerce"},{"type":"rippling","slug":"rippling","name":"Rippling","authType":"api-key","bgColor":"#502D3C","integrationType":"hr"},{"type":"rocketlane","slug":"rocketlane","name":"Rocketlane","authType":"api-key","bgColor":"#000000","integrationType":"productivity"},{"type":"rootly","slug":"rootly","name":"Rootly","authType":"api-key","bgColor":"#6C72C8","integrationType":"observability"},{"type":"s3","slug":"s3","name":"S3","authType":"api-key","bgColor":"linear-gradient(45deg, #1B660F 0%, #6CAE3E 100%)","integrationType":"documents"},{"type":"sailpoint","slug":"sailpoint","name":"SailPoint","authType":"api-key","bgColor":"#FFFFFF","integrationType":"security"},{"type":"salesforce","slug":"salesforce","name":"Salesforce","authType":"oauth","oauthServiceId":"salesforce","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"sap_concur","slug":"sap-concur","name":"SAP Concur","authType":"api-key","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"sap_s4hana","slug":"sap-s4hana","name":"SAP S4HANA","authType":"api-key","bgColor":"#FFFFFF","integrationType":"hr"},{"type":"semrush","slug":"semrush","name":"Semrush","authType":"api-key","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"sendblue","slug":"sendblue","name":"Sendblue","authType":"api-key","bgColor":"#008BFF","integrationType":"communication"},{"type":"sendgrid","slug":"sendgrid","name":"SendGrid","authType":"api-key","bgColor":"#1A82E2","integrationType":"email"},{"type":"sentry","slug":"sentry","name":"Sentry","authType":"api-key","bgColor":"#362D59","integrationType":"observability"},{"type":"serper","slug":"serper","name":"Serper","authType":"api-key","bgColor":"#2B3543","integrationType":"search"},{"type":"servicenow_v2","slug":"servicenow","name":"ServiceNow","authType":"api-key","bgColor":"#032D42","integrationType":"support"},{"type":"sftp_v2","slug":"sftp","name":"SFTP","authType":"api-key","bgColor":"#2D3748","integrationType":"documents"},{"type":"sharepoint_v2","slug":"sharepoint","name":"SharePoint","authType":"oauth","oauthServiceId":"sharepoint","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"shopify","slug":"shopify","name":"Shopify","authType":"oauth","oauthServiceId":"shopify","bgColor":"#FFFFFF","integrationType":"commerce"},{"type":"similarweb","slug":"similarweb","name":"Similarweb","authType":"api-key","bgColor":"#000922","integrationType":"analytics"},{"type":"sixtyfour","slug":"sixtyfour-ai","name":"Sixtyfour AI","authType":"api-key","bgColor":"#000000","integrationType":"sales"},{"type":"slack_v2","slug":"slack","name":"Slack","authType":"oauth","oauthServiceId":"slack","bgColor":"#611f69","integrationType":"communication"},{"type":"smartlead","slug":"smartlead","name":"Smartlead","authType":"api-key","bgColor":"#000000","integrationType":"email"},{"type":"smtp","slug":"smtp","name":"SMTP","authType":"api-key","bgColor":"#2D3748","integrationType":"email"},{"type":"snowflake","slug":"snowflake","name":"Snowflake","authType":"api-key","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"splunk","slug":"splunk","name":"Splunk","authType":"api-key","bgColor":"#FFFFFF","integrationType":"observability"},{"type":"sportmonks","slug":"sportmonks","name":"Sportmonks","authType":"api-key","bgColor":"#171534","integrationType":"analytics"},{"type":"square","slug":"square","name":"Square","authType":"api-key","bgColor":"#000000","integrationType":"commerce"},{"type":"ssh_v2","slug":"ssh","name":"SSH","authType":"api-key","bgColor":"#000000","integrationType":"devops"},{"type":"stagehand","slug":"stagehand","name":"Stagehand","authType":"api-key","bgColor":"#FFC83C","integrationType":"ai"},{"type":"stripe","slug":"stripe","name":"Stripe","authType":"api-key","bgColor":"#635BFF","integrationType":"commerce"},{"type":"supabase","slug":"supabase","name":"Supabase","authType":"api-key","bgColor":"#1C1C1C","integrationType":"databases"},{"type":"tailscale","slug":"tailscale","name":"Tailscale","authType":"api-key","bgColor":"#2E2D2D","integrationType":"security"},{"type":"tavily","slug":"tavily","name":"Tavily","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"telegram","slug":"telegram","name":"Telegram","authType":"api-key","bgColor":"#FFFFFF","integrationType":"communication"},{"type":"temporal","slug":"temporal","name":"Temporal","authType":"api-key","bgColor":"#141414","integrationType":"devops"},{"type":"thrive","slug":"thrive","name":"Thrive","authType":"api-key","bgColor":"#FFFFFF","integrationType":"hr"},{"type":"tiktok","slug":"tiktok","name":"TikTok","authType":"oauth","oauthServiceId":"tiktok","bgColor":"#000000","integrationType":"communication"},{"type":"tinybird","slug":"tinybird","name":"Tinybird","authType":"api-key","bgColor":"#2EF598","integrationType":"analytics"},{"type":"tinyfish","slug":"tinyfish","name":"TinyFish","authType":"api-key","bgColor":"#FFFFFF","integrationType":"ai"},{"type":"trello","slug":"trello","name":"Trello","authType":"oauth","oauthServiceId":"trello","bgColor":"#0052CC","integrationType":"productivity"},{"type":"trigger_dev","slug":"trigger-dev","name":"Trigger.dev","authType":"api-key","bgColor":"#000000","integrationType":"devops"},{"type":"twilio_sms","slug":"twilio-sms","name":"Twilio SMS","authType":"api-key","bgColor":"#F22F46","integrationType":"communication"},{"type":"twilio_voice","slug":"twilio-voice","name":"Twilio Voice","authType":"api-key","bgColor":"#F22F46","integrationType":"communication"},{"type":"typeform","slug":"typeform","name":"Typeform","authType":"api-key","bgColor":"#262627","integrationType":"documents"},{"type":"upstash","slug":"upstash","name":"Upstash","authType":"api-key","bgColor":"#181C1E","integrationType":"databases"},{"type":"uptimerobot","slug":"uptimerobot","name":"UptimeRobot","authType":"api-key","bgColor":"#111921","integrationType":"observability"},{"type":"vanta","slug":"vanta","name":"Vanta","authType":"api-key","bgColor":"#F8F4F3","integrationType":"security"},{"type":"vercel","slug":"vercel","name":"Vercel","authType":"api-key","bgColor":"#171717","integrationType":"devops"},{"type":"wealthbox","slug":"wealthbox","name":"Wealthbox","authType":"oauth","oauthServiceId":"wealthbox","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"webflow","slug":"webflow","name":"Webflow","authType":"oauth","oauthServiceId":"webflow","bgColor":"#FFFFFF","integrationType":"marketing"},{"type":"whatsapp","slug":"whatsapp","name":"WhatsApp","authType":"api-key","bgColor":"#25D366","integrationType":"communication"},{"type":"wikipedia","slug":"wikipedia","name":"Wikipedia","authType":"none","bgColor":"#000000","integrationType":"search"},{"type":"windchill","slug":"windchill","name":"Windchill","authType":"api-key","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"wiza","slug":"wiza","name":"Wiza","authType":"api-key","bgColor":"#9284BC","integrationType":"sales"},{"type":"wordpress","slug":"wordpress","name":"WordPress","authType":"oauth","oauthServiceId":"wordpress","bgColor":"#21759B","integrationType":"marketing"},{"type":"workday","slug":"workday","name":"Workday","authType":"none","bgColor":"#F5F0EB","integrationType":"hr"},{"type":"x","slug":"x","name":"X","authType":"oauth","oauthServiceId":"x","bgColor":"#000000","integrationType":"communication"},{"type":"youcom","slug":"you-com","name":"You.com","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"youtube","slug":"youtube","name":"YouTube","authType":"api-key","bgColor":"#FF0000","integrationType":"communication"},{"type":"zendesk","slug":"zendesk","name":"Zendesk","authType":"api-key","bgColor":"#03363D","integrationType":"support"},{"type":"zep","slug":"zep","name":"Zep","authType":"api-key","bgColor":"#E8E8E8","integrationType":"ai"},{"type":"zerobounce","slug":"zerobounce","name":"ZeroBounce","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"zoho_desk","slug":"zoho-desk","name":"Zoho Desk","authType":"oauth","oauthServiceId":"zoho-desk","bgColor":"#FFFFFF","integrationType":"support"},{"type":"zoom","slug":"zoom","name":"Zoom","authType":"oauth","oauthServiceId":"zoom","bgColor":"#2D8CFF","integrationType":"communication"},{"type":"zoominfo","slug":"zoominfo","name":"ZoomInfo","authType":"api-key","bgColor":"#EA1B15","integrationType":"sales"}]'
+ '[{"type":"onepassword","slug":"1password","name":"1Password","authType":"api-key","bgColor":"#FFFFFF","integrationType":"security"},{"type":"affinity","slug":"affinity","name":"Affinity","authType":"api-key","bgColor":"#1B1C1E","integrationType":"sales"},{"type":"agentmail","slug":"agentmail","name":"AgentMail","authType":"api-key","bgColor":"#000000","integrationType":"email"},{"type":"agentphone","slug":"agentphone","name":"AgentPhone","authType":"api-key","bgColor":"linear-gradient(135deg, #1a1a1a 0%, #0a2a14 100%)","integrationType":"communication"},{"type":"agiloft","slug":"agiloft","name":"Agiloft","authType":"api-key","bgColor":"#001028","integrationType":"productivity"},{"type":"ahrefs","slug":"ahrefs","name":"Ahrefs","authType":"api-key","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"airtable","slug":"airtable","name":"Airtable","authType":"oauth","oauthServiceId":"airtable","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"airweave","slug":"airweave","name":"Airweave","authType":"api-key","bgColor":"#6366F1","integrationType":"search"},{"type":"algolia","slug":"algolia","name":"Algolia","authType":"api-key","bgColor":"#003DFF","integrationType":"search"},{"type":"dynamodb","slug":"amazon-dynamodb","name":"Amazon DynamoDB","authType":"api-key","bgColor":"linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)","integrationType":"databases"},{"type":"rds","slug":"amazon-rds","name":"Amazon RDS","authType":"none","bgColor":"linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)","integrationType":"databases"},{"type":"sqs","slug":"amazon-sqs","name":"Amazon SQS","authType":"api-key","bgColor":"linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)","integrationType":"devops"},{"type":"amplitude","slug":"amplitude","name":"Amplitude","authType":"api-key","bgColor":"#13294B","integrationType":"analytics"},{"type":"apify","slug":"apify","name":"Apify","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"apollo","slug":"apollo","name":"Apollo","authType":"api-key","bgColor":"#EBF212","integrationType":"sales"},{"type":"arxiv","slug":"arxiv","name":"ArXiv","authType":"none","bgColor":"#FFFFFF","integrationType":"search"},{"type":"asana","slug":"asana","name":"Asana","authType":"oauth","oauthServiceId":"asana","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"ashby","slug":"ashby","name":"Ashby","authType":"api-key","bgColor":"#5D4ED6","integrationType":"hr"},{"type":"athena","slug":"athena","name":"Athena","authType":"none","bgColor":"linear-gradient(45deg, #4D27A8 0%, #A166FF 100%)","integrationType":"analytics"},{"type":"attio","slug":"attio","name":"Attio","authType":"oauth","oauthServiceId":"attio","bgColor":"#1D1E20","integrationType":"sales"},{"type":"appconfig","slug":"aws-appconfig","name":"AWS AppConfig","authType":"api-key","bgColor":"linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)","integrationType":"devops"},{"type":"iam","slug":"aws-iam","name":"AWS IAM","authType":"api-key","bgColor":"linear-gradient(45deg, #BD0816 0%, #FF5252 100%)","integrationType":"security"},{"type":"identity_center","slug":"aws-identity-center","name":"AWS Identity Center","authType":"api-key","bgColor":"linear-gradient(45deg, #BD0816 0%, #FF5252 100%)","integrationType":"security"},{"type":"secrets_manager","slug":"aws-secrets-manager","name":"AWS Secrets Manager","authType":"none","bgColor":"linear-gradient(45deg, #BD0816 0%, #FF5252 100%)","integrationType":"security"},{"type":"ses","slug":"aws-ses","name":"AWS SES","authType":"api-key","bgColor":"linear-gradient(45deg, #BD0816 0%, #FF5252 100%)","integrationType":"email"},{"type":"sts","slug":"aws-sts","name":"AWS STS","authType":"api-key","bgColor":"linear-gradient(45deg, #BD0816 0%, #FF5252 100%)","integrationType":"security"},{"type":"ssm","slug":"aws-systems-manager","name":"AWS Systems Manager","authType":"api-key","bgColor":"#E7157B","integrationType":"devops"},{"type":"textract_v2","slug":"aws-textract","name":"AWS Textract","authType":"api-key","bgColor":"linear-gradient(135deg, #055F4E 0%, #56C0A7 100%)","integrationType":"ai"},{"type":"microsoft_ad","slug":"azure-ad","name":"Azure AD","authType":"oauth","oauthServiceId":"microsoft-ad","bgColor":"#0078D4","integrationType":"security"},{"type":"azure_data_explorer","slug":"azure-data-explorer","name":"Azure Data Explorer","authType":"api-key","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"azure_devops","slug":"azure-devops","name":"Azure DevOps","authType":"api-key","bgColor":"#0078D4","integrationType":"devops"},{"type":"bitbucket","slug":"bitbucket","name":"Bitbucket","authType":"oauth","oauthServiceId":"bitbucket","bgColor":"#FFFFFF","integrationType":"devops"},{"type":"box_v2","slug":"box","name":"Box","authType":"oauth","oauthServiceId":"box","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"brandfetch","slug":"brandfetch","name":"Brandfetch","authType":"api-key","bgColor":"#000000","integrationType":"sales"},{"type":"brex","slug":"brex","name":"Brex","authType":"api-key","bgColor":"#171717","integrationType":"commerce"},{"type":"brightdata","slug":"bright-data","name":"Bright Data","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"browser_use","slug":"browser-use","name":"Browser Use","authType":"api-key","bgColor":"#181C1E","integrationType":"ai"},{"type":"buffer","slug":"buffer","name":"Buffer","authType":"api-key","bgColor":"#FFFFFF","integrationType":"marketing"},{"type":"calcom","slug":"cal-com","name":"Cal.com","authType":"oauth","oauthServiceId":"calcom","bgColor":"#292929","integrationType":"productivity"},{"type":"calendly","slug":"calendly","name":"Calendly","authType":"api-key","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"cbinsights","slug":"cb-insights","name":"CB Insights","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"circleback","slug":"circleback","name":"Circleback","authType":"api-key","bgColor":"linear-gradient(180deg, #E0F7FA 0%, #FFFFFF 100%)","integrationType":"ai"},{"type":"managed_agent","slug":"claude-managed-agents","name":"Claude Managed Agents","authType":"api-key","serviceAccountServiceId":"claude-platform","bgColor":"#DA7756","integrationType":"ai"},{"type":"clay","slug":"clay","name":"Clay","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"clerk","slug":"clerk","name":"Clerk","authType":"none","bgColor":"#131316","integrationType":"security"},{"type":"clickhouse","slug":"clickhouse","name":"ClickHouse","authType":"none","bgColor":"#f9ff69","integrationType":"databases"},{"type":"clickup","slug":"clickup","name":"ClickUp","authType":"oauth","oauthServiceId":"clickup","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"cloudflare","slug":"cloudflare","name":"Cloudflare","authType":"api-key","bgColor":"#F5F6FA","integrationType":"devops"},{"type":"cloudformation","slug":"cloudformation","name":"CloudFormation","authType":"none","bgColor":"linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)","integrationType":"devops"},{"type":"cloudtrail","slug":"cloudtrail","name":"CloudTrail","authType":"api-key","bgColor":"linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)","integrationType":"security"},{"type":"cloudwatch","slug":"cloudwatch","name":"CloudWatch","authType":"none","bgColor":"linear-gradient(45deg, #B0084D 0%, #FF4F8B 100%)","integrationType":"observability"},{"type":"coda","slug":"coda","name":"Coda","authType":"api-key","serviceAccountServiceId":"coda","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"codepipeline","slug":"codepipeline","name":"CodePipeline","authType":"api-key","bgColor":"linear-gradient(45deg, #2E27AD 0%, #527FFF 100%)","integrationType":"devops"},{"type":"confluence_v2","slug":"confluence","name":"Confluence","authType":"oauth","oauthServiceId":"confluence","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"context_dev","slug":"context-dev","name":"Context.dev","authType":"api-key","bgColor":"#ffffff","integrationType":"search"},{"type":"convex","slug":"convex","name":"Convex","authType":"api-key","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"crowdstrike","slug":"crowdstrike","name":"CrowdStrike","authType":"api-key","bgColor":"#E01F3D","integrationType":"security"},{"type":"crunchbase","slug":"crunchbase","name":"Crunchbase","authType":"api-key","bgColor":"#0287D1","integrationType":"sales"},{"type":"cursor_v2","slug":"cursor","name":"Cursor","authType":"api-key","bgColor":"#1E1E1E","integrationType":"devops"},{"type":"dagster","slug":"dagster","name":"Dagster","authType":"api-key","bgColor":"#ffffff","integrationType":"observability"},{"type":"databricks","slug":"databricks","name":"Databricks","authType":"api-key","bgColor":"#F9F7F4","integrationType":"databases"},{"type":"datadog","slug":"datadog","name":"Datadog","authType":"api-key","bgColor":"#632CA6","integrationType":"observability"},{"type":"datagma","slug":"datagma","name":"Datagma","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"daytona","slug":"daytona","name":"Daytona","authType":"api-key","bgColor":"#FFFFFF","integrationType":"devops"},{"type":"devin","slug":"devin","name":"Devin","authType":"api-key","bgColor":"#12141A","integrationType":"devops"},{"type":"discord","slug":"discord","name":"Discord","authType":"api-key","bgColor":"#5865F2","integrationType":"communication"},{"type":"docusign","slug":"docusign","name":"DocuSign","authType":"oauth","oauthServiceId":"docusign","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"downdetector","slug":"downdetector","name":"Downdetector","authType":"api-key","bgColor":"#FFFFFF","integrationType":"observability"},{"type":"dropbox_v2","slug":"dropbox","name":"Dropbox","authType":"oauth","oauthServiceId":"dropbox","bgColor":"#0061FF","integrationType":"documents"},{"type":"dropcontact","slug":"dropcontact","name":"Dropcontact","authType":"api-key","bgColor":"#0ABA9F","integrationType":"sales"},{"type":"dspy","slug":"dspy","name":"DSPy","authType":"api-key","bgColor":"#FFFFFF","integrationType":"ai"},{"type":"dub_v2","slug":"dub","name":"Dub","authType":"api-key","bgColor":"#181C1E","integrationType":"devops"},{"type":"duckduckgo","slug":"duckduckgo","name":"DuckDuckGo","authType":"none","bgColor":"#FFFFFF","integrationType":"search"},{"type":"dynatrace","slug":"dynatrace","name":"Dynatrace","authType":"api-key","bgColor":"#FFFFFF","integrationType":"observability"},{"type":"elasticsearch","slug":"elasticsearch","name":"Elasticsearch","authType":"api-key","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"elevenlabs","slug":"elevenlabs","name":"ElevenLabs","authType":"api-key","bgColor":"#181C1E","integrationType":"ai"},{"type":"emailbison","slug":"email-bison","name":"Email Bison","authType":"api-key","bgColor":"#FB7A22","integrationType":"email"},{"type":"embeddings","slug":"embeddings","name":"Embeddings","authType":"api-key","bgColor":"#7B4DFF","integrationType":"ai"},{"type":"enrich","slug":"enrich","name":"Enrich","authType":"api-key","bgColor":"#E5E5E6","integrationType":"sales"},{"type":"enrow","slug":"enrow","name":"Enrow","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"exa","slug":"exa","name":"Exa","authType":"api-key","bgColor":"#1F40ED","integrationType":"search"},{"type":"extend_v2","slug":"extend","name":"Extend","authType":"api-key","bgColor":"#000000","integrationType":"ai"},{"type":"fathom","slug":"fathom","name":"Fathom","authType":"api-key","bgColor":"#181C1E","integrationType":"analytics"},{"type":"findymail","slug":"findymail","name":"Findymail","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"firecrawl","slug":"firecrawl","name":"Firecrawl","authType":"api-key","bgColor":"#181C1E","integrationType":"search"},{"type":"fireflies_v2","slug":"fireflies","name":"Fireflies","authType":"api-key","bgColor":"#100730","integrationType":"productivity"},{"type":"flint","slug":"flint","name":"Flint","authType":"api-key","bgColor":"#F6F54F","integrationType":"marketing"},{"type":"gamma","slug":"gamma","name":"Gamma","authType":"api-key","bgColor":"#002253","integrationType":"marketing"},{"type":"github_v2","slug":"github","name":"GitHub","authType":"api-key","bgColor":"#181C1E","integrationType":"devops"},{"type":"gitlab","slug":"gitlab","name":"GitLab","authType":"api-key","bgColor":"#FFFFFF","integrationType":"devops"},{"type":"gmail_v2","slug":"gmail","name":"Gmail","authType":"oauth","oauthServiceId":"gmail","bgColor":"#FFFFFF","integrationType":"email"},{"type":"gong","slug":"gong","name":"Gong","authType":"api-key","bgColor":"#8039DF","integrationType":"sales"},{"type":"google_ads","slug":"google-ads","name":"Google Ads","authType":"oauth","oauthServiceId":"google-ads","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"google_appsheet","slug":"google-appsheet","name":"Google AppSheet","authType":"api-key","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"google_bigquery","slug":"google-bigquery","name":"Google BigQuery","authType":"oauth","oauthServiceId":"google-bigquery","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"google_books","slug":"google-books","name":"Google Books","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"google_calendar_v2","slug":"google-calendar","name":"Google Calendar","authType":"oauth","oauthServiceId":"google-calendar","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"google_contacts","slug":"google-contacts","name":"Google Contacts","authType":"oauth","oauthServiceId":"google-contacts","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"google_docs","slug":"google-docs","name":"Google Docs","authType":"oauth","oauthServiceId":"google-docs","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"google_drive","slug":"google-drive","name":"Google Drive","authType":"oauth","oauthServiceId":"google-drive","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"google_forms","slug":"google-forms","name":"Google Forms","authType":"oauth","oauthServiceId":"google-forms","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"google_groups","slug":"google-groups","name":"Google Groups","authType":"oauth","oauthServiceId":"google-groups","bgColor":"#E8F0FE","integrationType":"communication"},{"type":"google_maps","slug":"google-maps","name":"Google Maps","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"google_meet","slug":"google-meet","name":"Google Meet","authType":"oauth","oauthServiceId":"google-meet","bgColor":"#FFFFFF","integrationType":"communication"},{"type":"google_pagespeed","slug":"google-pagespeed","name":"Google PageSpeed","authType":"api-key","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"google_search","slug":"google-search","name":"Google Search","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"google_sheets_v2","slug":"google-sheets","name":"Google Sheets","authType":"oauth","oauthServiceId":"google-sheets","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"google_slides_v2","slug":"google-slides","name":"Google Slides","authType":"oauth","oauthServiceId":"google-drive","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"google_tasks","slug":"google-tasks","name":"Google Tasks","authType":"oauth","oauthServiceId":"google-tasks","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"google_translate","slug":"google-translate","name":"Google Translate","authType":"api-key","bgColor":"#FFFFFF","integrationType":"ai"},{"type":"google_vault","slug":"google-vault","name":"Google Vault","authType":"oauth","oauthServiceId":"google-vault","bgColor":"#E8F0FE","integrationType":"security"},{"type":"grafana","slug":"grafana","name":"Grafana","authType":"api-key","bgColor":"#FFFFFF","integrationType":"observability"},{"type":"grain_v2","slug":"grain","name":"Grain","authType":"api-key","bgColor":"#F6FAF9","integrationType":"productivity"},{"type":"granola","slug":"granola","name":"Granola","authType":"api-key","bgColor":"#B2C147","integrationType":"productivity"},{"type":"greenhouse","slug":"greenhouse","name":"Greenhouse","authType":"api-key","bgColor":"#469776","integrationType":"hr"},{"type":"greptile","slug":"greptile","name":"Greptile","authType":"api-key","bgColor":"#181C1E","integrationType":"devops"},{"type":"harmonic","slug":"harmonic","name":"Harmonic","authType":"api-key","serviceAccountServiceId":"harmonic","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"hex","slug":"hex","name":"Hex","authType":"api-key","bgColor":"#14151A","integrationType":"analytics"},{"type":"hubspot","slug":"hubspot","name":"HubSpot","authType":"oauth","oauthServiceId":"hubspot","bgColor":"#FF7A59","integrationType":"sales"},{"type":"huggingface","slug":"hugging-face","name":"Hugging Face","authType":"api-key","bgColor":"#0B0F19","integrationType":"ai"},{"type":"hunter","slug":"hunter-io","name":"Hunter.io","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"icypeas","slug":"icypeas","name":"Icypeas","authType":"api-key","bgColor":"#d4d4d4","integrationType":"sales"},{"type":"incidentio","slug":"incident-io","name":"incident.io","authType":"api-key","bgColor":"#FFFFFF","integrationType":"observability"},{"type":"infisical","slug":"infisical","name":"Infisical","authType":"api-key","bgColor":"#F7FE62","integrationType":"security"},{"type":"instantly","slug":"instantly","name":"Instantly","authType":"api-key","bgColor":"#FFFFFF","integrationType":"email"},{"type":"intercom_v2","slug":"intercom","name":"Intercom","authType":"api-key","bgColor":"#FFFFFF","integrationType":"support"},{"type":"jina","slug":"jina","name":"Jina","authType":"api-key","bgColor":"#333333","integrationType":"search"},{"type":"jira","slug":"jira","name":"Jira","authType":"oauth","oauthServiceId":"jira","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"jira_service_management","slug":"jira-service-management","name":"Jira Service Management","authType":"oauth","oauthServiceId":"jira","bgColor":"#FFFFFF","integrationType":"support"},{"type":"jotform","slug":"jotform","name":"Jotform","authType":"api-key","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"jupyter_v2","slug":"jupyter","name":"Jupyter","authType":"api-key","bgColor":"#FFFFFF","integrationType":"devops"},{"type":"kalshi_v2","slug":"kalshi","name":"Kalshi","authType":"api-key","bgColor":"#09C285","integrationType":"analytics"},{"type":"ketch","slug":"ketch","name":"Ketch","authType":"none","bgColor":"#9B5CFF","integrationType":"security"},{"type":"lambda","slug":"lambda","name":"Lambda","authType":"api-key","bgColor":"linear-gradient(45deg, #C8511B 0%, #FF9900 100%)","integrationType":"devops"},{"type":"langsmith","slug":"langsmith","name":"LangSmith","authType":"api-key","bgColor":"#181C1E","integrationType":"observability"},{"type":"latex","slug":"latex","name":"LaTeX","authType":"none","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"launchdarkly","slug":"launchdarkly","name":"LaunchDarkly","authType":"api-key","bgColor":"#191919","integrationType":"devops"},{"type":"leadmagic","slug":"leadmagic","name":"LeadMagic","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"lemlist","slug":"lemlist","name":"Lemlist","authType":"api-key","bgColor":"#316BFF","integrationType":"email"},{"type":"linear_v2","slug":"linear","name":"Linear","authType":"oauth","oauthServiceId":"linear","bgColor":"#5E6AD2","integrationType":"productivity"},{"type":"linkedin","slug":"linkedin","name":"LinkedIn","authType":"oauth","oauthServiceId":"linkedin","bgColor":"#0072B1","integrationType":"sales"},{"type":"linkup","slug":"linkup","name":"Linkup","authType":"api-key","bgColor":"#D6D3C7","integrationType":"search"},{"type":"linq","slug":"linq","name":"Linq","authType":"api-key","bgColor":"#000000","integrationType":"communication"},{"type":"logfire","slug":"logfire","name":"Logfire","authType":"api-key","bgColor":"#000000","integrationType":"observability"},{"type":"logrocket","slug":"logrocket","name":"LogRocket","authType":"api-key","bgColor":"#764ABC","integrationType":"observability"},{"type":"loops","slug":"loops","name":"Loops","authType":"api-key","bgColor":"#FAFAF9","integrationType":"email"},{"type":"luma","slug":"luma","name":"Luma","authType":"api-key","bgColor":"#000000","integrationType":"productivity"},{"type":"mailchimp","slug":"mailchimp","name":"Mailchimp","authType":"api-key","bgColor":"#FFE01B","integrationType":"email"},{"type":"mailgun","slug":"mailgun","name":"Mailgun","authType":"api-key","bgColor":"#C12126","integrationType":"email"},{"type":"manageengine_sdp","slug":"manageengine-servicedesk-plus","name":"ManageEngine ServiceDesk Plus","authType":"oauth","oauthServiceId":"manageengine-sdp","bgColor":"#FFFFFF","integrationType":"support"},{"type":"mem0","slug":"mem0","name":"Mem0","authType":"api-key","bgColor":"#181C1E","integrationType":"ai"},{"type":"microsoft_dataverse_v2","slug":"microsoft-dataverse","name":"Microsoft Dataverse","authType":"oauth","oauthServiceId":"microsoft-dataverse","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"microsoft_dynamics_365","slug":"microsoft-dynamics-365-crm","name":"Microsoft Dynamics 365 CRM","authType":"oauth","oauthServiceId":"microsoft-dataverse","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"microsoft_excel_v2","slug":"microsoft-excel","name":"Microsoft Excel","authType":"oauth","oauthServiceId":"microsoft-excel","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"microsoft_planner","slug":"microsoft-planner","name":"Microsoft Planner","authType":"oauth","oauthServiceId":"microsoft-planner","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"mssql","slug":"microsoft-sql-server","name":"Microsoft SQL Server","authType":"none","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"microsoft_teams","slug":"microsoft-teams","name":"Microsoft Teams","authType":"oauth","oauthServiceId":"microsoft-teams","bgColor":"#FFFFFF","integrationType":"communication"},{"type":"microsoft_word","slug":"microsoft-word","name":"Microsoft Word","authType":"oauth","oauthServiceId":"microsoft-word","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"millionverifier","slug":"millionverifier","name":"MillionVerifier","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"mintlify","slug":"mintlify","name":"Mintlify","authType":"api-key","bgColor":"#000000","integrationType":"documents"},{"type":"mistral_parse_v3","slug":"mistral-parser","name":"Mistral Parser","authType":"api-key","bgColor":"#000000","integrationType":"ai"},{"type":"modal","slug":"modal","name":"Modal","authType":"api-key","bgColor":"#000000","integrationType":"ai"},{"type":"monday","slug":"monday","name":"Monday","authType":"oauth","oauthServiceId":"monday","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"mongodb","slug":"mongodb","name":"MongoDB","authType":"none","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"mysql","slug":"mysql","name":"MySQL","authType":"none","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"neo4j","slug":"neo4j","name":"Neo4j","authType":"none","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"neverbounce","slug":"neverbounce","name":"NeverBounce","authType":"api-key","bgColor":"#064AF4","integrationType":"sales"},{"type":"new_relic","slug":"new-relic","name":"New Relic","authType":"api-key","bgColor":"#000000","integrationType":"observability"},{"type":"notion_v2","slug":"notion","name":"Notion","authType":"oauth","oauthServiceId":"notion","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"obsidian","slug":"obsidian","name":"Obsidian","authType":"api-key","bgColor":"#0F0F0F","integrationType":"documents"},{"type":"okta","slug":"okta","name":"Okta","authType":"api-key","bgColor":"#191919","integrationType":"security"},{"type":"onedrive","slug":"onedrive","name":"OneDrive","authType":"oauth","oauthServiceId":"onedrive","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"netsuite","slug":"oracle-netsuite","name":"Oracle NetSuite","authType":"api-key","serviceAccountServiceId":"netsuite","bgColor":"#FFFFFF","integrationType":"commerce"},{"type":"otter","slug":"otter-ai","name":"Otter.ai","authType":"api-key","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"outlook","slug":"outlook","name":"Outlook","authType":"oauth","oauthServiceId":"outlook","bgColor":"#FFFFFF","integrationType":"email"},{"type":"pagerduty","slug":"pagerduty","name":"PagerDuty","authType":"api-key","bgColor":"#06AC38","integrationType":"observability"},{"type":"parallel_ai","slug":"parallel-ai","name":"Parallel AI","authType":"api-key","bgColor":"#1D1C1A","integrationType":"search"},{"type":"peopledatalabs","slug":"people-data-labs","name":"People Data Labs","authType":"api-key","bgColor":"#4831C3","integrationType":"sales"},{"type":"perplexity","slug":"perplexity","name":"Perplexity","authType":"api-key","bgColor":"#20808D","integrationType":"ai"},{"type":"persona","slug":"persona","name":"Persona","authType":"api-key","bgColor":"#FFFFFF","integrationType":"security"},{"type":"pinecone","slug":"pinecone","name":"Pinecone","authType":"api-key","bgColor":"#0D1117","integrationType":"databases"},{"type":"pipedrive","slug":"pipedrive","name":"Pipedrive","authType":"oauth","oauthServiceId":"pipedrive","bgColor":"#2E6936","integrationType":"sales"},{"type":"pitchbook","slug":"pitchbook","name":"PitchBook","authType":"api-key","bgColor":"#1D5080","integrationType":"analytics"},{"type":"planetscale","slug":"planetscale","name":"PlanetScale","authType":"api-key","bgColor":"#111111","integrationType":"databases"},{"type":"polymarket","slug":"polymarket","name":"Polymarket","authType":"none","bgColor":"#4C82FB","integrationType":"analytics"},{"type":"postgresql","slug":"postgresql","name":"PostgreSQL","authType":"none","bgColor":"#336791","integrationType":"databases"},{"type":"posthog","slug":"posthog","name":"PostHog","authType":"api-key","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"powerbi","slug":"power-bi","name":"Power BI","authType":"oauth","oauthServiceId":"microsoft-powerbi","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"profound","slug":"profound","name":"Profound","authType":"api-key","bgColor":"#000000","integrationType":"analytics"},{"type":"prospeo","slug":"prospeo","name":"Prospeo","authType":"api-key","bgColor":"#FF1A26","integrationType":"sales"},{"type":"pulse_v2","slug":"pulse","name":"Pulse","authType":"api-key","bgColor":"#FFFFFF","integrationType":"ai"},{"type":"qdrant","slug":"qdrant","name":"Qdrant","authType":"api-key","bgColor":"#1A223F","integrationType":"databases"},{"type":"quartr","slug":"quartr","name":"Quartr","authType":"api-key","bgColor":"#000000","integrationType":"analytics"},{"type":"quickbooks","slug":"quickbooks","name":"QuickBooks","authType":"oauth","oauthServiceId":"quickbooks","bgColor":"#2CA01C","integrationType":"commerce"},{"type":"quiver_v2","slug":"quiver","name":"Quiver","authType":"api-key","bgColor":"#FFFFFF","integrationType":"ai"},{"type":"rabbitmq","slug":"rabbitmq","name":"RabbitMQ","authType":"api-key","bgColor":"#FFFFFF","integrationType":"devops"},{"type":"railway","slug":"railway","name":"Railway","authType":"api-key","bgColor":"#000000","integrationType":"devops"},{"type":"rb2b","slug":"rb2b","name":"RB2B","authType":"api-key","bgColor":"#51FF00","integrationType":"sales"},{"type":"reddit","slug":"reddit","name":"Reddit","authType":"oauth","oauthServiceId":"reddit","bgColor":"#FF5700","integrationType":"communication"},{"type":"redis","slug":"redis","name":"Redis","authType":"api-key","bgColor":"#FF4438","integrationType":"databases"},{"type":"reducto_v2","slug":"reducto","name":"Reducto","authType":"api-key","bgColor":"#5c0c5c","integrationType":"ai"},{"type":"resend","slug":"resend","name":"Resend","authType":"api-key","bgColor":"#181C1E","integrationType":"email"},{"type":"revenuecat","slug":"revenuecat","name":"RevenueCat","authType":"api-key","bgColor":"#F25A5A","integrationType":"commerce"},{"type":"rippling","slug":"rippling","name":"Rippling","authType":"api-key","bgColor":"#502D3C","integrationType":"hr"},{"type":"rocketlane","slug":"rocketlane","name":"Rocketlane","authType":"api-key","bgColor":"#000000","integrationType":"productivity"},{"type":"rootly","slug":"rootly","name":"Rootly","authType":"api-key","bgColor":"#6C72C8","integrationType":"observability"},{"type":"s3","slug":"s3","name":"S3","authType":"api-key","bgColor":"linear-gradient(45deg, #1B660F 0%, #6CAE3E 100%)","integrationType":"documents"},{"type":"sailpoint","slug":"sailpoint","name":"SailPoint","authType":"api-key","bgColor":"#FFFFFF","integrationType":"security"},{"type":"salesforce","slug":"salesforce","name":"Salesforce","authType":"oauth","oauthServiceId":"salesforce","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"sap_concur","slug":"sap-concur","name":"SAP Concur","authType":"api-key","bgColor":"#FFFFFF","integrationType":"productivity"},{"type":"sap_s4hana","slug":"sap-s4hana","name":"SAP S4HANA","authType":"api-key","bgColor":"#FFFFFF","integrationType":"hr"},{"type":"semrush","slug":"semrush","name":"Semrush","authType":"api-key","bgColor":"#FFFFFF","integrationType":"analytics"},{"type":"sendblue","slug":"sendblue","name":"Sendblue","authType":"api-key","bgColor":"#008BFF","integrationType":"communication"},{"type":"sendgrid","slug":"sendgrid","name":"SendGrid","authType":"api-key","bgColor":"#1A82E2","integrationType":"email"},{"type":"sentry","slug":"sentry","name":"Sentry","authType":"api-key","bgColor":"#362D59","integrationType":"observability"},{"type":"serper","slug":"serper","name":"Serper","authType":"api-key","bgColor":"#2B3543","integrationType":"search"},{"type":"servicenow_v2","slug":"servicenow","name":"ServiceNow","authType":"api-key","bgColor":"#032D42","integrationType":"support"},{"type":"sftp_v2","slug":"sftp","name":"SFTP","authType":"api-key","bgColor":"#2D3748","integrationType":"documents"},{"type":"sharepoint_v2","slug":"sharepoint","name":"SharePoint","authType":"oauth","oauthServiceId":"sharepoint","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"shopify","slug":"shopify","name":"Shopify","authType":"oauth","oauthServiceId":"shopify","bgColor":"#FFFFFF","integrationType":"commerce"},{"type":"similarweb","slug":"similarweb","name":"Similarweb","authType":"api-key","bgColor":"#000922","integrationType":"analytics"},{"type":"sixtyfour","slug":"sixtyfour-ai","name":"Sixtyfour AI","authType":"api-key","bgColor":"#000000","integrationType":"sales"},{"type":"slack_v2","slug":"slack","name":"Slack","authType":"oauth","oauthServiceId":"slack","bgColor":"#611f69","integrationType":"communication"},{"type":"smartlead","slug":"smartlead","name":"Smartlead","authType":"api-key","bgColor":"#000000","integrationType":"email"},{"type":"smtp","slug":"smtp","name":"SMTP","authType":"api-key","bgColor":"#2D3748","integrationType":"email"},{"type":"snowflake","slug":"snowflake","name":"Snowflake","authType":"api-key","serviceAccountServiceId":"snowflake","bgColor":"#FFFFFF","integrationType":"databases"},{"type":"splunk","slug":"splunk","name":"Splunk","authType":"api-key","bgColor":"#FFFFFF","integrationType":"observability"},{"type":"sportmonks","slug":"sportmonks","name":"Sportmonks","authType":"api-key","bgColor":"#171534","integrationType":"analytics"},{"type":"square","slug":"square","name":"Square","authType":"api-key","bgColor":"#000000","integrationType":"commerce"},{"type":"ssh_v2","slug":"ssh","name":"SSH","authType":"api-key","bgColor":"#000000","integrationType":"devops"},{"type":"stagehand","slug":"stagehand","name":"Stagehand","authType":"api-key","bgColor":"#FFC83C","integrationType":"ai"},{"type":"stripe","slug":"stripe","name":"Stripe","authType":"api-key","bgColor":"#635BFF","integrationType":"commerce"},{"type":"supabase","slug":"supabase","name":"Supabase","authType":"api-key","bgColor":"#1C1C1C","integrationType":"databases"},{"type":"tailscale","slug":"tailscale","name":"Tailscale","authType":"api-key","bgColor":"#2E2D2D","integrationType":"security"},{"type":"tavily","slug":"tavily","name":"Tavily","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"telegram","slug":"telegram","name":"Telegram","authType":"api-key","bgColor":"#FFFFFF","integrationType":"communication"},{"type":"temporal","slug":"temporal","name":"Temporal","authType":"api-key","bgColor":"#141414","integrationType":"devops"},{"type":"thrive","slug":"thrive","name":"Thrive","authType":"api-key","bgColor":"#FFFFFF","integrationType":"hr"},{"type":"tiktok","slug":"tiktok","name":"TikTok","authType":"oauth","oauthServiceId":"tiktok","bgColor":"#000000","integrationType":"communication"},{"type":"tinybird","slug":"tinybird","name":"Tinybird","authType":"api-key","bgColor":"#2EF598","integrationType":"analytics"},{"type":"tinyfish","slug":"tinyfish","name":"TinyFish","authType":"api-key","bgColor":"#FFFFFF","integrationType":"ai"},{"type":"trello","slug":"trello","name":"Trello","authType":"oauth","oauthServiceId":"trello","bgColor":"#0052CC","integrationType":"productivity"},{"type":"trigger_dev","slug":"trigger-dev","name":"Trigger.dev","authType":"api-key","bgColor":"#000000","integrationType":"devops"},{"type":"twilio_sms","slug":"twilio-sms","name":"Twilio SMS","authType":"api-key","bgColor":"#F22F46","integrationType":"communication"},{"type":"twilio_voice","slug":"twilio-voice","name":"Twilio Voice","authType":"api-key","bgColor":"#F22F46","integrationType":"communication"},{"type":"typeform","slug":"typeform","name":"Typeform","authType":"api-key","bgColor":"#262627","integrationType":"documents"},{"type":"upstash","slug":"upstash","name":"Upstash","authType":"api-key","bgColor":"#181C1E","integrationType":"databases"},{"type":"uptimerobot","slug":"uptimerobot","name":"UptimeRobot","authType":"api-key","bgColor":"#111921","integrationType":"observability"},{"type":"vanta","slug":"vanta","name":"Vanta","authType":"api-key","bgColor":"#F8F4F3","integrationType":"security"},{"type":"vercel","slug":"vercel","name":"Vercel","authType":"api-key","bgColor":"#171717","integrationType":"devops"},{"type":"wealthbox","slug":"wealthbox","name":"Wealthbox","authType":"oauth","oauthServiceId":"wealthbox","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"webflow","slug":"webflow","name":"Webflow","authType":"oauth","oauthServiceId":"webflow","bgColor":"#FFFFFF","integrationType":"marketing"},{"type":"whatsapp","slug":"whatsapp","name":"WhatsApp","authType":"api-key","bgColor":"#25D366","integrationType":"communication"},{"type":"wikipedia","slug":"wikipedia","name":"Wikipedia","authType":"none","bgColor":"#000000","integrationType":"search"},{"type":"windchill","slug":"windchill","name":"Windchill","authType":"api-key","bgColor":"#FFFFFF","integrationType":"documents"},{"type":"wiza","slug":"wiza","name":"Wiza","authType":"api-key","bgColor":"#9284BC","integrationType":"sales"},{"type":"wordpress","slug":"wordpress","name":"WordPress","authType":"oauth","oauthServiceId":"wordpress","bgColor":"#21759B","integrationType":"marketing"},{"type":"workday","slug":"workday","name":"Workday","authType":"none","bgColor":"#F5F0EB","integrationType":"hr"},{"type":"x","slug":"x","name":"X","authType":"oauth","oauthServiceId":"x","bgColor":"#000000","integrationType":"communication"},{"type":"youcom","slug":"you-com","name":"You.com","authType":"api-key","bgColor":"#FFFFFF","integrationType":"search"},{"type":"youtube","slug":"youtube","name":"YouTube","authType":"api-key","bgColor":"#FF0000","integrationType":"communication"},{"type":"zendesk","slug":"zendesk","name":"Zendesk","authType":"api-key","bgColor":"#03363D","integrationType":"support"},{"type":"zep","slug":"zep","name":"Zep","authType":"api-key","bgColor":"#E8E8E8","integrationType":"ai"},{"type":"zerobounce","slug":"zerobounce","name":"ZeroBounce","authType":"api-key","bgColor":"#FFFFFF","integrationType":"sales"},{"type":"zoho_desk","slug":"zoho-desk","name":"Zoho Desk","authType":"oauth","oauthServiceId":"zoho-desk","bgColor":"#FFFFFF","integrationType":"support"},{"type":"zoom","slug":"zoom","name":"Zoom","authType":"oauth","oauthServiceId":"zoom","bgColor":"#2D8CFF","integrationType":"communication"},{"type":"zoominfo","slug":"zoominfo","name":"ZoomInfo","authType":"api-key","bgColor":"#EA1B15","integrationType":"sales"}]'
)
diff --git a/packages/deployment-config/src/integrations.json b/packages/deployment-config/src/integrations.json
index 4170c2b0681..0910cf25b04 100644
--- a/packages/deployment-config/src/integrations.json
+++ b/packages/deployment-config/src/integrations.json
@@ -1,5 +1,5 @@
{
- "updatedAt": "2026-10-02",
+ "updatedAt": "2026-10-03",
"integrations": [
{
"type": "onepassword",
@@ -4363,6 +4363,7 @@
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
+ "serviceAccountServiceId": "claude-platform",
"category": "tools",
"integrationType": "ai",
"tags": ["agentic", "llm"]
@@ -5748,6 +5749,7 @@
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
+ "serviceAccountServiceId": "coda",
"category": "tools",
"integrationType": "documents",
"tags": ["note-taking", "knowledge-base", "spreadsheet", "project-management", "automation"]
@@ -11553,6 +11555,7 @@
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
+ "serviceAccountServiceId": "harmonic",
"category": "tools",
"integrationType": "sales",
"tags": ["enrichment", "automation", "agentic"]
@@ -17088,6 +17091,7 @@
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
+ "serviceAccountServiceId": "netsuite",
"category": "tools",
"integrationType": "commerce",
"tags": ["automation", "data-analytics", "payments"]
@@ -23677,6 +23681,7 @@
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
+ "serviceAccountServiceId": "snowflake",
"category": "tools",
"integrationType": "databases",
"tags": ["data-warehouse", "data-analytics", "cloud"]
diff --git a/packages/deployment-config/src/service-account-providers.generated.ts b/packages/deployment-config/src/service-account-providers.generated.ts
index 5084ee16254..c79611b2038 100644
--- a/packages/deployment-config/src/service-account-providers.generated.ts
+++ b/packages/deployment-config/src/service-account-providers.generated.ts
@@ -8,7 +8,9 @@ export const SERVICE_ACCOUNT_PROVIDER_BY_OAUTH_SERVICE_ID = {
attio: 'attio-service-account',
box: 'box-service-account',
calcom: 'calcom-service-account',
+ 'claude-platform': 'claude-platform-service-account',
clickup: 'clickup-service-account',
+ coda: 'coda-service-account',
confluence: 'atlassian-service-account',
gmail: 'google-service-account',
'google-bigquery': 'google-service-account',
@@ -22,15 +24,18 @@ export const SERVICE_ACCOUNT_PROVIDER_BY_OAUTH_SERVICE_ID = {
'google-sheets': 'google-service-account',
'google-tasks': 'google-service-account',
'google-vault': 'google-service-account',
+ harmonic: 'harmonic-service-account',
hubspot: 'hubspot-service-account',
jira: 'atlassian-service-account',
linear: 'linear-service-account',
monday: 'monday-service-account',
+ netsuite: 'netsuite-service-account',
notion: 'notion-service-account',
pipedrive: 'pipedrive-service-account',
salesforce: 'salesforce-service-account',
shopify: 'shopify-service-account',
slack: 'slack-custom-bot',
+ snowflake: 'snowflake-service-account',
trello: 'trello-service-account',
wealthbox: 'wealthbox-service-account',
webflow: 'webflow-service-account',
diff --git a/scripts/check-integration-catalog.ts b/scripts/check-integration-catalog.ts
index 546f85eba7b..eb7417e5cd9 100644
--- a/scripts/check-integration-catalog.ts
+++ b/scripts/check-integration-catalog.ts
@@ -8,6 +8,7 @@ import { stripVersionSuffix } from '@sim/utils/string'
*/
import { BLOCK_REGISTRY } from '../apps/sim/blocks/registry-maps'
import { AuthMode, type BlockConfig } from '../apps/sim/blocks/types'
+import { getServiceConfigByServiceId } from '../apps/sim/lib/oauth/utils'
import { INTEGRATION_METADATA } from '../packages/deployment-config/src/integration-metadata'
import integrationsJson from '../packages/deployment-config/src/integrations.json'
import { DOCS_ORIGIN, DOCS_OUTPUT_PATH, defaultIntegrationDocsUrl } from './generate-docs'
@@ -23,6 +24,7 @@ interface CatalogEntry {
bgColor: string
authType: CatalogAuthType
oauthServiceId?: string
+ serviceAccountServiceId?: string
}
function resolveAuthType(block: BlockConfig): CatalogAuthType {
@@ -57,7 +59,14 @@ function expectedEntry(block: BlockConfig): CatalogEntry {
throw new Error(`Integration block "${block.type}" is missing integrationType`)
}
const authType = resolveAuthType(block)
- const oauthServiceId = authType === 'oauth' ? resolveOAuthServiceId(block) : undefined
+ const credentialServiceId = resolveOAuthServiceId(block)
+ const oauthServiceId = authType === 'oauth' ? credentialServiceId : undefined
+ const serviceAccountServiceId =
+ authType !== 'oauth' &&
+ credentialServiceId &&
+ getServiceConfigByServiceId(credentialServiceId)?.serviceAccountProviderId
+ ? credentialServiceId
+ : undefined
if (authType === 'oauth' && !oauthServiceId) {
throw new Error(`OAuth integration block "${block.type}" is missing an OAuth service ID`)
}
@@ -73,6 +82,7 @@ function expectedEntry(block: BlockConfig): CatalogEntry {
bgColor: block.bgColor,
authType,
...(oauthServiceId ? { oauthServiceId } : {}),
+ ...(serviceAccountServiceId ? { serviceAccountServiceId } : {}),
}
}
@@ -134,12 +144,22 @@ function verifyIntegrationCatalog(): void {
const actual = integrationsJson.integrations as readonly CatalogEntry[]
const expectedMetadata = actual.map(
- ({ type, slug, name, authType, oauthServiceId, bgColor, integrationType }) => ({
+ ({
+ type,
+ slug,
+ name,
+ authType,
+ oauthServiceId,
+ serviceAccountServiceId,
+ bgColor,
+ integrationType,
+ }) => ({
type,
slug,
name,
authType,
...(oauthServiceId ? { oauthServiceId } : {}),
+ ...(serviceAccountServiceId ? { serviceAccountServiceId } : {}),
bgColor,
integrationType,
})
@@ -178,6 +198,7 @@ function verifyIntegrationCatalog(): void {
'bgColor',
'authType',
'oauthServiceId',
+ 'serviceAccountServiceId',
] as const) {
if (generated[field] !== entry[field]) {
issues.push(`"${type}" has stale ${field}`)
diff --git a/scripts/generate-deployment-config.ts b/scripts/generate-deployment-config.ts
index 12e01aeca9b..e6290ca5d32 100644
--- a/scripts/generate-deployment-config.ts
+++ b/scripts/generate-deployment-config.ts
@@ -47,14 +47,25 @@ function buildOAuthDeploymentFacts(): CanonicalOAuthDeploymentFacts {
}
const catalogServiceIds = new Set()
+ const oauthServiceIds = new Set()
for (const integration of INTEGRATION_METADATA) {
- if (integration.authType !== 'oauth') continue
- if (!integration.oauthServiceId) {
+ if (integration.authType === 'oauth' && !integration.oauthServiceId) {
throw new Error(
'Generated integration catalog contains an OAuth entry without oauthServiceId'
)
}
- catalogServiceIds.add(integration.oauthServiceId)
+ if (integration.authType === 'oauth' && integration.oauthServiceId) {
+ catalogServiceIds.add(integration.oauthServiceId)
+ oauthServiceIds.add(integration.oauthServiceId)
+ }
+ if (integration.serviceAccountServiceId) {
+ if (!canonicalServices.get(integration.serviceAccountServiceId)?.serviceAccountProviderId) {
+ throw new Error(
+ `Integration catalog references a service without a service-account provider: ${integration.serviceAccountServiceId}`
+ )
+ }
+ catalogServiceIds.add(integration.serviceAccountServiceId)
+ }
}
const providers = new Map()
@@ -64,7 +75,9 @@ function buildOAuthDeploymentFacts(): CanonicalOAuthDeploymentFacts {
throw new Error(`Integration catalog references unknown OAuth service: ${serviceId}`)
}
const service = canonicalServices.get(serviceId)
- if (service?.credentialConfigured) credentialConfiguredOAuthServiceIds.push(serviceId)
+ if (oauthServiceIds.has(serviceId) && service?.credentialConfigured) {
+ credentialConfiguredOAuthServiceIds.push(serviceId)
+ }
const providerId = service?.serviceAccountProviderId
if (providerId) providers.set(serviceId, providerId)
}
diff --git a/scripts/generate-docs.test.ts b/scripts/generate-docs.test.ts
index 933d4c4c5ec..a4c74cfbc1b 100644
--- a/scripts/generate-docs.test.ts
+++ b/scripts/generate-docs.test.ts
@@ -9,6 +9,7 @@ import {
extractAllBlockConfigs,
extractBlockSuppliedParamIds,
extractInheritedBlockCategory,
+ extractIntegrationCredentialServices,
extractToolInfo,
extractUserSettableParamIds,
generateIconMappings,
@@ -17,6 +18,54 @@ import {
parsePropertiesContent,
} from './generate-docs'
+describe('integration credential relationships', () => {
+ const serviceAccountServiceIds = new Set([
+ 'netsuite',
+ 'snowflake',
+ 'harmonic',
+ 'claude-platform',
+ 'jira',
+ ])
+ it.each([
+ { serviceId: 'netsuite', blockFile: 'netsuite' },
+ { serviceId: 'snowflake', blockFile: 'snowflake' },
+ { serviceId: 'harmonic', blockFile: 'harmonic' },
+ { serviceId: 'claude-platform', blockFile: 'managed_agent' },
+ ])(
+ 'projects $serviceId stored credentials without changing its auth marker',
+ ({ serviceId, blockFile }) => {
+ const source = fs.readFileSync(
+ path.join(__dirname, `../apps/sim/blocks/blocks/${blockFile}.ts`),
+ 'utf8'
+ )
+ expect(extractIntegrationCredentialServices(source, serviceAccountServiceIds)).toEqual({
+ serviceAccountServiceId: serviceId,
+ })
+ }
+ )
+
+ it('preserves the OAuth relationship and ignores unrelated selector services', () => {
+ expect(
+ extractIntegrationCredentialServices(
+ `{ authMode: AuthMode.OAuth, subBlocks: [{ type: 'oauth-input', serviceId: 'jira' }] }`,
+ serviceAccountServiceIds
+ )
+ ).toEqual({ oauthServiceId: 'jira' })
+ expect(
+ extractIntegrationCredentialServices(
+ `{ authMode: AuthMode.ApiKey, subBlocks: [{ type: 'selector-input', serviceId: 'netsuite' }] }`,
+ serviceAccountServiceIds
+ )
+ ).toEqual({})
+ expect(
+ extractIntegrationCredentialServices(
+ `{ authMode: AuthMode.ApiKey, subBlocks: [{ type: 'oauth-input', serviceId: 'x' }] }`,
+ serviceAccountServiceIds
+ )
+ ).toEqual({})
+ })
+})
+
describe('documentation editor icon metadata', () => {
it('keeps core icons and inherited categories out of the integration catalog', async () => {
const { docs, visible, coreBlockTypes } = await generateIconMappings()
diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts
index 77e0c0b6d71..e56a093350c 100755
--- a/scripts/generate-docs.ts
+++ b/scripts/generate-docs.ts
@@ -491,6 +491,7 @@ interface IntegrationEntry {
triggerCount: number
authType: 'oauth' | 'api-key' | 'none'
oauthServiceId?: string
+ serviceAccountServiceId?: string
category: BlockCategory
integrationType: IntegrationType
tags?: string[]
@@ -1773,6 +1774,20 @@ function isEnvironmentBoundCredential(blockContent: string): boolean {
return subBlock ? /dependsOn\s*:\s*\[[^\]]*['"]environmentUrl['"]/.test(subBlock) : false
}
+/** Keeps stored service-account ownership independent of the catalog's OAuth marker. */
+export function extractIntegrationCredentialServices(
+ blockContent: string,
+ serviceAccountServiceIds: ReadonlySet
+): {
+ oauthServiceId?: string
+ serviceAccountServiceId?: string
+} {
+ const serviceId = extractOAuthServiceId(blockContent)
+ if (!serviceId) return {}
+ if (extractAuthType(blockContent) === 'oauth') return { oauthServiceId: serviceId }
+ return serviceAccountServiceIds.has(serviceId) ? { serviceAccountServiceId: serviceId } : {}
+}
+
/**
* Extract the list of trigger IDs from the block's `triggers.available` array.
* Handles blocks that declare `triggers: { enabled: true, available: [...] }`.
@@ -1944,6 +1959,12 @@ ${mappingEntries}
* Applies the same visibility filters as the docs generation pipeline.
*/
async function writeIntegrationsJson(iconMapping: Record): Promise {
+ const { getAllOAuthServices } = await import('../apps/sim/lib/oauth/utils')
+ const serviceAccountServiceIds = new Set(
+ getAllOAuthServices()
+ .filter((service) => service.serviceAccountProviderId)
+ .map((service) => service.serviceId)
+ )
try {
if (!fs.existsSync(INTEGRATIONS_DATA_PATH)) {
fs.mkdirSync(INTEGRATIONS_DATA_PATH, { recursive: true })
@@ -2069,7 +2090,11 @@ async function writeIntegrationsJson(iconMapping: Record): Prom
.replace(/^-|-$/g, '')
const authType = extractAuthType(fileContent)
- const oauthServiceId = authType === 'oauth' ? extractOAuthServiceId(fileContent) : undefined
+ const credentialServices = extractIntegrationCredentialServices(
+ fileContent,
+ serviceAccountServiceIds
+ )
+ const { oauthServiceId } = credentialServices
// OAuth integrations resolve their connect UI through the service id
// (see `resolveOAuthServiceForIntegration`), so fail loudly rather than
// shipping a catalog entry that silently falls back to the API-key path.
@@ -2094,7 +2119,7 @@ async function writeIntegrationsJson(iconMapping: Record): Prom
triggers,
triggerCount: triggers.length,
authType,
- ...(oauthServiceId ? { oauthServiceId } : {}),
+ ...credentialServices,
category: 'tools',
integrationType,
...(config.tags ? { tags: config.tags } : {}),
@@ -2107,12 +2132,22 @@ async function writeIntegrationsJson(iconMapping: Record): Prom
const metadataPath = path.join(INTEGRATIONS_CATALOG_PATH, 'integration-metadata.ts')
const metadata = integrations.map(
- ({ type, slug, name, authType, oauthServiceId, bgColor, integrationType }) => ({
+ ({
+ type,
+ slug,
+ name,
+ authType,
+ oauthServiceId,
+ serviceAccountServiceId,
+ bgColor,
+ integrationType,
+ }) => ({
type,
slug,
name,
authType,
...(oauthServiceId ? { oauthServiceId } : {}),
+ ...(serviceAccountServiceId ? { serviceAccountServiceId } : {}),
bgColor,
integrationType,
})
@@ -2130,6 +2165,7 @@ export interface IntegrationMetadata {
name: string
authType: 'oauth' | 'api-key' | 'none'
oauthServiceId?: string
+ serviceAccountServiceId?: string
bgColor: string
integrationType: string
}