diff --git a/apps/sim/app/(landing)/integrations/[slug]/page.tsx b/apps/sim/app/(landing)/integrations/[slug]/page.tsx
index 693e0fee68..743fc2f006 100644
--- a/apps/sim/app/(landing)/integrations/[slug]/page.tsx
+++ b/apps/sim/app/(landing)/integrations/[slug]/page.tsx
@@ -5,7 +5,6 @@ import { TEMPLATES } from '@/app/workspace/[workspaceId]/home/components/templat
import { IntegrationIcon } from '../components/integration-icon'
import { blockTypeToIconMap } from '../data/icon-mapping'
import integrations from '../data/integrations.json'
-import { POPULAR_WORKFLOWS } from '../data/popular-workflows'
import type { AuthType, FAQItem, Integration } from '../data/types'
import { IntegrationFAQ } from './components/integration-faq'
import { TemplateCardButton } from './components/template-card-button'
@@ -14,44 +13,52 @@ const allIntegrations = integrations as Integration[]
const INTEGRATION_COUNT = allIntegrations.length
/** Fast O(1) lookups — avoids repeated linear scans inside render loops. */
-const byName = new Map(allIntegrations.map((i) => [i.name, i]))
const bySlug = new Map(allIntegrations.map((i) => [i.slug, i]))
const byType = new Map(allIntegrations.map((i) => [i.type, i]))
-/** Returns workflow pairs that feature the given integration on either side. */
-function getPairsFor(name: string) {
- return POPULAR_WORKFLOWS.filter((p) => p.from === name || p.to === name)
-}
-
/**
* Returns up to `limit` related integration slugs.
*
- * Scoring:
- * +100 — integration appears as a workflow pair partner (explicit editorial signal)
- * +N — N operation names shared with the current integration (semantic similarity)
+ * Scoring (additive):
+ * +3 per shared operation name — strongest signal (same capability)
+ * +2 per shared operation word — weaker signal (e.g. both have "create" ops)
+ * +1 same auth type — comparable setup experience
*
- * This means genuine partners always rank first; operation-similar integrations
- * (e.g. Slack → Teams → Discord for "Send Message") fill the rest organically.
+ * Every integration gets a score, so the sidebar always has suggestions.
+ * Ties are broken by alphabetical slug order for determinism.
*/
function getRelatedSlugs(
- name: string,
slug: string,
operations: Integration['operations'],
+ authType: AuthType,
limit = 6
): string[] {
- const partners = new Set(getPairsFor(name).map((p) => (p.from === name ? p.to : p.from)))
- const currentOps = new Set(operations.map((o) => o.name.toLowerCase()))
+ const currentOpNames = new Set(operations.map((o) => o.name.toLowerCase()))
+ const currentOpWords = new Set(
+ operations.flatMap((o) =>
+ o.name
+ .toLowerCase()
+ .split(/\s+/)
+ .filter((w) => w.length > 3)
+ )
+ )
return allIntegrations
.filter((i) => i.slug !== slug)
- .map((i) => ({
- slug: i.slug,
- score:
- (partners.has(i.name) ? 100 : 0) +
- i.operations.filter((o) => currentOps.has(o.name.toLowerCase())).length,
- }))
- .filter(({ score }) => score > 0)
- .sort((a, b) => b.score - a.score)
+ .map((i) => {
+ const sharedNames = i.operations.filter((o) =>
+ currentOpNames.has(o.name.toLowerCase())
+ ).length
+ const sharedWords = i.operations.filter((o) =>
+ o.name
+ .toLowerCase()
+ .split(/\s+/)
+ .some((w) => w.length > 3 && currentOpWords.has(w))
+ ).length
+ const sameAuth = i.authType === authType ? 1 : 0
+ return { slug: i.slug, score: sharedNames * 3 + sharedWords * 2 + sameAuth }
+ })
+ .sort((a, b) => b.score - a.score || a.slug.localeCompare(b.slug))
.slice(0, limit)
.map(({ slug: s }) => s)
}
@@ -70,7 +77,6 @@ function buildFAQs(integration: Integration): FAQItem[] {
const { name, description, operations, triggers, authType } = integration
const topOps = operations.slice(0, 5)
const topOpNames = topOps.map((o) => o.name)
- const pairs = getPairsFor(name)
const authStep = AUTH_STEP[authType]
const faqs: FAQItem[] = [
@@ -89,6 +95,10 @@ function buildFAQs(integration: Integration): FAQItem[] {
question: `How do I connect ${name} to Sim?`,
answer: `Getting started takes under five minutes: (1) Create a free account at sim.ai. (2) Open a new workflow. (3) Drag a ${name} block onto the canvas. (4) ${authStep} (5) Choose the tool you want to use, wire it to the inputs you need, and click Run. Your automation is live.`,
},
+ {
+ question: `Can I use ${name} as a tool inside an AI agent in Sim?`,
+ answer: `Yes — this is one of Sim's core capabilities. Instead of hard-coding when and how ${name} is used, you give an AI agent access to ${name} tools and describe the goal in plain language. The agent decides which tools to call, in what order, and how to handle the results. This means your automation adapts to context rather than breaking when inputs change.`,
+ },
...(topOpNames.length >= 2
? [
{
@@ -97,19 +107,15 @@ function buildFAQs(integration: Integration): FAQItem[] {
},
]
: []),
- ...(pairs.length > 0
+ ...(triggers.length > 0
? [
{
- question: `Can I connect ${name} to ${pairs[0].from === name ? pairs[0].to : pairs[0].from} with Sim?`,
- answer: `Yes. ${pairs[0].description} In Sim, you set this up by adding both a ${name} block and a ${pairs[0].from === name ? pairs[0].to : pairs[0].from} block to the same workflow and connecting them through an AI agent that orchestrates the logic between them.`,
+ question: `How do I trigger a Sim workflow from ${name} automatically?`,
+ answer: `In your Sim workflow, switch the ${name} block to Trigger mode and copy the generated webhook URL. Paste that URL into ${name}'s webhook settings and select the events you want to listen for (${triggers.map((t) => t.name).join(', ')}). From that point on, every matching event in ${name} instantly fires your workflow — no polling, no delay.`,
},
- ]
- : []),
- ...(triggers.length > 0
- ? [
{
- question: `Can ${name} trigger a Sim workflow automatically?`,
- answer: `Yes. ${name} supports ${triggers.length} webhook trigger${triggers.length === 1 ? '' : 's'} that can instantly start a Sim workflow: ${triggers.map((t) => t.name).join(', ')}. No polling needed — the workflow fires the moment the event occurs in ${name}.`,
+ question: `What data does Sim receive when a ${name} event triggers a workflow?`,
+ answer: `When ${name} fires a webhook, Sim receives the full event payload that ${name} sends — typically the record or object that changed, along with metadata like the event type and timestamp. Inside your workflow, every field from that payload is available as a variable you can pass to AI agents, conditions, or other integrations.`,
},
]
: []),
@@ -190,11 +196,10 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
const IconComponent = blockTypeToIconMap[integration.type]
const faqs = buildFAQs(integration)
- const relatedSlugs = getRelatedSlugs(name, slug, operations)
+ const relatedSlugs = getRelatedSlugs(slug, operations, authType)
const relatedIntegrations = relatedSlugs
.map((s) => bySlug.get(s))
.filter((i): i is Integration => i !== undefined)
- const featuredPairs = getPairsFor(name)
const baseType = integration.type.replace(/_v\d+$/, '')
const matchingTemplates = TEMPLATES.filter(
(t) =>
@@ -420,15 +425,18 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
{triggers.length > 0 && (
- Triggers
+ Real-time triggers
-
- These events in {name} can automatically start a Sim workflow — no polling
- required.
+
+ Connect a {name} webhook to Sim and your workflow fires the instant an event
+ happens — no polling, no delay. Sim receives the full event payload and makes
+ every field available as a variable inside your workflow.
+
+ {/* Event cards */}
{triggers.map((trigger) => (
-
- Trigger
+ Event