mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-14 09:27:58 -05:00
Compare commits
6 Commits
fix/error-
...
fix/trigge
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df20d1035b | ||
|
|
d9d261de1d | ||
|
|
2cb4593d81 | ||
|
|
f04a7b5364 | ||
|
|
00fb84fbde | ||
|
|
ce38024b20 |
@@ -552,6 +552,53 @@ All fields automatically have:
|
|||||||
- `mode: 'trigger'` - Only shown in trigger mode
|
- `mode: 'trigger'` - Only shown in trigger mode
|
||||||
- `condition: { field: 'selectedTriggerId', value: triggerId }` - Only shown when this trigger is selected
|
- `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
|
||||||
|
|
||||||
Trigger outputs use the same schema as block outputs (NOT tool outputs).
|
Trigger outputs use the same schema as block outputs (NOT tool outputs).
|
||||||
@@ -649,6 +696,11 @@ export const {service}WebhookTrigger: TriggerConfig = {
|
|||||||
- [ ] Added `delete{Service}Webhook` function to `provider-subscriptions.ts`
|
- [ ] Added `delete{Service}Webhook` function to `provider-subscriptions.ts`
|
||||||
- [ ] Added provider to `cleanupExternalWebhook` function
|
- [ ] 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
|
### Testing
|
||||||
- [ ] Run `bun run type-check` to verify no TypeScript errors
|
- [ ] Run `bun run type-check` to verify no TypeScript errors
|
||||||
- [ ] Restart dev server to pick up new triggers
|
- [ ] Restart dev server to pick up new triggers
|
||||||
|
|||||||
@@ -313,6 +313,26 @@ export const getBlock = (type: string): BlockConfig | undefined => {
|
|||||||
return registry[normalized]
|
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 => {
|
export const getBlockByToolName = (toolName: string): BlockConfig | undefined => {
|
||||||
return Object.values(registry).find((block) => block.tools?.access?.includes(toolName))
|
return Object.values(registry).find((block) => block.tools?.access?.includes(toolName))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -378,21 +378,10 @@ function buildManualTriggerOutput(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildIntegrationTriggerOutput(
|
function buildIntegrationTriggerOutput(
|
||||||
finalInput: unknown,
|
_finalInput: unknown,
|
||||||
workflowInput: unknown
|
workflowInput: unknown
|
||||||
): NormalizedBlockOutput {
|
): NormalizedBlockOutput {
|
||||||
const base: NormalizedBlockOutput = isPlainObject(workflowInput)
|
return isPlainObject(workflowInput) ? (workflowInput as NormalizedBlockOutput) : {}
|
||||||
? ({ ...(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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractSubBlocks(block: SerializedBlock): Record<string, unknown> | undefined {
|
function extractSubBlocks(block: SerializedBlock): Record<string, unknown> | undefined {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getBlock } from '@/blocks/registry'
|
import { getLatestBlock } from '@/blocks/registry'
|
||||||
import { getAllTriggers } from '@/triggers'
|
import { getAllTriggers } from '@/triggers'
|
||||||
|
|
||||||
export interface TriggerOption {
|
export interface TriggerOption {
|
||||||
@@ -48,22 +48,13 @@ export function getTriggerOptions(): TriggerOption[] {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const block = getBlock(provider)
|
const block = getLatestBlock(provider)
|
||||||
|
|
||||||
if (block) {
|
providerMap.set(provider, {
|
||||||
providerMap.set(provider, {
|
value: provider,
|
||||||
value: provider,
|
label: block?.name || formatProviderName(provider),
|
||||||
label: block.name, // Use block's display name (e.g., "Slack", "GitHub")
|
color: block?.bgColor || '#6b7280',
|
||||||
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 integrationOptions = Array.from(providerMap.values()).sort((a, b) =>
|
const integrationOptions = Array.from(providerMap.values()).sort((a, b) =>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2290,7 +2290,7 @@ describe('hasWorkflowChanged', () => {
|
|||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'push' } },
|
model: { value: 'gpt-4' },
|
||||||
webhookId: { value: null },
|
webhookId: { value: null },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -2302,7 +2302,7 @@ describe('hasWorkflowChanged', () => {
|
|||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'push' } },
|
model: { value: 'gpt-4' },
|
||||||
webhookId: { value: 'wh_123456' },
|
webhookId: { value: 'wh_123456' },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -2318,7 +2318,7 @@ describe('hasWorkflowChanged', () => {
|
|||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'push' } },
|
model: { value: 'gpt-4' },
|
||||||
triggerPath: { value: '' },
|
triggerPath: { value: '' },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -2330,7 +2330,7 @@ describe('hasWorkflowChanged', () => {
|
|||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'push' } },
|
model: { value: 'gpt-4' },
|
||||||
triggerPath: { value: '/api/webhooks/abc123' },
|
triggerPath: { value: '/api/webhooks/abc123' },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -2346,7 +2346,7 @@ describe('hasWorkflowChanged', () => {
|
|||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'push' } },
|
model: { value: 'gpt-4' },
|
||||||
webhookId: { value: null },
|
webhookId: { value: null },
|
||||||
triggerPath: { value: '' },
|
triggerPath: { value: '' },
|
||||||
},
|
},
|
||||||
@@ -2359,7 +2359,7 @@ describe('hasWorkflowChanged', () => {
|
|||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'push' } },
|
model: { value: 'gpt-4' },
|
||||||
webhookId: { value: 'wh_123456' },
|
webhookId: { value: 'wh_123456' },
|
||||||
triggerPath: { value: '/api/webhooks/abc123' },
|
triggerPath: { value: '/api/webhooks/abc123' },
|
||||||
},
|
},
|
||||||
@@ -2371,14 +2371,18 @@ describe('hasWorkflowChanged', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it.concurrent(
|
it.concurrent(
|
||||||
'should detect change when triggerConfig differs but runtime metadata also differs',
|
'should detect change when actual config differs but runtime metadata also differs',
|
||||||
() => {
|
() => {
|
||||||
|
// Test that when a real config field changes along with runtime metadata,
|
||||||
|
// the change is still detected. Using 'model' as the config field since
|
||||||
|
// triggerConfig is now excluded from comparison (individual trigger fields
|
||||||
|
// are compared separately).
|
||||||
const deployedState = createWorkflowState({
|
const deployedState = createWorkflowState({
|
||||||
blocks: {
|
blocks: {
|
||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'push' } },
|
model: { value: 'gpt-4' },
|
||||||
webhookId: { value: null },
|
webhookId: { value: null },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -2390,7 +2394,7 @@ describe('hasWorkflowChanged', () => {
|
|||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'pull_request' } },
|
model: { value: 'gpt-4o' },
|
||||||
webhookId: { value: 'wh_123456' },
|
webhookId: { value: 'wh_123456' },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -2402,8 +2406,12 @@ describe('hasWorkflowChanged', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
it.concurrent(
|
it.concurrent(
|
||||||
'should not detect change when runtime metadata is added to current state',
|
'should not detect change when triggerConfig differs (individual fields compared separately)',
|
||||||
() => {
|
() => {
|
||||||
|
// triggerConfig is excluded from comparison because:
|
||||||
|
// 1. Individual trigger fields are stored as separate subblocks and compared individually
|
||||||
|
// 2. The client populates triggerConfig with default values from trigger definitions,
|
||||||
|
// which aren't present in the deployed state, causing false positive change detection
|
||||||
const deployedState = createWorkflowState({
|
const deployedState = createWorkflowState({
|
||||||
blocks: {
|
blocks: {
|
||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
@@ -2420,7 +2428,36 @@ describe('hasWorkflowChanged', () => {
|
|||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'push' } },
|
triggerConfig: { value: { event: 'pull_request', extraField: true } },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(hasWorkflowChanged(currentState, deployedState)).toBe(false)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
it.concurrent(
|
||||||
|
'should not detect change when runtime metadata is added to current state',
|
||||||
|
() => {
|
||||||
|
const deployedState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1', {
|
||||||
|
type: 'starter',
|
||||||
|
subBlocks: {
|
||||||
|
model: { value: 'gpt-4' },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentState = createWorkflowState({
|
||||||
|
blocks: {
|
||||||
|
block1: createBlock('block1', {
|
||||||
|
type: 'starter',
|
||||||
|
subBlocks: {
|
||||||
|
model: { value: 'gpt-4' },
|
||||||
webhookId: { value: 'wh_123456' },
|
webhookId: { value: 'wh_123456' },
|
||||||
triggerPath: { value: '/api/webhooks/abc123' },
|
triggerPath: { value: '/api/webhooks/abc123' },
|
||||||
},
|
},
|
||||||
@@ -2440,7 +2477,7 @@ describe('hasWorkflowChanged', () => {
|
|||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'push' } },
|
model: { value: 'gpt-4' },
|
||||||
webhookId: { value: 'wh_old123' },
|
webhookId: { value: 'wh_old123' },
|
||||||
triggerPath: { value: '/api/webhooks/old' },
|
triggerPath: { value: '/api/webhooks/old' },
|
||||||
},
|
},
|
||||||
@@ -2453,7 +2490,7 @@ describe('hasWorkflowChanged', () => {
|
|||||||
block1: createBlock('block1', {
|
block1: createBlock('block1', {
|
||||||
type: 'starter',
|
type: 'starter',
|
||||||
subBlocks: {
|
subBlocks: {
|
||||||
triggerConfig: { value: { event: 'push' } },
|
model: { value: 'gpt-4' },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -96,23 +96,3 @@ export function buildMeetingOutputs(): Record<string, TriggerOutput> {
|
|||||||
},
|
},
|
||||||
} as Record<string, TriggerOutput>
|
} as Record<string, TriggerOutput>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Build output schema for generic webhook events
|
|
||||||
*/
|
|
||||||
export function buildGenericOutputs(): Record<string, TriggerOutput> {
|
|
||||||
return {
|
|
||||||
payload: {
|
|
||||||
type: 'object',
|
|
||||||
description: 'Raw webhook payload',
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
type: 'object',
|
|
||||||
description: 'Request headers',
|
|
||||||
},
|
|
||||||
timestamp: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'ISO8601 received timestamp',
|
|
||||||
},
|
|
||||||
} as Record<string, TriggerOutput>
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { CirclebackIcon } from '@/components/icons'
|
import { CirclebackIcon } from '@/components/icons'
|
||||||
import type { TriggerConfig } from '@/triggers/types'
|
import type { TriggerConfig } from '@/triggers/types'
|
||||||
import { buildGenericOutputs, circlebackSetupInstructions, circlebackTriggerOptions } from './utils'
|
import { buildMeetingOutputs, circlebackSetupInstructions, circlebackTriggerOptions } from './utils'
|
||||||
|
|
||||||
export const circlebackWebhookTrigger: TriggerConfig = {
|
export const circlebackWebhookTrigger: TriggerConfig = {
|
||||||
id: 'circleback_webhook',
|
id: 'circleback_webhook',
|
||||||
@@ -74,7 +74,7 @@ export const circlebackWebhookTrigger: TriggerConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
outputs: buildGenericOutputs(),
|
outputs: buildMeetingOutputs(),
|
||||||
|
|
||||||
webhook: {
|
webhook: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -31,8 +31,14 @@ export const TRIGGER_PERSISTED_SUBBLOCK_IDS: string[] = [
|
|||||||
/**
|
/**
|
||||||
* Trigger-related subblock IDs that represent runtime metadata. They should remain
|
* Trigger-related subblock IDs that represent runtime metadata. They should remain
|
||||||
* in the workflow state but must not be modified or cleared by diff operations.
|
* in the workflow state but must not be modified or cleared by diff operations.
|
||||||
|
*
|
||||||
|
* Note: 'triggerConfig' is included because it's an aggregate of individual trigger
|
||||||
|
* field subblocks. Those individual fields are compared separately, so comparing
|
||||||
|
* triggerConfig would be redundant. Additionally, the client populates triggerConfig
|
||||||
|
* with default values from the trigger definition on load, which aren't present in
|
||||||
|
* the deployed state, causing false positive change detection.
|
||||||
*/
|
*/
|
||||||
export const TRIGGER_RUNTIME_SUBBLOCK_IDS: string[] = ['webhookId', 'triggerPath']
|
export const TRIGGER_RUNTIME_SUBBLOCK_IDS: string[] = ['webhookId', 'triggerPath', 'triggerConfig']
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of consecutive failures before a trigger (schedule/webhook) is auto-disabled.
|
* Maximum number of consecutive failures before a trigger (schedule/webhook) is auto-disabled.
|
||||||
|
|||||||
@@ -116,6 +116,11 @@ export const githubIssueClosedTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'GitHub event type from X-GitHub-Event header (e.g., issues, pull_request, push)',
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Action performed (opened, closed, reopened, edited, etc.)',
|
description: 'Action performed (opened, closed, reopened, edited, etc.)',
|
||||||
|
|||||||
@@ -117,6 +117,10 @@ export const githubIssueCommentTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'GitHub event type from X-GitHub-Event header (e.g., issue_comment)',
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Action performed (created, edited, deleted)',
|
description: 'Action performed (created, edited, deleted)',
|
||||||
|
|||||||
@@ -137,6 +137,11 @@ export const githubIssueOpenedTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'GitHub event type from X-GitHub-Event header (e.g., issues, pull_request, push)',
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Action performed (opened, closed, reopened, edited, etc.)',
|
description: 'Action performed (opened, closed, reopened, edited, etc.)',
|
||||||
|
|||||||
@@ -117,6 +117,10 @@ export const githubPRClosedTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'GitHub event type from X-GitHub-Event header (e.g., pull_request)',
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Action performed (opened, closed, synchronize, reopened, edited, etc.)',
|
description: 'Action performed (opened, closed, synchronize, reopened, edited, etc.)',
|
||||||
|
|||||||
@@ -117,6 +117,10 @@ export const githubPRCommentTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'GitHub event type from X-GitHub-Event header (e.g., issue_comment)',
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Action performed (created, edited, deleted)',
|
description: 'Action performed (created, edited, deleted)',
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ export const githubPRMergedTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'GitHub event type from X-GitHub-Event header (e.g., pull_request)',
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Action performed (opened, closed, synchronize, reopened, edited, etc.)',
|
description: 'Action performed (opened, closed, synchronize, reopened, edited, etc.)',
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ export const githubPROpenedTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'GitHub event type from X-GitHub-Event header (e.g., pull_request)',
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Action performed (opened, closed, synchronize, reopened, edited, etc.)',
|
description: 'Action performed (opened, closed, synchronize, reopened, edited, etc.)',
|
||||||
|
|||||||
@@ -117,6 +117,10 @@ export const githubPRReviewedTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'GitHub event type from X-GitHub-Event header (e.g., pull_request_review)',
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Action performed (submitted, edited, dismissed)',
|
description: 'Action performed (submitted, edited, dismissed)',
|
||||||
|
|||||||
@@ -116,6 +116,14 @@ export const githubPushTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'GitHub event type from X-GitHub-Event header (e.g., push)',
|
||||||
|
},
|
||||||
|
branch: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Branch name derived from ref (e.g., main from refs/heads/main)',
|
||||||
|
},
|
||||||
ref: {
|
ref: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Git reference that was pushed (e.g., refs/heads/main)',
|
description: 'Git reference that was pushed (e.g., refs/heads/main)',
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ export const githubReleasePublishedTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'GitHub event type from X-GitHub-Event header (e.g., release)',
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -117,6 +117,10 @@ export const githubWorkflowRunTrigger: TriggerConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
outputs: {
|
outputs: {
|
||||||
|
event_type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'GitHub event type from X-GitHub-Event header (e.g., workflow_run)',
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Action performed (requested, in_progress, completed)',
|
description: 'Action performed (requested, in_progress, completed)',
|
||||||
|
|||||||
@@ -265,11 +265,6 @@ function buildBaseWebhookOutputs(): Record<string, TriggerOutput> {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
webhook: {
|
|
||||||
type: 'json',
|
|
||||||
description: 'Webhook metadata including provider, path, and raw payload',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { LemlistIcon } from '@/components/icons'
|
import { LemlistIcon } from '@/components/icons'
|
||||||
import { buildTriggerSubBlocks } from '@/triggers'
|
import { buildTriggerSubBlocks } from '@/triggers'
|
||||||
import {
|
import {
|
||||||
buildActivityOutputs,
|
buildEmailBouncedOutputs,
|
||||||
buildLemlistExtraFields,
|
buildLemlistExtraFields,
|
||||||
lemlistSetupInstructions,
|
lemlistSetupInstructions,
|
||||||
lemlistTriggerOptions,
|
lemlistTriggerOptions,
|
||||||
@@ -27,7 +27,7 @@ export const lemlistEmailBouncedTrigger: TriggerConfig = {
|
|||||||
extraFields: buildLemlistExtraFields('lemlist_email_bounced'),
|
extraFields: buildLemlistExtraFields('lemlist_email_bounced'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
outputs: buildActivityOutputs(),
|
outputs: buildEmailBouncedOutputs(),
|
||||||
|
|
||||||
webhook: {
|
webhook: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { LemlistIcon } from '@/components/icons'
|
import { LemlistIcon } from '@/components/icons'
|
||||||
import { buildTriggerSubBlocks } from '@/triggers'
|
import { buildTriggerSubBlocks } from '@/triggers'
|
||||||
import {
|
import {
|
||||||
buildActivityOutputs,
|
buildEmailClickedOutputs,
|
||||||
buildLemlistExtraFields,
|
buildLemlistExtraFields,
|
||||||
lemlistSetupInstructions,
|
lemlistSetupInstructions,
|
||||||
lemlistTriggerOptions,
|
lemlistTriggerOptions,
|
||||||
@@ -27,7 +27,7 @@ export const lemlistEmailClickedTrigger: TriggerConfig = {
|
|||||||
extraFields: buildLemlistExtraFields('lemlist_email_clicked'),
|
extraFields: buildLemlistExtraFields('lemlist_email_clicked'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
outputs: buildActivityOutputs(),
|
outputs: buildEmailClickedOutputs(),
|
||||||
|
|
||||||
webhook: {
|
webhook: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { LemlistIcon } from '@/components/icons'
|
import { LemlistIcon } from '@/components/icons'
|
||||||
import { buildTriggerSubBlocks } from '@/triggers'
|
import { buildTriggerSubBlocks } from '@/triggers'
|
||||||
import {
|
import {
|
||||||
buildActivityOutputs,
|
buildEmailOpenedOutputs,
|
||||||
buildLemlistExtraFields,
|
buildLemlistExtraFields,
|
||||||
lemlistSetupInstructions,
|
lemlistSetupInstructions,
|
||||||
lemlistTriggerOptions,
|
lemlistTriggerOptions,
|
||||||
@@ -27,7 +27,7 @@ export const lemlistEmailOpenedTrigger: TriggerConfig = {
|
|||||||
extraFields: buildLemlistExtraFields('lemlist_email_opened'),
|
extraFields: buildLemlistExtraFields('lemlist_email_opened'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
outputs: buildActivityOutputs(),
|
outputs: buildEmailOpenedOutputs(),
|
||||||
|
|
||||||
webhook: {
|
webhook: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { LemlistIcon } from '@/components/icons'
|
import { LemlistIcon } from '@/components/icons'
|
||||||
import { buildTriggerSubBlocks } from '@/triggers'
|
import { buildTriggerSubBlocks } from '@/triggers'
|
||||||
import {
|
import {
|
||||||
buildEmailReplyOutputs,
|
buildEmailRepliedOutputs,
|
||||||
buildLemlistExtraFields,
|
buildLemlistExtraFields,
|
||||||
lemlistSetupInstructions,
|
lemlistSetupInstructions,
|
||||||
lemlistTriggerOptions,
|
lemlistTriggerOptions,
|
||||||
@@ -30,7 +30,7 @@ export const lemlistEmailRepliedTrigger: TriggerConfig = {
|
|||||||
extraFields: buildLemlistExtraFields('lemlist_email_replied'),
|
extraFields: buildLemlistExtraFields('lemlist_email_replied'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
outputs: buildEmailReplyOutputs(),
|
outputs: buildEmailRepliedOutputs(),
|
||||||
|
|
||||||
webhook: {
|
webhook: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { LemlistIcon } from '@/components/icons'
|
import { LemlistIcon } from '@/components/icons'
|
||||||
import { buildTriggerSubBlocks } from '@/triggers'
|
import { buildTriggerSubBlocks } from '@/triggers'
|
||||||
import {
|
import {
|
||||||
buildActivityOutputs,
|
buildEmailSentOutputs,
|
||||||
buildLemlistExtraFields,
|
buildLemlistExtraFields,
|
||||||
lemlistSetupInstructions,
|
lemlistSetupInstructions,
|
||||||
lemlistTriggerOptions,
|
lemlistTriggerOptions,
|
||||||
@@ -27,7 +27,7 @@ export const lemlistEmailSentTrigger: TriggerConfig = {
|
|||||||
extraFields: buildLemlistExtraFields('lemlist_email_sent'),
|
extraFields: buildLemlistExtraFields('lemlist_email_sent'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
outputs: buildActivityOutputs(),
|
outputs: buildEmailSentOutputs(),
|
||||||
|
|
||||||
webhook: {
|
webhook: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { LemlistIcon } from '@/components/icons'
|
import { LemlistIcon } from '@/components/icons'
|
||||||
import { buildTriggerSubBlocks } from '@/triggers'
|
import { buildTriggerSubBlocks } from '@/triggers'
|
||||||
import {
|
import {
|
||||||
buildActivityOutputs,
|
buildInterestOutputs,
|
||||||
buildLemlistExtraFields,
|
buildLemlistExtraFields,
|
||||||
lemlistSetupInstructions,
|
lemlistSetupInstructions,
|
||||||
lemlistTriggerOptions,
|
lemlistTriggerOptions,
|
||||||
@@ -27,7 +27,7 @@ export const lemlistInterestedTrigger: TriggerConfig = {
|
|||||||
extraFields: buildLemlistExtraFields('lemlist_interested'),
|
extraFields: buildLemlistExtraFields('lemlist_interested'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
outputs: buildActivityOutputs(),
|
outputs: buildInterestOutputs(),
|
||||||
|
|
||||||
webhook: {
|
webhook: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { LemlistIcon } from '@/components/icons'
|
|||||||
import { buildTriggerSubBlocks } from '@/triggers'
|
import { buildTriggerSubBlocks } from '@/triggers'
|
||||||
import {
|
import {
|
||||||
buildLemlistExtraFields,
|
buildLemlistExtraFields,
|
||||||
buildLinkedInReplyOutputs,
|
buildLinkedInRepliedOutputs,
|
||||||
lemlistSetupInstructions,
|
lemlistSetupInstructions,
|
||||||
lemlistTriggerOptions,
|
lemlistTriggerOptions,
|
||||||
} from '@/triggers/lemlist/utils'
|
} from '@/triggers/lemlist/utils'
|
||||||
@@ -27,7 +27,7 @@ export const lemlistLinkedInRepliedTrigger: TriggerConfig = {
|
|||||||
extraFields: buildLemlistExtraFields('lemlist_linkedin_replied'),
|
extraFields: buildLemlistExtraFields('lemlist_linkedin_replied'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
outputs: buildLinkedInReplyOutputs(),
|
outputs: buildLinkedInRepliedOutputs(),
|
||||||
|
|
||||||
webhook: {
|
webhook: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { LemlistIcon } from '@/components/icons'
|
import { LemlistIcon } from '@/components/icons'
|
||||||
import { buildTriggerSubBlocks } from '@/triggers'
|
import { buildTriggerSubBlocks } from '@/triggers'
|
||||||
import {
|
import {
|
||||||
buildActivityOutputs,
|
buildInterestOutputs,
|
||||||
buildLemlistExtraFields,
|
buildLemlistExtraFields,
|
||||||
lemlistSetupInstructions,
|
lemlistSetupInstructions,
|
||||||
lemlistTriggerOptions,
|
lemlistTriggerOptions,
|
||||||
@@ -27,7 +27,7 @@ export const lemlistNotInterestedTrigger: TriggerConfig = {
|
|||||||
extraFields: buildLemlistExtraFields('lemlist_not_interested'),
|
extraFields: buildLemlistExtraFields('lemlist_not_interested'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
outputs: buildActivityOutputs(),
|
outputs: buildInterestOutputs(),
|
||||||
|
|
||||||
webhook: {
|
webhook: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -66,203 +66,254 @@ export function buildLemlistExtraFields(triggerId: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base activity outputs shared across all Lemlist triggers
|
* Core fields present in ALL Lemlist webhook payloads
|
||||||
|
* See: https://help.lemlist.com/en/articles/9423940-use-the-api-to-list-activity-types
|
||||||
*/
|
*/
|
||||||
function buildBaseActivityOutputs(): Record<string, TriggerOutput> {
|
const coreOutputs = {
|
||||||
|
_id: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Unique activity identifier',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Activity type (e.g., emailsSent, emailsReplied)',
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Activity creation timestamp (ISO 8601)',
|
||||||
|
},
|
||||||
|
teamId: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Lemlist team identifier',
|
||||||
|
},
|
||||||
|
leadId: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Lead identifier',
|
||||||
|
},
|
||||||
|
campaignId: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Campaign identifier',
|
||||||
|
},
|
||||||
|
campaignName: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Campaign name',
|
||||||
|
},
|
||||||
|
} as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lead fields present in webhook payloads
|
||||||
|
*/
|
||||||
|
const leadOutputs = {
|
||||||
|
email: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Lead email address',
|
||||||
|
},
|
||||||
|
firstName: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Lead first name',
|
||||||
|
},
|
||||||
|
lastName: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Lead last name',
|
||||||
|
},
|
||||||
|
companyName: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Lead company name',
|
||||||
|
},
|
||||||
|
linkedinUrl: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Lead LinkedIn profile URL',
|
||||||
|
},
|
||||||
|
} as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sequence/campaign tracking fields for email activities
|
||||||
|
*/
|
||||||
|
const sequenceOutputs = {
|
||||||
|
sequenceId: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Sequence identifier',
|
||||||
|
},
|
||||||
|
sequenceStep: {
|
||||||
|
type: 'number',
|
||||||
|
description: 'Current step in the sequence (0-indexed)',
|
||||||
|
},
|
||||||
|
totalSequenceStep: {
|
||||||
|
type: 'number',
|
||||||
|
description: 'Total number of steps in the sequence',
|
||||||
|
},
|
||||||
|
isFirst: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether this is the first activity of this type for this step',
|
||||||
|
},
|
||||||
|
} as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sender information fields
|
||||||
|
*/
|
||||||
|
const senderOutputs = {
|
||||||
|
sendUserId: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Sender user identifier',
|
||||||
|
},
|
||||||
|
sendUserEmail: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Sender email address',
|
||||||
|
},
|
||||||
|
sendUserName: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Sender display name',
|
||||||
|
},
|
||||||
|
} as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email content fields
|
||||||
|
*/
|
||||||
|
const emailContentOutputs = {
|
||||||
|
subject: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Email subject line',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Email body content (HTML)',
|
||||||
|
},
|
||||||
|
messageId: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Email message ID (RFC 2822 format)',
|
||||||
|
},
|
||||||
|
emailId: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Lemlist email identifier',
|
||||||
|
},
|
||||||
|
} as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build outputs for email sent events
|
||||||
|
*/
|
||||||
|
export function buildEmailSentOutputs(): Record<string, TriggerOutput> {
|
||||||
return {
|
return {
|
||||||
type: {
|
...coreOutputs,
|
||||||
type: 'string',
|
...leadOutputs,
|
||||||
description: 'Activity type (emailsReplied, linkedinReplied, interested, emailsOpened, etc.)',
|
...sequenceOutputs,
|
||||||
},
|
...senderOutputs,
|
||||||
_id: {
|
...emailContentOutputs,
|
||||||
type: 'string',
|
} as Record<string, TriggerOutput>
|
||||||
description: 'Unique activity identifier',
|
|
||||||
},
|
|
||||||
leadId: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Associated lead ID',
|
|
||||||
},
|
|
||||||
campaignId: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Campaign ID',
|
|
||||||
},
|
|
||||||
campaignName: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Campaign name',
|
|
||||||
},
|
|
||||||
sequenceId: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Sequence ID within the campaign',
|
|
||||||
},
|
|
||||||
stepId: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Step ID that triggered this activity',
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'When the activity occurred (ISO 8601)',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lead outputs - information about the lead
|
* Build outputs for email replied events
|
||||||
*/
|
*/
|
||||||
function buildLeadOutputs(): Record<string, TriggerOutput> {
|
export function buildEmailRepliedOutputs(): Record<string, TriggerOutput> {
|
||||||
return {
|
return {
|
||||||
lead: {
|
...coreOutputs,
|
||||||
_id: {
|
...leadOutputs,
|
||||||
type: 'string',
|
...sequenceOutputs,
|
||||||
description: 'Lead unique identifier',
|
...senderOutputs,
|
||||||
},
|
...emailContentOutputs,
|
||||||
email: {
|
} as Record<string, TriggerOutput>
|
||||||
type: 'string',
|
|
||||||
description: 'Lead email address',
|
|
||||||
},
|
|
||||||
firstName: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Lead first name',
|
|
||||||
},
|
|
||||||
lastName: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Lead last name',
|
|
||||||
},
|
|
||||||
companyName: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Lead company name',
|
|
||||||
},
|
|
||||||
phone: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Lead phone number',
|
|
||||||
},
|
|
||||||
linkedinUrl: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Lead LinkedIn profile URL',
|
|
||||||
},
|
|
||||||
picture: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Lead profile picture URL',
|
|
||||||
},
|
|
||||||
icebreaker: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Personalized icebreaker text',
|
|
||||||
},
|
|
||||||
timezone: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Lead timezone (e.g., America/New_York)',
|
|
||||||
},
|
|
||||||
isUnsubscribed: {
|
|
||||||
type: 'boolean',
|
|
||||||
description: 'Whether the lead is unsubscribed',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard activity outputs (activity + lead data)
|
* Build outputs for email opened events
|
||||||
*/
|
*/
|
||||||
export function buildActivityOutputs(): Record<string, TriggerOutput> {
|
export function buildEmailOpenedOutputs(): Record<string, TriggerOutput> {
|
||||||
return {
|
return {
|
||||||
...buildBaseActivityOutputs(),
|
...coreOutputs,
|
||||||
...buildLeadOutputs(),
|
...leadOutputs,
|
||||||
webhook: {
|
...sequenceOutputs,
|
||||||
type: 'json',
|
...senderOutputs,
|
||||||
description: 'Full webhook payload with all activity-specific data',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Email-specific outputs (includes message content for replies)
|
|
||||||
*/
|
|
||||||
export function buildEmailReplyOutputs(): Record<string, TriggerOutput> {
|
|
||||||
return {
|
|
||||||
...buildBaseActivityOutputs(),
|
|
||||||
...buildLeadOutputs(),
|
|
||||||
messageId: {
|
messageId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Email message ID',
|
description: 'Email message ID that was opened',
|
||||||
},
|
},
|
||||||
subject: {
|
} as Record<string, TriggerOutput>
|
||||||
type: 'string',
|
|
||||||
description: 'Email subject line',
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Email reply text content',
|
|
||||||
},
|
|
||||||
html: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Email reply HTML content',
|
|
||||||
},
|
|
||||||
sentAt: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'When the reply was sent',
|
|
||||||
},
|
|
||||||
webhook: {
|
|
||||||
type: 'json',
|
|
||||||
description: 'Full webhook payload with all email data',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LinkedIn-specific outputs (includes message content)
|
* Build outputs for email clicked events
|
||||||
*/
|
*/
|
||||||
export function buildLinkedInReplyOutputs(): Record<string, TriggerOutput> {
|
export function buildEmailClickedOutputs(): Record<string, TriggerOutput> {
|
||||||
return {
|
return {
|
||||||
...buildBaseActivityOutputs(),
|
...coreOutputs,
|
||||||
...buildLeadOutputs(),
|
...leadOutputs,
|
||||||
|
...sequenceOutputs,
|
||||||
|
...senderOutputs,
|
||||||
messageId: {
|
messageId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'LinkedIn message ID',
|
description: 'Email message ID containing the clicked link',
|
||||||
},
|
},
|
||||||
text: {
|
clickedUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'LinkedIn message text content',
|
description: 'URL that was clicked',
|
||||||
},
|
},
|
||||||
sentAt: {
|
} as Record<string, TriggerOutput>
|
||||||
type: 'string',
|
|
||||||
description: 'When the message was sent',
|
|
||||||
},
|
|
||||||
webhook: {
|
|
||||||
type: 'json',
|
|
||||||
description: 'Full webhook payload with all LinkedIn data',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All outputs for generic webhook (activity + lead + all possible fields)
|
* Build outputs for email bounced events
|
||||||
*/
|
*/
|
||||||
export function buildAllOutputs(): Record<string, TriggerOutput> {
|
export function buildEmailBouncedOutputs(): Record<string, TriggerOutput> {
|
||||||
return {
|
return {
|
||||||
...buildBaseActivityOutputs(),
|
...coreOutputs,
|
||||||
...buildLeadOutputs(),
|
...leadOutputs,
|
||||||
|
...sequenceOutputs,
|
||||||
|
...senderOutputs,
|
||||||
messageId: {
|
messageId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Message ID (for email/LinkedIn events)',
|
description: 'Email message ID that bounced',
|
||||||
},
|
},
|
||||||
subject: {
|
errorMessage: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Email subject (for email events)',
|
description: 'Bounce error message',
|
||||||
},
|
},
|
||||||
|
} as Record<string, TriggerOutput>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build outputs for LinkedIn replied events
|
||||||
|
*/
|
||||||
|
export function buildLinkedInRepliedOutputs(): Record<string, TriggerOutput> {
|
||||||
|
return {
|
||||||
|
...coreOutputs,
|
||||||
|
...leadOutputs,
|
||||||
|
...sequenceOutputs,
|
||||||
text: {
|
text: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Message text content',
|
description: 'LinkedIn message content',
|
||||||
},
|
},
|
||||||
html: {
|
} as Record<string, TriggerOutput>
|
||||||
type: 'string',
|
}
|
||||||
description: 'Message HTML content (for email events)',
|
|
||||||
},
|
/**
|
||||||
sentAt: {
|
* Build outputs for interested/not interested events
|
||||||
type: 'string',
|
*/
|
||||||
description: 'When the message was sent',
|
export function buildInterestOutputs(): Record<string, TriggerOutput> {
|
||||||
},
|
return {
|
||||||
webhook: {
|
...coreOutputs,
|
||||||
type: 'json',
|
...leadOutputs,
|
||||||
description: 'Full webhook payload with all data',
|
...sequenceOutputs,
|
||||||
},
|
} as Record<string, TriggerOutput>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build outputs for generic webhook (all events)
|
||||||
|
* Includes all possible fields across event types
|
||||||
|
*/
|
||||||
|
export function buildLemlistOutputs(): Record<string, TriggerOutput> {
|
||||||
|
return {
|
||||||
|
...coreOutputs,
|
||||||
|
...leadOutputs,
|
||||||
|
...sequenceOutputs,
|
||||||
|
...senderOutputs,
|
||||||
|
...emailContentOutputs,
|
||||||
|
clickedUrl: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'URL that was clicked (for emailsClicked events)',
|
||||||
|
},
|
||||||
|
errorMessage: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Error message (for bounce/failed events)',
|
||||||
|
},
|
||||||
|
} as Record<string, TriggerOutput>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { LemlistIcon } from '@/components/icons'
|
import { LemlistIcon } from '@/components/icons'
|
||||||
import { buildTriggerSubBlocks } from '@/triggers'
|
import { buildTriggerSubBlocks } from '@/triggers'
|
||||||
import {
|
import {
|
||||||
buildAllOutputs,
|
|
||||||
buildLemlistExtraFields,
|
buildLemlistExtraFields,
|
||||||
|
buildLemlistOutputs,
|
||||||
lemlistSetupInstructions,
|
lemlistSetupInstructions,
|
||||||
lemlistTriggerOptions,
|
lemlistTriggerOptions,
|
||||||
} from '@/triggers/lemlist/utils'
|
} from '@/triggers/lemlist/utils'
|
||||||
@@ -27,7 +27,7 @@ export const lemlistWebhookTrigger: TriggerConfig = {
|
|||||||
extraFields: buildLemlistExtraFields('lemlist_webhook'),
|
extraFields: buildLemlistExtraFields('lemlist_webhook'),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
outputs: buildAllOutputs(),
|
outputs: buildLemlistOutputs(),
|
||||||
|
|
||||||
webhook: {
|
webhook: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ export const telegramWebhookTrigger: TriggerConfig = {
|
|||||||
},
|
},
|
||||||
sender: {
|
sender: {
|
||||||
id: { type: 'number', description: 'Sender user ID' },
|
id: { type: 'number', description: 'Sender user ID' },
|
||||||
|
username: { type: 'string', description: 'Sender username (if available)' },
|
||||||
firstName: { type: 'string', description: 'Sender first name' },
|
firstName: { type: 'string', description: 'Sender first name' },
|
||||||
lastName: { type: 'string', description: 'Sender last name' },
|
lastName: { type: 'string', description: 'Sender last name' },
|
||||||
languageCode: { type: 'string', description: 'Sender language code (if available)' },
|
languageCode: { type: 'string', description: 'Sender language code (if available)' },
|
||||||
|
|||||||
@@ -136,6 +136,8 @@ export const typeformWebhookTrigger: TriggerConfig = {
|
|||||||
'Array of respondent answers (only includes answered questions). Each answer contains type, value, and field reference.',
|
'Array of respondent answers (only includes answered questions). Each answer contains type, value, and field reference.',
|
||||||
},
|
},
|
||||||
definition: {
|
definition: {
|
||||||
|
description:
|
||||||
|
'Form definition (only included when "Include Form Definition" is enabled in trigger settings)',
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Form ID',
|
description: 'Form ID',
|
||||||
|
|||||||
@@ -96,10 +96,6 @@ export const webflowCollectionItemChangedTrigger: TriggerConfig = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The site ID where the event occurred',
|
description: 'The site ID where the event occurred',
|
||||||
},
|
},
|
||||||
workspaceId: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'The workspace ID where the event occurred',
|
|
||||||
},
|
|
||||||
collectionId: {
|
collectionId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The collection ID where the item was changed',
|
description: 'The collection ID where the item was changed',
|
||||||
|
|||||||
@@ -109,10 +109,6 @@ export const webflowCollectionItemCreatedTrigger: TriggerConfig = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The site ID where the event occurred',
|
description: 'The site ID where the event occurred',
|
||||||
},
|
},
|
||||||
workspaceId: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'The workspace ID where the event occurred',
|
|
||||||
},
|
|
||||||
collectionId: {
|
collectionId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The collection ID where the item was created',
|
description: 'The collection ID where the item was created',
|
||||||
|
|||||||
@@ -97,10 +97,6 @@ export const webflowCollectionItemDeletedTrigger: TriggerConfig = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The site ID where the event occurred',
|
description: 'The site ID where the event occurred',
|
||||||
},
|
},
|
||||||
workspaceId: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'The workspace ID where the event occurred',
|
|
||||||
},
|
|
||||||
collectionId: {
|
collectionId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The collection ID where the item was deleted',
|
description: 'The collection ID where the item was deleted',
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ export const webflowFormSubmissionTrigger: TriggerConfig = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The site ID where the form was submitted',
|
description: 'The site ID where the form was submitted',
|
||||||
},
|
},
|
||||||
workspaceId: {
|
formId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The workspace ID where the event occurred',
|
description: 'The form ID',
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
|||||||
Reference in New Issue
Block a user