Skip to main content
ClaudeWave
Slash Command28.8k repo starsupdated today

add-trigger

The `add-trigger` command creates webhook or polling triggers that connect external services to Sim blocks, enabling real-time or periodic event detection. Use this when integrating a new service that needs to send notifications to Sim based on specific events, such as user actions, data changes, or scheduled checks.

Install in Claude Code
Copy
mkdir -p ~/.claude/commands && curl -fsSL https://raw.githubusercontent.com/simstudioai/sim/HEAD/.claude/commands/add-trigger.md -o ~/.claude/commands/add-trigger.md
Then start a new Claude Code session; the slash command loads automatically.

add-trigger.md

# Add Trigger

You are an expert at creating webhook and polling triggers for Sim. You understand the trigger system, the generic `buildTriggerSubBlocks` helper, polling infrastructure, and how triggers connect to blocks.

## Your Task

1. Research what webhook events the service supports — if the service lacks reliable webhooks, use polling
2. Create the trigger files using the generic builder (webhook) or manual config (polling)
3. Create a provider handler (webhook) or polling handler (polling)
4. Register triggers and connect them to the block

## Directory Structure

```
apps/sim/triggers/{service}/
├── index.ts              # Barrel exports
├── utils.ts              # Service-specific helpers (options, instructions, extra fields, outputs)
├── {event_a}.ts          # Primary trigger (includes dropdown)
├── {event_b}.ts          # Secondary trigger (no dropdown)
└── webhook.ts            # Generic webhook trigger (optional, for "all events")

apps/sim/lib/webhooks/
├── provider-subscription-utils.ts  # Shared subscription helpers (getProviderConfig, getNotificationUrl)
├── providers/
│   ├── {service}.ts       # Provider handler (auth, formatInput, matchEvent, subscriptions)
│   ├── types.ts           # WebhookProviderHandler interface
│   ├── utils.ts           # Shared helpers (createHmacVerifier, verifyTokenAuth, skipByEventTypes)
│   └── registry.ts        # Handler map + default handler
```

## Step 1: Create `utils.ts`

This file contains all service-specific helpers used by triggers.

```typescript
import type { SubBlockConfig } from '@/blocks/types'
import type { TriggerOutput } from '@/triggers/types'

export const {service}TriggerOptions = [
  { label: 'Event A', id: '{service}_event_a' },
  { label: 'Event B', id: '{service}_event_b' },
]

export function {service}SetupInstructions(eventType: string): string {
  const instructions = [
    'Copy the <strong>Webhook URL</strong> above',
    'Go to <strong>{Service} Settings > Webhooks</strong>',
    `Select the <strong>${eventType}</strong> event type`,
    'Paste the webhook URL and save',
    'Click "Save" above to activate your trigger',
  ]
  return instructions
    .map((instruction, index) =>
      `<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
    )
    .join('')
}

export function build{Service}ExtraFields(triggerId: string): SubBlockConfig[] {
  return [
    {
      id: 'projectId',
      title: 'Project ID (Optional)',
      type: 'short-input',
      placeholder: 'Leave empty for all projects',
      mode: 'trigger',
      condition: { field: 'selectedTriggerId', value: triggerId },
    },
  ]
}

export function build{Service}Outputs(): Record<string, TriggerOutput> {
  return {
    eventType: { type: 'string', description: 'The type of event' },
    resourceId: { type: 'string', description: 'ID of the affected resource' },
    resource: {
      id: { type: 'string', description: 'Resource ID' },
      name: { type: 'string', description: 'Resource name' },
    },
  }
}
```

## Step 2: Create Trigger Files

**Primary trigger** — MUST include `includeDropdown: true`:

```typescript
import { {Service}Icon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import { build{Service}ExtraFields, build{Service}Outputs, {service}SetupInstructions, {service}TriggerOptions } from '@/triggers/{service}/utils'
import type { TriggerConfig } from '@/triggers/types'

export const {service}EventATrigger: TriggerConfig = {
  id: '{service}_event_a',
  name: '{Service} Event A',
  provider: '{service}',
  description: 'Trigger workflow when Event A occurs',
  version: '1.0.0',
  icon: {Service}Icon,
  subBlocks: buildTriggerSubBlocks({
    triggerId: '{service}_event_a',
    triggerOptions: {service}TriggerOptions,
    includeDropdown: true,
    setupInstructions: {service}SetupInstructions('Event A'),
    extraFields: build{Service}ExtraFields('{service}_event_a'),
  }),
  outputs: build{Service}Outputs(),
  webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
}
```

**Secondary triggers** — NO `includeDropdown` (it's already in the primary):

```typescript
export const {service}EventBTrigger: TriggerConfig = {
  // Same as above but: id: '{service}_event_b', no includeDropdown
}
```

## Step 3: Register and Wire

### `apps/sim/triggers/{service}/index.ts`

```typescript
export { {service}EventATrigger } from './event_a'
export { {service}EventBTrigger } from './event_b'
```

### `apps/sim/triggers/registry.ts`

```typescript
import { {service}EventATrigger, {service}EventBTrigger } from '@/triggers/{service}'

export const TRIGGER_REGISTRY: TriggerRegistry = {
  // ... existing ...
  {service}_event_a: {service}EventATrigger,
  {service}_event_b: {service}EventBTrigger,
}
```

### Block file (`apps/sim/blocks/blocks/{service}.ts`)

Wire triggers into the block so the trigger UI appears and `generate-docs.ts` discovers them. Two changes are needed:

1. **Spread trigger subBlocks** at the end of the block's `subBlocks` array
2. **Add `triggers` property** after `outputs` with `enabled: true` and `available: [...]`

```typescript
import { getTrigger } from '@/triggers'

export const {Service}Block: BlockConfig = {
  // ...
  subBlocks: [
    // Regular tool subBlocks first...
    ...getTrigger('{service}_event_a').subBlocks,
    ...getTrigger('{service}_event_b').subBlocks,
  ],
  // ... tools, inputs, outputs ...
  triggers: {
    enabled: true,
    available: ['{service}_event_a', '{service}_event_b'],
  },
}
```

**Versioned blocks (V1 + V2):** Many integrations have a hidden V1 block and a visible V2 block. Where you add the trigger wiring depends on how V2 inherits from V1:

- **V2 uses `...V1Block` spread** (e.g., Google Calendar): Add trigger to V1 — V2 inherits both `subBlocks` and `triggers` automatically.
- **V2 defines its own `subBlocks`** (e.g., Google Sheets): Add trigger to V2 (the visible block). V1 is hidden and doesn't need it.
- **Singl