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
52 changes: 52 additions & 0 deletions .claude/commands/add-trigger.md
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,53 @@ All fields automatically have:
- `mode: 'trigger'` - Only shown in trigger mode
- `condition: { field: 'selectedTriggerId', value: triggerId }` - Only shown when this trigger is selected

## Trigger Outputs & Webhook Input Formatting

### Important: Two Sources of Truth

There are two related but separate concerns:

1. **Trigger `outputs`** - Schema/contract defining what fields SHOULD be available. Used by UI for tag dropdown.
2. **`formatWebhookInput`** - Implementation that transforms raw webhook payload into actual data. Located in `apps/sim/lib/webhooks/utils.server.ts`.

**These MUST be aligned.** The fields returned by `formatWebhookInput` should match what's defined in trigger `outputs`. If they differ:
- Tag dropdown shows fields that don't exist (broken variable resolution)
- Or actual data has fields not shown in dropdown (users can't discover them)

### When to Add a formatWebhookInput Handler

- **Simple providers**: If the raw webhook payload structure already matches your outputs, you don't need a handler. The generic fallback returns `body` directly.
- **Complex providers**: If you need to transform, flatten, extract nested data, compute fields, or handle conditional logic, add a handler.

### Adding a Handler

In `apps/sim/lib/webhooks/utils.server.ts`, add a handler block:

```typescript
if (foundWebhook.provider === '{service}') {
// Transform raw webhook body to match trigger outputs
return {
eventType: body.type,
resourceId: body.data?.id || '',
timestamp: body.created_at,
resource: body.data,
}
}
```

**Key rules:**
- Return fields that match your trigger `outputs` definition exactly
- No wrapper objects like `webhook: { data: ... }` or `{service}: { ... }`
- No duplication (don't spread body AND add individual fields)
- Use `null` for missing optional data, not empty objects with empty strings

### Verify Alignment

Run the alignment checker:
```bash
bunx scripts/check-trigger-alignment.ts {service}
```

## Trigger Outputs

Trigger outputs use the same schema as block outputs (NOT tool outputs).
Expand Down Expand Up @@ -649,6 +696,11 @@ export const {service}WebhookTrigger: TriggerConfig = {
- [ ] Added `delete{Service}Webhook` function to `provider-subscriptions.ts`
- [ ] Added provider to `cleanupExternalWebhook` function

### Webhook Input Formatting
- [ ] Added handler in `apps/sim/lib/webhooks/utils.server.ts` (if custom formatting needed)
- [ ] Handler returns fields matching trigger `outputs` exactly
- [ ] Run `bunx scripts/check-trigger-alignment.ts {service}` to verify alignment

### Testing
- [ ] Run `bun run type-check` to verify no TypeScript errors
- [ ] Restart dev server to pick up new triggers
Expand Down
20 changes: 20 additions & 0 deletions apps/sim/blocks/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,26 @@ export const getBlock = (type: string): BlockConfig | undefined => {
return registry[normalized]
}

export const getLatestBlock = (baseType: string): BlockConfig | undefined => {
const normalized = baseType.replace(/-/g, '_')

const versionedKeys = Object.keys(registry).filter((key) => {
const match = key.match(new RegExp(`^${normalized}_v(\\d+)$`))
return match !== null
})

if (versionedKeys.length > 0) {
const sorted = versionedKeys.sort((a, b) => {
const versionA = Number.parseInt(a.match(/_v(\d+)$/)?.[1] || '0', 10)
const versionB = Number.parseInt(b.match(/_v(\d+)$/)?.[1] || '0', 10)
return versionB - versionA
})
return registry[sorted[0]]
}

return registry[normalized]
}

export const getBlockByToolName = (toolName: string): BlockConfig | undefined => {
return Object.values(registry).find((block) => block.tools?.access?.includes(toolName))
}
Expand Down
15 changes: 2 additions & 13 deletions apps/sim/executor/utils/start-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,21 +378,10 @@ function buildManualTriggerOutput(
}

function buildIntegrationTriggerOutput(
finalInput: unknown,
_finalInput: unknown,
workflowInput: unknown
): NormalizedBlockOutput {
const base: NormalizedBlockOutput = isPlainObject(workflowInput)
? ({ ...(workflowInput as Record<string, unknown>) } as NormalizedBlockOutput)
: {}

if (isPlainObject(finalInput)) {
Object.assign(base, finalInput as Record<string, unknown>)
base.input = { ...(finalInput as Record<string, unknown>) }
} else {
base.input = finalInput
}

return mergeFilesIntoOutput(base, workflowInput)
return isPlainObject(workflowInput) ? (workflowInput as NormalizedBlockOutput) : {}
}

function extractSubBlocks(block: SerializedBlock): Record<string, unknown> | undefined {
Expand Down
25 changes: 8 additions & 17 deletions apps/sim/lib/logs/get-trigger-options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getBlock } from '@/blocks/registry'
import { getLatestBlock } from '@/blocks/registry'
import { getAllTriggers } from '@/triggers'

export interface TriggerOption {
Expand Down Expand Up @@ -48,22 +48,13 @@ export function getTriggerOptions(): TriggerOption[] {
continue
}

const block = getBlock(provider)

if (block) {
providerMap.set(provider, {
value: provider,
label: block.name, // Use block's display name (e.g., "Slack", "GitHub")
color: block.bgColor || '#6b7280', // Use block's hex color, fallback to gray
})
} else {
const label = formatProviderName(provider)
providerMap.set(provider, {
value: provider,
label,
color: '#6b7280', // gray fallback
})
}
const block = getLatestBlock(provider)

providerMap.set(provider, {
value: provider,
label: block?.name || formatProviderName(provider),
color: block?.bgColor || '#6b7280',
})
}

const integrationOptions = Array.from(providerMap.values()).sort((a, b) =>
Expand Down
Loading