Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude/commands/add-trigger.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ export const {service}PollingHandler: PollingProviderHandler = {

try {
// For OAuth services:
const accessToken = await resolveOAuthCredential(webhookData, '{service}', requestId, logger)
const accessToken = await resolveOAuthCredential(webhookData, '{service}', requestId)
const config = webhookData.providerConfig as unknown as {Service}WebhookConfig

// First poll: seed state, emit nothing
Expand Down Expand Up @@ -421,7 +421,7 @@ export const {service}PollingTrigger: TriggerConfig = {
polling: true, // REQUIRED — routes to polling infrastructure

subBlocks: [
{ id: 'triggerCredentials', type: 'oauth-input', title: 'Credentials', serviceId: '{service}', requiredScopes: [], required: true, mode: 'trigger', supportsCredentialSets: true },
{ id: 'triggerCredentials', type: 'oauth-input', title: 'Credentials', serviceId: '{service}', requiredScopes: [], required: true, mode: 'trigger' },
// ... service-specific config fields (dropdowns, inputs, switches) ...
{ id: 'triggerInstructions', type: 'text', title: 'Setup Instructions', hideFromPreview: true, mode: 'trigger', defaultValue: '...' },
],
Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/sim-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Next.js rewrites **every** export of a `'use client'` module into a *client refe
So any **query-key factory, standalone `requestJson` fetcher, mapper, or constant** that a server module imports must live in a **non-`'use client'`** module:

- key factories → `hooks/queries/utils/<entity>-keys.ts` (see `folder-keys.ts`, `table-keys.ts`, `credential-keys.ts`)
- standalone fetchers/mappers → `hooks/queries/utils/fetch-*.ts` / `*-list-query.ts` (see `fetch-workflow-envelope.ts`, `fetch-credential-set.ts`)
- standalone fetchers/mappers → `hooks/queries/utils/fetch-*.ts` / `*-list-query.ts` (see `fetch-workflow-envelope.ts`, `fetch-workspace-credentials.ts`)

The `'use client'` hook module then imports these back for its hooks. **Never** define a server-imported factory/fetcher directly in a `'use client'` hooks file — it crashes SSR (this caused the tables-page crash). Enforced for prefetch/route/trigger/block files by `scripts/check-client-boundary-imports.ts` (`bun run check:client-boundary`, run in CI). Escape hatch for a genuinely browser-only path: `// client-boundary-allow: <reason>` on the line above the import.

Expand Down
4 changes: 2 additions & 2 deletions .claude/rules/sim-settings-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ pairing is:
This is not a stylistic guess — it is the tokenized form of the literal-pixel
pairing (`text-[14px] text-[var(--text-body)]` / `text-[12px]
text-[var(--text-muted)]`) already used for this exact row shape across
`member-list.tsx`, `api-keys.tsx`, `mcp.tsx`, `billing.tsx`, `credential-sets.tsx`,
`member-list.tsx`, `api-keys.tsx`, `mcp.tsx`, `billing.tsx`,
`workflow-mcp-servers.tsx`, and others — keep new rows consistent with it rather
than inventing a new size pairing.

Expand Down Expand Up @@ -203,7 +203,7 @@ changes" modal:
## Detail sub-views

A drill-down view reached from a list row (selected MCP server, workflow MCP
server, credential set, permission group, retention policy) renders through
server, permission group, retention policy) renders through
`SettingsPanel` like a list page: pass `back={{ text, icon: ArrowLeft, onSelect }}`
for the left back chip, `title` (the entity name), and the header `actions`, then
render the body. Do NOT hand-roll a shell or header bar; a tab bar renders as the
Expand Down
4 changes: 2 additions & 2 deletions .cursor/commands/add-trigger.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ export const {service}PollingHandler: PollingProviderHandler = {

try {
// For OAuth services:
const accessToken = await resolveOAuthCredential(webhookData, '{service}', requestId, logger)
const accessToken = await resolveOAuthCredential(webhookData, '{service}', requestId)
const config = webhookData.providerConfig as unknown as {Service}WebhookConfig

// First poll: seed state, emit nothing
Expand Down Expand Up @@ -416,7 +416,7 @@ export const {service}PollingTrigger: TriggerConfig = {
polling: true, // REQUIRED — routes to polling infrastructure

subBlocks: [
{ id: 'triggerCredentials', type: 'oauth-input', title: 'Credentials', serviceId: '{service}', requiredScopes: [], required: true, mode: 'trigger', supportsCredentialSets: true },
{ id: 'triggerCredentials', type: 'oauth-input', title: 'Credentials', serviceId: '{service}', requiredScopes: [], required: true, mode: 'trigger' },
// ... service-specific config fields (dropdowns, inputs, switches) ...
{ id: 'triggerInstructions', type: 'text', title: 'Setup Instructions', hideFromPreview: true, mode: 'trigger', defaultValue: '...' },
],
Expand Down
1 change: 0 additions & 1 deletion apps/docs/content/docs/de/enterprise/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ Für selbst gehostete Bereitstellungen können Enterprise-Funktionen über Umgeb
| Variable | Beschreibung |
|----------|-------------|
| `SSO_ENABLED`, `NEXT_PUBLIC_SSO_ENABLED` | Single Sign-On mit SAML/OIDC |
| `CREDENTIAL_SETS_ENABLED`, `NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | Polling-Gruppen für E-Mail-Trigger |
| `DISABLE_INVITATIONS`, `NEXT_PUBLIC_DISABLE_INVITATIONS` | Workspace-/Organisations-Einladungen global deaktivieren |

<Callout type="warn">
Expand Down
4 changes: 0 additions & 4 deletions apps/docs/content/docs/de/triggers/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ Verwende den Start-Block für alles, was aus dem Editor, deploy-to-API oder depl
<Card title="RSS Feed" href="/triggers/rss">
RSS- und Atom-Feeds auf neue Inhalte überwachen
</Card>
<Card title="Email Polling Groups" href="#email-polling-groups">
Team-Gmail- und Outlook-Postfächer überwachen
</Card>
</Cards>

## Schneller Vergleich
Expand All @@ -46,7 +43,6 @@ Verwende den Start-Block für alles, was aus dem Editor, deploy-to-API oder depl
| **Schedule** | Timer, der im Schedule-Block verwaltet wird |
| **Webhook** | Bei eingehender HTTP-Anfrage |
| **RSS Feed** | Neues Element im Feed veröffentlicht |
| **Email Polling Groups** | Neue E-Mail in Team-Gmail- oder Outlook-Postfächern empfangen |

> Der Start-Block stellt immer `input`, `conversationId` und `files` Felder bereit. Füge benutzerdefinierte Felder zum Eingabeformat für zusätzliche strukturierte Daten hinzu.
Expand Down
6 changes: 0 additions & 6 deletions apps/docs/content/docs/en/integrations/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,6 @@ Open a connection from the **Connected** list to manage it:
If you disconnect an integration that is used in a workflow, that workflow will fail at any block referencing it. Update blocks before disconnecting.
</Callout>

## Email polling groups

The Gmail and Outlook email triggers can watch several team members' inboxes through a single trigger, called an **email polling group** (Team and Enterprise plans). An admin creates a group under **Settings → Email Polling**, picks Gmail or Outlook, and invites members by email; each invitee connects their own inbox through a link. On an email trigger, select the group from the credentials dropdown instead of a single account, and the trigger routes everyone's mail through the workflow.

Inviting someone to a group grants inbox access for that trigger only, which is separate from workspace membership.

<FAQ items={[
{ question: "Does Sim handle OAuth token refresh automatically?", answer: "Yes. When an integration is used during execution, Sim checks whether the access token has expired and automatically refreshes it using the stored refresh token before making the API call. You do not need to handle token refresh manually." },
{ question: "Can I connect multiple accounts for the same service?", answer: "Yes. You can connect multiple accounts per service (for example, two separate Gmail accounts). Each block lets you select which account to use from the account dropdown. This is useful when different workflows need different identities or permissions." },
Expand Down
1 change: 0 additions & 1 deletion apps/docs/content/docs/en/platform/enterprise/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ Self-hosted deployments enable enterprise features via environment variables ins
| `AUDIT_LOGS_ENABLED`, `NEXT_PUBLIC_AUDIT_LOGS_ENABLED` | Audit logging |
| `NEXT_PUBLIC_DATA_RETENTION_ENABLED` | Data retention configuration |
| `DATA_DRAINS_ENABLED`, `NEXT_PUBLIC_DATA_DRAINS_ENABLED` | Data drains |
| `CREDENTIAL_SETS_ENABLED`, `NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | Polling groups for email triggers |
| `INBOX_ENABLED`, `NEXT_PUBLIC_INBOX_ENABLED` | Sim Mailer inbox |
| `DISABLE_INVITATIONS`, `NEXT_PUBLIC_DISABLE_INVITATIONS` | Disable invitations; manage membership via Admin API |

Expand Down
1 change: 0 additions & 1 deletion apps/docs/content/docs/es/enterprise/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ Para implementaciones self-hosted, las funciones enterprise se pueden activar me
| Variable | Descripción |
|----------|-------------|
| `SSO_ENABLED`, `NEXT_PUBLIC_SSO_ENABLED` | Inicio de sesión único con SAML/OIDC |
| `CREDENTIAL_SETS_ENABLED`, `NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | Grupos de sondeo para activadores de correo electrónico |
| `DISABLE_INVITATIONS`, `NEXT_PUBLIC_DISABLE_INVITATIONS` | Desactivar globalmente invitaciones a espacios de trabajo/organizaciones |

<Callout type="warn">
Expand Down
4 changes: 0 additions & 4 deletions apps/docs/content/docs/es/triggers/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ Utiliza el bloque Start para todo lo que se origina desde el editor, despliegue
<Card title="RSS Feed" href="/triggers/rss">
Monitorea feeds RSS y Atom para detectar contenido nuevo
</Card>
<Card title="Email Polling Groups" href="#email-polling-groups">
Monitorea bandejas de entrada de Gmail y Outlook del equipo
</Card>
</Cards>

## Comparación rápida
Expand All @@ -46,7 +43,6 @@ Utiliza el bloque Start para todo lo que se origina desde el editor, despliegue
| **Schedule** | Temporizador gestionado en el bloque de programación |
| **Webhook** | Al recibir una solicitud HTTP entrante |
| **RSS Feed** | Nuevo elemento publicado en el feed |
| **Email Polling Groups** | Nuevo correo electrónico recibido en bandejas de entrada de Gmail o Outlook del equipo |

> El bloque Start siempre expone los campos `input`, `conversationId` y `files`. Añade campos personalizados al formato de entrada para datos estructurados adicionales.

Expand Down
1 change: 0 additions & 1 deletion apps/docs/content/docs/fr/enterprise/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ Pour les déploiements auto-hébergés, les fonctionnalités entreprise peuvent
| Variable | Description |
|----------|-------------|
| `SSO_ENABLED`, `NEXT_PUBLIC_SSO_ENABLED` | Authentification unique avec SAML/OIDC |
| `CREDENTIAL_SETS_ENABLED`, `NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | Groupes de sondage pour les déclencheurs d'e-mail |
| `DISABLE_INVITATIONS`, `NEXT_PUBLIC_DISABLE_INVITATIONS` | Désactiver globalement les invitations aux espaces de travail/organisations |

<Callout type="warn">
Expand Down
4 changes: 0 additions & 4 deletions apps/docs/content/docs/fr/triggers/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ Utilisez le bloc Démarrer pour tout ce qui provient de l'éditeur, du déploiem
<Card title="RSS Feed" href="/triggers/rss">
Surveiller les flux RSS et Atom pour détecter du nouveau contenu
</Card>
<Card title="Email Polling Groups" href="#email-polling-groups">
Surveiller les boîtes de réception Gmail et Outlook de l'équipe
</Card>
</Cards>

## Comparaison rapide
Expand All @@ -46,7 +43,6 @@ Utilisez le bloc Démarrer pour tout ce qui provient de l'éditeur, du déploiem
| **Schedule** | Minuteur géré dans le bloc de planification |
| **Webhook** | Lors d'une requête HTTP entrante |
| **RSS Feed** | Nouvel élément publié dans le flux |
| **Email Polling Groups** | Nouvel e-mail reçu dans les boîtes de réception Gmail ou Outlook de l'équipe |

> Le bloc Démarrer expose toujours les champs `input`, `conversationId` et `files`. Ajoutez des champs personnalisés au format d'entrée pour des données structurées supplémentaires.

Expand Down
1 change: 0 additions & 1 deletion apps/docs/content/docs/ja/enterprise/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ Simのホストキーの代わりに、AIモデルプロバイダー用の独自
| 変数 | 説明 |
|----------|-------------|
| `SSO_ENABLED``NEXT_PUBLIC_SSO_ENABLED` | SAML/OIDCによるシングルサインオン |
| `CREDENTIAL_SETS_ENABLED``NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | メールトリガー用のポーリンググループ |
| `DISABLE_INVITATIONS``NEXT_PUBLIC_DISABLE_INVITATIONS` | ワークスペース/組織への招待をグローバルに無効化 |

<Callout type="warn">
Expand Down
26 changes: 0 additions & 26 deletions apps/docs/content/docs/ja/triggers/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ import { Image } from '@/components/ui/image'
<Card title="RSS Feed" href="/triggers/rss">
RSSおよびAtomフィードの新しいコンテンツを監視
</Card>
<Card title="Email Polling Groups" href="#email-polling-groups">
チームのGmailおよびOutlook受信トレイを監視
</Card>
</Cards>

## クイック比較
Expand All @@ -46,7 +43,6 @@ import { Image } from '@/components/ui/image'
| **Schedule** | スケジュールブロックで管理されるタイマー |
| **Webhook** | インバウンドHTTPリクエスト時 |
| **RSS Feed** | フィードに新しいアイテムが公開された時 |
| **Email Polling Groups** | チームのGmailまたはOutlook受信トレイに新しいメールが受信された時 |

> スタートブロックは常に `input`、`conversationId`、および `files` フィールドを公開します。追加の構造化データには入力フォーマットにカスタムフィールドを追加してください。

Expand All @@ -69,25 +65,3 @@ import { Image } from '@/components/ui/image'
ワークフローに複数のトリガーがある場合、最も優先度の高いトリガーが実行されます。例えば、スタートブロックとウェブフックトリガーの両方がある場合、実行をクリックするとスタートブロックが実行されます。

**モックペイロードを持つ外部トリガー**: 外部トリガー(ウェブフックと連携)が手動で実行される場合、Simはトリガーの予想されるデータ構造に基づいてモックペイロードを自動生成します。これにより、テスト中に下流のブロックが変数を正しく解決できるようになります。

## Email Polling Groups

Polling Groupsを使用すると、単一のトリガーで複数のチームメンバーのGmailまたはOutlook受信トレイを監視できます。TeamまたはEnterpriseプランが必要です。

**Polling Groupの作成**(管理者/オーナー)

1. **設定 → Email Polling**に移動
2. **作成**をクリックし、GmailまたはOutlookを選択
3. グループの名前を入力

**メンバーの招待**

1. Polling Groupの**メンバーを追加**をクリック
2. メールアドレスを入力(カンマまたは改行で区切る、またはCSVをドラッグ&ドロップ)
3. **招待を送信**をクリック

招待された人は、アカウントを接続するためのリンクが記載されたメールを受信します。接続されると、その受信トレイは自動的にPolling Groupに含まれます。招待された人は、Sim組織のメンバーである必要はありません。

**ワークフローでの使用**

メールトリガーを設定する際、個別のアカウントではなく、認証情報ドロップダウンからPolling Groupを選択します。システムは各メンバーのWebhookを作成し、すべてのメールをワークフローを通じてルーティングします。
1 change: 0 additions & 1 deletion apps/docs/content/docs/zh/enterprise/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ Sim 企业版为需要更高安全性、合规性和管理能力的组织提供
| 变量 | 描述 |
|----------|-------------|
| `SSO_ENABLED``NEXT_PUBLIC_SSO_ENABLED` | 使用 SAML/OIDC 的单点登录 |
| `CREDENTIAL_SETS_ENABLED``NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | 用于邮件触发器的轮询组 |
| `DISABLE_INVITATIONS``NEXT_PUBLIC_DISABLE_INVITATIONS` | 全局禁用工作区/组织邀请 |

<Callout type="warn">
Expand Down
4 changes: 0 additions & 4 deletions apps/docs/content/docs/zh/triggers/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ import { Image } from '@/components/ui/image'
<Card title="RSS Feed" href="/triggers/rss">
监控 RSS 和 Atom 订阅源的新内容
</Card>
<Card title="Email Polling Groups" href="#email-polling-groups">
监控团队 Gmail 和 Outlook 收件箱
</Card>
</Cards>

## 快速对比
Expand All @@ -46,7 +43,6 @@ import { Image } from '@/components/ui/image'
| **Schedule** | 在 schedule 块中管理的定时器 |
| **Webhook** | 收到入站 HTTP 请求时 |
| **RSS Feed** | 订阅源中有新内容发布时 |
| **Email Polling Groups** | 团队 Gmail 或 Outlook 收件箱收到新邮件时 |

> Start 块始终公开 `input`、`conversationId` 和 `files` 字段。通过向输入格式添加自定义字段来增加结构化数据。

Expand Down
5 changes: 1 addition & 4 deletions apps/sim/app/(auth)/signup/signup-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,7 @@ function SignupFormContent({
}
const redirectUrl = isValidRedirectUrl ? rawRedirectUrl : ''
const isInviteFlow = useMemo(
() =>
searchParams.get('invite_flow') === 'true' ||
redirectUrl.startsWith('/invite/') ||
redirectUrl.startsWith('/credential-account/'),
() => searchParams.get('invite_flow') === 'true' || redirectUrl.startsWith('/invite/'),
[searchParams, redirectUrl]
)

Expand Down
8 changes: 0 additions & 8 deletions apps/sim/app/api/auth/oauth/disconnect/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,8 @@ import {
} from '@sim/testing'
import { beforeEach, describe, expect, it, vi } from 'vitest'

const { mockSyncAllWebhooksForCredentialSet } = vi.hoisted(() => ({
mockSyncAllWebhooksForCredentialSet: vi.fn().mockResolvedValue({}),
}))

vi.mock('@sim/db', () => dbChainMock)

vi.mock('@/lib/webhooks/utils.server', () => ({
syncAllWebhooksForCredentialSet: mockSyncAllWebhooksForCredentialSet,
}))

vi.mock('@sim/audit', () => auditMock)

import { POST } from '@/app/api/auth/oauth/disconnect/route'
Expand Down
46 changes: 1 addition & 45 deletions apps/sim/app/api/auth/oauth/disconnect/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
import { db } from '@sim/db'
import { account, credential, credentialSet, credentialSetMember } from '@sim/db/schema'
import { account, credential } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, inArray, like, or } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
Expand All @@ -11,7 +11,6 @@ import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { deleteCredential } from '@/lib/credentials/deletion'
import { captureServerEvent } from '@/lib/posthog/server'
import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'

export const dynamic = 'force-dynamic'

Expand Down Expand Up @@ -104,49 +103,6 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
await db.delete(account).where(inArray(account.id, targetAccountIds))
}

// Sync webhooks for all credential sets the user is a member of
// This removes webhooks that were using the disconnected credential
const userMemberships = await db
.select({
id: credentialSetMember.id,
credentialSetId: credentialSetMember.credentialSetId,
providerId: credentialSet.providerId,
})
.from(credentialSetMember)
.innerJoin(credentialSet, eq(credentialSetMember.credentialSetId, credentialSet.id))
.where(
and(
eq(credentialSetMember.userId, session.user.id),
eq(credentialSetMember.status, 'active')
)
)

for (const membership of userMemberships) {
// Only sync if the credential set matches this provider
// Credential sets store OAuth provider IDs like 'google-email' or 'outlook'
const matchesProvider =
membership.providerId === provider ||
membership.providerId === providerId ||
membership.providerId?.startsWith(`${provider}-`)

if (matchesProvider) {
try {
await syncAllWebhooksForCredentialSet(membership.credentialSetId, requestId)
logger.info(`[${requestId}] Synced webhooks after credential disconnect`, {
credentialSetId: membership.credentialSetId,
provider,
})
} catch (error) {
// Log but don't fail the disconnect - credential is already removed
logger.error(`[${requestId}] Failed to sync webhooks after credential disconnect`, {
credentialSetId: membership.credentialSetId,
provider,
error,
})
}
}
}

recordAudit({
workspaceId: null,
actorId: session.user.id,
Expand Down
Loading
Loading